Issue with removing multiple rows at once from JavaFX TableView - java

I am trying to make a button that, when multiple rows in a TableView are selected, all of the selected rows are removed.
I am creating an observable list using getSelectedIndicies and it isn't working right.
If I select the first three rows, I have it print out the indicies as it removes them and it prints 0,1 and then it removes the first and third row, but the middle of the three rows is not removed.
delBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
ObservableList<Integer> index =
table.getSelectionModel().getSelectedIndices();
for (int location: index) {
System.out.println(location);
data.remove(location);
}
table.getSelectionModel().clearSelection();
}
});

For some reason, this works:
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
List items = new ArrayList (treeTable.getSelectionModel().getSelectedItems());
data.removeAll(items);
table.getSelectionModel().clearSelection();
}
});
I doubt that the internal implementation of the selectedItems list ( com.sun.javafx.collections.ObservableListWrapper ) might have some bug.
Edit
Yes it's definitely a bug: https://javafx-jira.kenai.com/browse/RT-24367

Removing using index can't work since at each suppression the remaining indexes change.
You could remove the selectedItems :
delBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
data.removeAll(table.getSelectionModel().getSelectedItems());
table.getSelectionModel().clearSelection();
}
});

You can use a for loop, it make a snapshoot of your table selection and iterate in it. For exmple:
#FXML
private void deleteButtonFired(ActionEvent actionEvent) throws InterruptedException {
for(Object o : table.getSelectionModel().getSelectedItems()){
table.getItems().remove(o);
}
table.getSelectionModel().clearSelection();
}
I hope they fix this bug.

I came across a similar issue using ListView (selectedView in my case) and also guessed items were removed by indices. So I gave up on using a loop looking like the following
selectedView.getSelectionModel().getSelectedItems().forEach(i -> selectedView.getItems().remove(i));
changing it to
selectedView.getItems().removeAll(selectedView.getSelectionModel().getSelectedItems());
which worked just fine. Hope this helps anybody.

There is a way round this, using getSelectedIndices(), as the OP originally required. Here is the solution:
ArrayList<Integer> list = new ArrayList<>(listView.getSelectionModel().getSelectedIndices());
Comparator<Integer> comparator = Comparator.comparingInt(Integer::intValue);
comparator = comparator.reversed();
list.sort(comparator);
for(Integer i : list) {
listView.getItems().remove(i.intValue());
}
This works because it sorts the indices in descending order, and so only removes the highest index first so that the indices of other items to be removed are not changed as a result of the removal.
Sometimes you can't use the getSelectedItems() and removeAll(...) functions because removeAll will remove all occurences of the referenced objects. What if your list contains multiple entries with the same referenced object, and you only want to remove one of those references? That's why you need to use the getSelectedIndices() function.

Related

Selenium WebDriver Java Cycle of adding words in the same field

This is my first message and it can looks like newbie, but I need your help)
My objective is to add pairs of words from some list, click on Add button, then repeat action with another pair of words. So I created method, which receives 2 words, add them to fields and presses add button.
public static void addWord(String eng, String rus) {
driver.findElement(By.xpath("//input[contains(#placeholder, 'English Word')]")).sendKeys(eng);
driver.findElement(By.xpath("//input[contains(#placeholder, 'Russian Word')]")).sendKeys(rus);
driver.findElement(By.xpath("//button[contains(#class, 'add')]")).submit();
}
So if I do it manually by using addWord("Hello", "Привет"); it works, but I have a big list of words and want to write code, which will get pair of words from list and add them to method, then repeat until all words will be added. Please advice me how it can be done, maybe using arrays and cycles, but I don't know how to do it correctly.
You can create a Map with the key pairs and then iterate trough it:
Map<String, String> yourMap = new HashMap<String, String>()
{
{
put("Hello", "Привет");
put("Hello 2", "Привет 2");
// Add all the inputs needed
}
};
public static void addWord(Map yourMap) {
yourMap.forEach((eng, rus) -> {
driver.findElement(By.xpath("//input[contains(#placeholder, 'English Word')]")).sendKeys(eng);
driver.findElement(By.xpath("//input[contains(#placeholder, 'Russian Word')]")).sendKeys(rus);
driver.findElement(By.xpath("//button[contains(#class, 'add')]")).submit();
});
}

Remove only one element from duplicated list

I have a simple list with same elements:
private List<String> boxes = new ArrayList<>();
boxes.add("100");
boxes.add("20");
boxes.add("20");
boxes.add("5");
boxes.add("5");
boxes.add("5");
boxes.add("5");
boxes.add("5");
boxes.add("Extra life");
boxes.add("Game over");
boxes.add("Game over");
boxes.add("Game over");
I would like to remove a "Game over" value from this list, but only one, not all three. How can I do it? I tried to call remove method on this list but it removes all threee elements.
You can use this overload of the remove method:
boxes.remove("Game over");
public static void remove(List<String> boxes, String str) {
int index = boxes.indexOf(str);
if(index >= 0)
boxes.remove(index);
}
One thing about it. I am worrying about concrete implementation of List<String>. In some cases, boxes.remove(index) could take O(n). So think implementation with Iterator could be better (but not critical). It loop given list only once.
public static void remove(List<String> boxes, String str) {
Iterator<String> it = boxes.iterator();
while (it.hasNext()) {
if (it.next().equals(str)) {
it.remove();
break;
}
}
}
Moreover, as mentioned by #Andreas, List.remove(Object) removes only first found element. Approach that you tried seems to be working correctly:
public static void remove(List<String> boxes, String str) {
boxes.remove(str);
}

Issue with Sorting List using collections in java

I have a list with Order Entries. Each order entry has a base price. I want to sort this list on entry.basePrice descending (enty.basePrice == 0 entries should be at the bottom).
In this list, either no entry will have 0 price or one entry will have. I am doing like this ..
final Collection<AbstractOrderEntryModel> orderEntry = Collections2.filter(source.getEntries(),
new Predicate<AbstractOrderEntryModel>()
{
#Override
public boolean apply(final AbstractOrderEntryModel arg)
{
return arg.getBasePrice().doubleValue() == 0 ? true : false;
}
});
Here I m filtering my entry which having baseprice = 0.0
Now How I will remove and add this item (orderEntry.iterator().next()) at last in OrderEntry List?
If its not a recommended solution, and it can be possible through Collections.sort also then please give me solution.
As far as I understand, you want to put an item matching a predicate at the end of the list. This can be done straightforward:
List<AbstractOrderEntryModel> list=source.getEntries()
final Collection<AbstractOrderEntryModel> orderEntry = Collections2.filter(list,
new Predicate<AbstractOrderEntryModel>()
{
public boolean apply(final AbstractOrderEntryModel arg)
{
return arg.getBasePrice().doubleValue() == 0;
}
});
if(!orderEntry.isEmpty()) {
assert orderEntry.size()==1; // so you said in your question
AbstractOrderEntryModel e=orderEntry.iterator().next();
list.remove(e);
list.add(e);
}

Writing a method with ArrayList of strings as parameters

I am trying to write a method that takes an ArrayList of Strings as a parameter and that places a string of four asterisks in front of every string of length 4.
However, in my code, I am getting an error in the way I constructed my method.
Here is my mark length class
import java.util.ArrayList;
public class Marklength {
void marklength4(ArrayList <String> themarklength){
for(String n : themarklength){
if(n.length() ==4){
themarklength.add("****");
}
}
System.out.println(themarklength);
}
}
And the following is my main class:
import java.util.ArrayList;
public class MarklengthTestDrive {
public static void main(String[] args){
ArrayList <String> words = new ArrayList<String>();
words.add("Kane");
words.add("Cane");
words.add("Fame");
words.add("Dame");
words.add("Lame");
words.add("Same");
Marklength ish = new Marklength();
ish.marklength4(words);
}
}
Essentially in this case, it should run so it adds an arraylist with a string of "****" placed before every previous element of the array list because the lengths of the strings are all 4.
BTW
This consists of adding another element
I am not sure where I went wrong. Possibly in my for loop?
I got the following error:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at Marklength.marklength4(Marklength.java:7)
at MarklengthTestDrive.main(MarklengthTestDrive.java:18)
Thank you very much. Help is appreciated.
Let's think about this piece of code, and pretend like you don't get that exception:
import java.util.ArrayList;
public class Marklength {
void marklength4(ArrayList <String> themarklength){
for(String n : themarklength){
if(n.length() ==4){
themarklength.add("****");
}
}
System.out.println(themarklength);
}
}
Ok, so what happens if your list just contains item.
You hit the line if(n.length() ==4){, which is true because you are looking at item, so you go execute its block.
Next you hit the line themarklength.add("****");. Your list now has the element **** at the end of it.
The loop continues, and you get the next item in the list, which happens to be the one you just added, ****.
The next line you hit is if(n.length() ==4){. This is true, so you execute its block.
You go to the line themarklength.add("****");, and add **** to the end of the list.
Do we see a bad pattern here? Yes, yes we do.
The Java runtime environment also knows that this is bad, which is why it prevents something called Concurrent Modification. In your case, this means you cannot modify a list while you are iterating over it, which is what that for loop does.
My best guess as to what you are trying to do is something like this:
import java.util.ArrayList;
public class Marklength {
ArrayList<String> marklength4(ArrayList <String> themarklength){
ArrayList<String> markedStrings = new ArrayList<String>(themarklength.size());
for(String n : themarklength){
if(n.length() ==4){
markedStrings.add("****");
}
markedStrings.add(n);
}
System.out.println(themarklength);
return markedStrings;
}
}
And then:
import java.util.ArrayList;
public class MarklengthTestDrive {
public static void main(String[] args){
ArrayList <String> words = new ArrayList<String>();
words.add("Kane");
words.add("Cane");
words.add("Fame");
words.add("Dame");
words.add("Lame");
words.add("Same");
Marklength ish = new Marklength();
words = ish.marklength4(words);
}
}
This...
if(n.length() ==4){
themarklength.add("****");
}
Is simply trying to add "****" to the end of the list. This fails because the Iterator used by the for-each loop won't allow changes to occur to the underlying List while it's been iterated.
You could create a copy of the List first...
List<String> values = new ArrayList<String>(themarklength);
Or convert it to an array of String
String[] values = themarklength.toArray(new String[themarklength.size()]);
And uses these as you iteration points...
for (String value : values) {
Next, you need to be able to insert a new element into the ArrayList at a specific point. To do this, you will need to know the original index of the value you are working with...
if (value.length() == 4) {
int index = themarklength.indexOf(value);
And then add a new value at the required location...
themarklength.add(index, "****");
This will add the "****" at the index point, pushing all the other entries down
Updated
As has, correctly, been pointed out to me, the use of themarklength.indexOf(value) won't take into account the use case where the themarklength list contains two elements of the same value, which would return the wrong index.
I also wasn't focusing on performance as a major requirement for the providing a possible solution.
Updated...
As pointed out by JohnGarnder and AnthonyAccioly, you could use for-loop instead of a for-each which would allow you to dispense with the themarklength.indexOf(value)
This will remove the risk of duplicate values messing up the index location and improve the overall performance, as you don't need to create a second iterator...
// This assumes you're using the ArrayList as the copy...
for (int index = 0; index < themarklength.size(); index++) {
String value = themarklength.get(index);
if (value.length() == 4) {
themarklength.add(index, "****");
index++;
But which you use is up to you...
The problem is that in your method, you didn't modify each string in the arraylist, but only adds 4 stars to the list. So the correct way to do this is, you need to modify each element of the arraylist and replace the old string with the new one:
void marklength4(ArrayList<String> themarklength){
int index = 0;
for(String n : themarklength){
if(n.length() ==4){
n = "****" + n;
}
themarklength.set(index++, n);
}
System.out.println(themarklength);
}
If this is not what you want but you want to add a new string "**" before each element in the arraylist, then you can use listIterator method in the ArrayList to add new additional element before EACH string if the length is 4.
ListIterator<String> it = themarklength.listIterator();
while(it.hasNext()) {
String name = it.next();
if(name.length() == 4) {
it.previous();
it.add("****");
it.next();
}
}
The difference is: ListIterator allows you to modify the list when iterating through it and also allows you to go backward in the list.
I would use a ListIterator instead of a for each, listiterator.add likely do exactly what you want.
public void marklength4(List<String> themarklength){
final ListIterator<String> lit =
themarklength.listIterator(themarklength.size());
boolean shouldInsert = false;
while(lit.hasPrevious()) {
if (shouldInsert) {
lit.add("****");
lit.previous();
shouldInsert = false;
}
final String n = lit.previous();
shouldInsert = (n.length() == 4);
}
if (shouldInsert) {
lit.add("****");
}
}
Working example
Oh I remember this lovely error from the good old days. The problem is that your ArrayList isn't completely populated by the time the array element is to be accessed. Think of it, you create the object and then immediately start looping it. The object hence, has to populate itself with the values as the loop is going to be running.
The simple way to solve this is to pre-populate your ArrayList.
public class MarklengthTestDrive {
public static void main(String[] args){
ArrayList <String> words = new ArrayList<String>() {{
words.add("Kane");
words.add("Cane");
words.add("Fame");
words.add("Dame");
words.add("Lame");
words.add("Same");
}};
}
}
Do tell me if that fixes it. You can also use a static initializer.
make temporary arraylist, modify this list and copy its content at the end to the original list
import java.util.ArrayList;
public class MarkLength {
void marklength4(ArrayList <String> themarklength){
ArrayList<String> temp = new ArrayList<String>();
for(String n : themarklength){
if(n.length() ==4){
temp.add(n);
temp.add("****");
}
}
themarklength.clear();
themarklength.addAll(temp);
System.out.println(themarklength);
}
}

Gwt ListBox Alphabetical Order

I have a GWT listbox and i need to sort the contents(Strings) of listbox in alphabetical order.
What is a decent way to solve this?
I was thinking of using a Collection and Sort that or use Arrays.sort() and then populate the listbox with sorted values.Is this a right approach?
Thanks
You have already said the answer and I just code it
Arrays.sort the list
Add items to the list
Here is the code
String[] strings = new String[] { "ab", "ac", "a", "abc" };
java.util.Arrays.sort(strings);
ListBox l = new ListBox();
for (String s : strings) {
l.addItem(s);
}
I made the following method in a helper class:
public static void ListBoxAddSorted(ListBox lb, String text, String value)
{
int count = lb.getItemCount();
int j;
for ( j = 0; j < count; j++ )
{
String item = lb.getItemText( j );
if ( item.compareTo( text ) >= 0 )
{
break;
}
}
lb.insertItem( text, value, j );
}
Basically, as you insert items to the list they are inserted in alphabetical order. This way you can delete and add at will and your list will always be sorted.
Presorting doesn't work when you have many points in the application where things get added to the ListBox. You can add an event handler when it gets updated. Pseudocode:
import com.google.gwt.event.dom.client.ChangeHandler;
ChangeHandler sortMyList(){
//extract your elements, sort, then reinsert
}
ListBox lb = new ListBox();
lb.addChangeHandler( sortMyList());
The other alternative is to extend ListBox to a class called SortedListBox which keeps elements sorted as they are inserted. This would avoid O(n^2) amortized insertion cost.

Categories

Resources