JavaFX deleting from TableView - java

I'm working on a JavaFX app and am using a TableView as part of the GUI as follows:
public TableView<Meal> meals;
And to it I assigned a button that is supposed to delete elements which are selected. To do that I used a function that kept giving me a NoSuchElementException
public void deleteMealButtonClicked()
{
ObservableList<Meal> mealsSelected = meals.getSelectionModel().getSelectedItems();
ObservableList<Meal> allMeals = meals.getItems();
mealsSelected.forEach(allMeals::remove);
}
I debugged my way into finding out the problem was in the last line of the method.
After researching for quite a while, I happened to stumble across a piece of code that solved the problem. Here is the method with the code that works:
public void deleteMealButtonClicked()
{
ObservableList<Meal> mealsSelected = meals.getSelectionModel().getSelectedItems();
ObservableList<Meal> allMeals = meals.getItems();
if (mealsSelected != null) {
ArrayList<Meal> rows = new ArrayList<>(mealsSelected);
rows.forEach(row -> meals.getItems().remove(row));
}
}
My question is, why does the second method work and not the first? Aren't they fundamentally the same thing, other than adding the selected rows to an ArrayList on the second method?
Thank you in advance.

The selection model of the TableView must observe the ObservableList in the items property in order to properly reflect the state of the table. In other words, if an element is removed from items it must also be removed from the selection model's selected items.
This results in you indirectly modifying the selectedMeals list while iterating it to remove the contained elements from the meals list. In a lot of the java.util collections the iterators are fail-fast and this would result in a ConcurrentModificationException; I guess the selection model is not fail-fast and so you simply see indeterminate behavior (in the form of a NullPointerException).
The second option works because you first make a copy of selectedMeals and then iterate the copy.
You ask in a comment for a way to not create a copy; unfortunately, I can't think of one. You could simplify your code a bit by using the removeAll, however.
ObservableList<Meal> selected = ...;
// List.copyOf is a Java 10 method (and doesn't accept null elements)
table.getItems().removeAll(List.copyOf(selected));
// Pre Java 10 you can use toArray since ObservableList overloads removeAll
// with a version that takes a varargs parameter
table.getItems().removeAll(selected.toArray(new Meal[0]));
// or use the copy constructor of many collection classes
table.getItems().removeAll(new ArrayList<>(selected));
// In Java 11, toArray can be replaced with
table.getItems().removeAll(selected.toArray(Meal[]::new));

Related

Does HashSet Provide Any Added Value to Performance in This Instance?

So, I'm working with an existing method in Java that returns a List (ArrayList). However, I want to add some functionality to it so that if specified, it will exclude a certain Object. Now I understand that in general using contains() on a HashSet yields better performance vs an ArrayList, but I'm wondering if there is a justifiable performance boost to be had in the two variations of the code I have below:
Notes: listOfAccounts is an ArrayList returned from a DAO call. personalAccount is an Object of type Account.
if (excludePersonalAccount) {
Set<Account> accounts = new HashSet<Account>(listOfAccounts);
if (accounts.contains(personalAccount) {
listOfAccounts.remove(personalAccount);
}
}
VS
if (excludePersonalAccount) {
listOfAccounts.remove(personalAccount)
}
Set<Account> accounts = new HashSet<Account>(listOfAccounts);
The above line takes all of the elements of the ArrayList and adds it to the HashSet. Instead of doing all of that, you could iterate over the List and look to see if your element is contained inside it. If it is, then you can remove it (which is essentially what you second snippet is doing).
For that reason, the second snippet is preferred, as they both run in linear time.

Java: Adding Future<obj> to a list

Now that I have a handle on retrieving objects in parallel, how can I add those objects to a list?
I have a list of Future<Site> objects that I'm attempting to add to an ArrayList of Site objects. Here's my for loop. I can add a print statement in the loop and it reveals that the list of Future<Site> objects is indeed populated, however adding to the existing list (last line) does not work.
List<Future<Site>> futures=threadmaker.invokeAll(active_sites.stream().map(site -> new TAG_SCANNER(site, loggr)).collect(Collectors.toList()));
//Now fetch all the results
for (Future<Site> result : futures) {
//SOUND THE ALARMS (adding to existing list)
alarm_sites.add(result.get());
}
EDIT:
I thought Future's get method was blocking, in that the code would not progress until it returns a result. Does my mistake lie in trying to add to an already existing list?
For some reason, creating the list after my invokeall command solved my issues. I'm not sure why the pre-existing list didn't work but I'm marking the issue as solved for now, and I'll look into this later to see what may have happened.

Iterator Java to store object

I need to store objects using iterator but I found just the last element, what do you think ?
if (links.size()>0) {
for (Iterator<Link> iterator = links.iterator();
iterator.hasNext();) {
Link link = (Link) iterator.next();
item.setLink(link);
objects.add(item);
}
}
In objects, I pass to item and I found the last link, so what should I do? This idea will work item.setLink( list of links); or not?
It's difficult to tell what you're trying to achieve as the code has several problems:
you're setting the link in the class object in each iteration which is why only the last element remains (this is assuming the .setLink() method doesn't do anything funky inside)
if your objects collection (is it a collection?) is a Set and the item hashCode method computes its value based on the link value only your elements will be replaced
It's a guess only but perhaps this is what you're trying to do?
for (Link link : links) { // works only if your collection is properly typed
MyClass item = new MyClass();
item.setLink(link);
objects.add(item);
}
A couple of other suggestions:
use an enhanced for loop (as shown above)
the if (links.size()>0) { line is redundant -- your loop will simply do nothing if the collection is empty

how to remove element from List in java

This is reg. a requirement where I need to remove an element from List in java. I am getting unsupported exception when I try to remove element from List. Below is the code:
String[] str_array = {"abc","def","ght"};
List<String> results = Arrays.asList(str_array);
String tobeRemovedItem="";
for(int i=0;i<results.size();i++){
if(results.get(i).equalsIgnoreCase(searchString)) {
tobeRemovedItem=results.get(i);
}
}
if(!TextUtils.isEmpty(tobeRemovedItem)) {
results.remove(tobeRemovedItem); // I am getting exception here.
}
Can anyone help me in solving this issue?
The type of list returned by Arrays.asList does not support the remove operation. Hence the exception.
You can use the java.util.ArrayList instead.
List<String> results = new ArrayList<String>(Arrays.asList(str_array));
Answered already, but now without indirect datastructure of .asList()
List<String> results = new ArrayList<>();
Collections.addAll(results, str_array);
The .asList is backed by the array, hence you can modify the original array be modifying the list. And vice versa you cannot grow or shrink the list, as then the backed array object would need to be exchanged, as arrays are fixed in java.
The size of List returned by Arrays.asList cannot be changed. Instead you can do:
List<String> results = new ArrayList<>(Arrays.asList(str_array));
In general, UnsupportedOperationException is thrown by the implementation of an interface (or a child class of a class with an abstract method), where the implementor did not want to support that particular method.
That said, to debug these issues in the future, check which implementation you're using - in this case, it's given via the Arrays.asList() method from the Android sdk. Here you can see it says that it does not support adding or removing of items to the list.
If you must add and remove items, you can wrap the call into the ArrayList implementation of List which does support such modification (as suggested by Banthar and khelwood). The constructor takes a list as input, and copies the elements inside.

What is the ObservableSet equivalent for setAll() method from ObservableList

What method should I use in the setter of a JavaFX ObservableSet collection to clear the set and initialise it to the given collection? ObservableList has the method setAll(Collection) that is used to initialise the list by first clearing it.
The closest I have seen is addAll(Collection) which does not clear the set beforehand. When setting the collection in my project I want it to have the normal behaviour of setting the ObservableSet to a new set, but according to the javadoc:
Adds all of the elements in the specified collection to this set if they're not already present (optional operation). If the specified collection is also a set, the addAll operation effectively modifies this set so that its value is the union of the two sets.
I cannot just set the value using = because the passed parameter in the setter is a set and the ObservableSet is an internal wrapper that the outside knows nothing about. I also would like to avoid doing clear then addAll.
As you can see in the Javadoc of ObservableSet, there is no such method.
In fact, the method ObservableList::setAll is just a convenient "shortcut":
Clears the ObservableList and add all elements from the collection.
The common implementation ModifiableObservableListBase in JavaFX does a clear then an addAll:
#Override
public boolean setAll(Collection<? extends E> col) {
beginChange();
try {
clear();
addAll(col);
} finally {
endChange();
}
return true;
}
The main advantage of having a setAll shortcut is that only one "big" change event (ListChangeListener.Change) is sent to the listeners. Better performance.
In fact, you might want to extend com.sun.javafx.collections.ObservableSetWrapper with your own setAll but there will be no performance benefit since the event SetChangeListener.Change is an elementary change: m removed events, and n added events will be sent.
So you have no other choice than:
set.clear();
set.addAll(otherSet);
or copy in a new set and assign:
set = FXCollections.observableSet(otherSet);

Categories

Resources