Modifying an Array list while I am iterating over it - java

How can I modify an arraylist while iterating over it? I would like answers that only deal with whilst I am iterating it please no answers regarding that I can save it and then modify it.
for (ListIterator<CardGroup> ShortSeqGroupListIterator = ShortSeqGroupList
.listIterator(); ShortSeqGroupListIterator.hasNext();) {
CardGroup ShortSeqGroup = ShortSeqGroupListIterator.next();
System.out.println("Iteration --- "+ShortSeqGroup.getCardList());
for (ListIterator<CardGroup> cardGroupListIterator = this.cardGroupList
.listIterator(); cardGroupListIterator.hasNext();) {
CardGroup cardGroup = cardGroupListIterator.next();
if (cardGroup.getCardGroupType() == CardGroupType.PURESEQUENCE
|| cardGroup.getCardGroupType() == CardGroupType.SHORTSEQUENCE) {
continue;
}
Listindex = cardGroupListIterator.nextIndex() - 1;
listOfIndex.add(Listindex);
cardGroup.setCardGroupType(CardGroupType.NONE);
this.mergeExtraGroups();
}
ShortSeqGroup.setCardGroupType(CardGroupType.NONE);
this.mergeExtraGroups();
this.markSets();
this.markSequences(false);
int PenaltyPointsShSeq = totalPenaltyOfUser(this.cardGroupList);
PenaltyMapShortSeq.put(PenaltyPointsShSeq, this.cardGroupList);
this.cardGroupList = clonedCardGroupList;
System.out.println("&************************&");
this.print();
}
NavigableMap<Integer, List<CardGroup>> descendedPenaltyMapShortSeq=PenaltyMapShortSeq.descendingMap();
System.out.println(descendedPenaltyMapShortSeq.firstKey());
I want to operate on the list and then after saving the operation I need to get the previous state of the list back ..
The problem is of course Concurrent modification exception.
The this.cardList is the one I am operating on and the cloned cardgrouplist is a copy of it...
The cardGrouplist 1st element again contains:
An arraylist

Don't use an Iterator, switch it out for a normal for(int i = 0; i < list.length; i++) loop.

It look messy to modify a collection while iterating. I would suggest you to iterate to collect the list of items to add/delete first. Then, perform add/delete the collected item accordingly.

Related

Is removing elements from an ArrayList or LinkedList while iterating through it with a for loop in Java bad? If so, why?

I was showing my code to someone and they said that it would cause undefined behavior. Being a Java programmer, that's not something I understand well. In the following code block I am iterating through scenes, which is an ArrayList, and removing elements from it.
for(int i = 0; i < scenes.size() - 1; i++)
{
if(!(Double.valueOf(scenes.get(i + 1)) - Double.valueOf(scenes.get(i)) > 10))
{
scenes.remove(i + 1);
i--;
}
}
This compiles and doesn't throw an exception at runtime, but I'm still not sure if it's a programming no-no, why it's a programming no-no, and what is the right way to do it. I've heard about using Iterator.remove() and about just creating a whole new List.
In an ArrayList, removing an element from the middle of the list requires you to shift all of the elements with a higher index down by one. This is fine if you do it once (or a small number of times), but inefficient if you do it repeatedly.
You don't really want to use an Iterator for this either, because Iterator.remove() suffers from the same issue.
A better approach to this is to go through the list, moving the elements you want to keep to their new positions; and then just remove the tail of the list at the end:
int dst = 0;
for (int src = 0; src < scenes.size(); ++dst) {
// You want to keep this element.
scenes.set(dst, scenes.get(src++));
// Now walk along the list until you find the element you want to keep.
while (src < scenes.size()
&& Double.parseDouble(scenes.get(src)) - Double.parseDouble(scenes.get(dst)) <= 10) {
// Increment the src pointer, so you won't keep the element.
++src;
}
}
// Remove the tail of the list in one go.
scenes.subList(dst, scenes.size()).clear();
(This "shift and clear" approach is what is used by ArrayList.removeIf; you can't use that directly here because you can't inspect adjacent elements in the list, you only have access to the current element).
You can take a similar approach which will also work efficiently with non-random access lists such as LinkedList. You need to avoid repeatedly calling get and set, since these are e.g. O(size) in the case of LinkedList.
In that case, you would use ListIterator instead of plain indexes:
ListIterator<String> dst = scenes.listIterator();
for (ListIterator<String> src = scenes.listIterator(); src.hasNext();) {
dst.next();
String curr = src.next();
dst.set(curr);
while (src.hasNext()
&& Double.parseDouble(src.next()) - Double.parseDouble(curr) <= 10) {}
}
scenes.subList(dst.nextIndex(), scenes.size()).clear();
Or something like this. I've not tested it, and ListIterator is always pretty confusing to use.
This is straightforward and will work for either ArrayList or LinkedList:
Iterator<String> iterator = list.iterator();
double current = 0;
double next;
boolean firstTime = true;
while (iterator.hasNext()) {
if (firstTime) {
current = Double.parseDouble(iterator.next());
firstTime = false;
} else {
next = Double.parseDouble(iterator.next());
if (next - current > 10) {
current = next;
} else {
iterator.remove();
}
}
}

Could we remove while iterating if we just remove 1 element?

I have a list of custom objects. I need to get/remove a specific object from that list but the equals implemented would not work based on what I need to search.
The following would work:
int index = -1;
for(int i = 0; i < list.size(); i++) {
if(list.get(i).getAttr().equals(arg)) {
index = i;
break;
}
}
CustomObject = list.remove(index);
// use CustomObject here
I was wondering if I could do the list.remove inside the for loop despite not using an iterator since the loop breaks immediately
Using the delete(int) method in your loop will work just fine.
Your loop is closed so you have full control on i and you can use the list as you please. You don't use i after having deleted the first element that matches, so there are no caveat. If you were to reuse it, you would have to not increment it.
To avoid any trouble, the following if both more readable and expressive. Also, it's totally implementation-agnostic.
CustomObject deletedObject = null;
for (Iterator<CustomObject> i = list.iterator(); i.hasNext(); ) {
CustomObject candidate = i.next();
if (candidate.getAttr().equals(arg)) {
deletedObject = candidate;
i.remove();
break;
}
}
if (deletedObject != null) {
// Do something with deletedObject
}
There is no special program state associated with “being inside a for loop”. What matters, are the actions your program performs.
So
int index = -1;
for(int i = 0; i < list.size(); i++) {
if(list.get(i).getAttr().equals(arg)) {
index = i;
break;
}
}
CustomObject o = list.remove(index);
// use CustomObject here
is identical to
for(int i = 0; i < list.size(); i++) {
if(list.get(i).getAttr().equals(arg)) {
CustomObject o = list.remove(i);
// use CustomObject here
break;
}
}
as it performs the same actions (letting aside that the first variant will throw when no match has been found). The differences regarding local variables defined in these code snippets are, well, local and do not affect anything outside the containing method.
That said, the rule that you must not modify a collection (except through the iterator) while iterating over it, applies to iterator-based loops, where you are not in control of the iterator’s internal state. When you are using an index based loop and fully understand the implications of removing an object at a particular index (of a random access list), you can even continue iterating. The important aspects, to do it correctly, are that the indices of all subsequent elements decrease by one when removing an element, further the size decreases so you must either, reread the size or decrement a previously cached size value.
E.g., the following loop is valid
for(int i = 0; i < list.size(); i++) {// rereads size on each iteration
if(list.get(i).getAttr().equals(arg)) {
CustomObject o = list.remove(i--); // decrease index after removal
// use CustomObject here
// continue
}
}
But, of course, it’s more idiomatic to use an Iterator or removeIf, as these approaches are not only easier to handle, they also work with other collections than random access lists. And especially removeIf may be more efficient when you remove more than one element.
Just another way using streams,
List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("D");
str1.add("D");
Optional<Object> foundVal = str1.stream().filter(s ->
s.contains("D")).findFirst().map(val -> {
str1.remove(val);
return val;
});
System.out.println(str1);
System.out.print(" " + foundVal.get());
Output
[A, B, D] D

Iterating over List of Lists

I'm trying to iterate over a list os lists but I'm getting CME all the time even using Iterator to remove and add elements while iterating over the lists.
I searched here in the community for similar questions but those I found didn't help me. Really hope you guys help me to figure out how to do what I need to do.
I have I ListIterator<List<Event<T>>> itrListsEvent = partitionSubLists.listIterator();
partitionSubLists is A list of lists. So I have one bigger List and inside it I have four sublists.
I need to iterate over the sublists, and while iterating I remove and add elements. After finishing to iterate over the first sublist, I need to go forward to iterate over the second sublist and so on and so forth.
This is what I've done so far:
public List<List<Event<T>>> partitionedLists (List<Event<T>> list)
{
int listSize = list.size();
int partitionSize = listSize / 4;
List<List<Event<T>>> partitions = new ArrayList<>();
for (int i = 0; i < listSize; i += partitionSize)
{
partitions.add(list.subList(i, Math.min(i + partitionSize, list.size())));
}
return partitions;
}
List<List<Event<T>>> partitionSubLists = partitionedLists(List<Event<T>>);
ListIterator<List<Event<T>>> itrListsEvent = partitionSubLists.listIterator();
while(itrListsEvent.hasNext())
{
List<PrefixEvent<T>> listPE = new ArrayList<Event<T>>();
listPE = itrListsPrefixEvent.next();
ListIterator<Event<T>> itrEvent = listPE.listIterator();
while(itrEvent.hasNext())
{
//here I remove and add elements inside the sublist.
//when finished, I need to go back to first while and go forward to the next sublists
//and in this moment, i got ConcurrentModificationException
itrEvent.remove()
.
.
.
// some code here
itrEvent.add(new Event<T>);
}
}
It's rather unclear exactly what you're trying to achieve. As far as I understand, you could achieve it like this:
List<PrefixEvent<T>> listPE = itrListsPrefixEvent.next();
// No iterator.
for (int i = 0; i < listPE.size(); ++i) {
listPE.remove(i);
// some code here
listPE.add(i, new Event<>());
}
This avoids a ConcurrentModificationException because you don't structurally modify the list after creating an Iterator.
If you don't actually require the "one element removed" list in between the itrEvent.remove() and itrEvent.add(new Event<T>()), you can continue to use the ListIterator, and then set the value to a new value:
itrEvent.set(new Event<>());

Getting most recent objects in a list

How can i get the most recent objects in a List?
I have tried
int i = 5;
while(i > 0) {
Object o = list.get(list.size() - i);
i--;
}
but to no avail, i could be doing it wrong, but if i am i am unware how to fix my mistake :/
(The problem is it is still giving me the entire list)
You could just reverse the list then iterate over it:
// Your original list
List<Object> list = new ArrayList<Object>(); // Populated elsewhere
// Make a copy, so you don't corrupt the original list
List<Object> reverseList = new ArrayList<Object>(list);
// Reverse the order of its elements
Collections.reverse(reverseList);
// Now iteration will be in "latest added first" order - ie LIFO
for (Object o : reverseList) {
// Do something with o
}
I think that you're expecting your code to change the original list, but this is a wrong assumption since the code simply gets an object held by the list and doesn't remove it. Then you discard the object held by o, so there is no way to test if your method is working or not. To solve this, you've got to save the stuff produced from your method to test if it works or not.
int i = 5;
List savedJunk = new ArrayList();
while(i > 0) {
Object o = list.get(list.size() - i);
savedJunk.add(o);
i--;
}
for (Object foo : savedJunk) {
System.out.println(foo);
}
It's a good question and you pretty much had the right answer. The central idea is that items in a list appear in the order in which you added them, so to get the most recent item you need to go through the list in reverse. Here is one way to do that with a for loop.
ArrayList<String> myList = new ArrayList<String>();
myList.add("one");
myList.add("two");
myList.add("three");
myList.add("four");
for (int index = myList.size() - 1; index >= 0 ; index--) {
System.out.println(myList.get(index));
}
The output of the above code is:
four
three
two
one

Java LinkedList issues - how to remove items that meet certain qualifications

I have a question,
Here is what I need to do -
I have BankItems that are associated with numbers. I fill the list but when an objects enters that is $100 more than the lowest dollar value currently in the list, I want to delete the object that has the low value.
First - I create the list
List<BankItem> listOfBankItems = new LinkedList<BankItem>();
Later in the program I create a new BankItem object and it to the list
listOfBankItems.add(createdItem);
and after adding each item I want to check to see if the new item is $100 more than any object already in the list so I run something like this
for (int i = 0; i < listOfBankItems.size(); i++) {
int oldValue =listOfBankItems.get(i).getAmount();
int newValue = createdItem.getAmount();
int calculatedDif = newValue - oldValue;
if (calculatedDif > 100) {
listOfBankItems.remove(i);
}
}
Unfortunately, this isn't working. I don't know what it up. Maybe I shouldn't use a LinkedList? Maybe my logic is way off-base. Please help.
Thanks!!!
The problem is that the index of all items after the removed one will change after you remove that element; therefore you'll basically skip the next element after you remove one.
Use an iterator:
for (Iterator<BankItem> itr = listOfBankItems.iterator(); itr.hasNext();) {
BankItem item = itr.next();
int oldValue = item.getAmount();
int newValue = createdItem.getAmount();
int calculatedDif = newValue - oldValue;
if (calculatedDif > 100) {
itr.remove();
}
}
Your most significant issue probably relates to concurrent modification. For example, if element #49 is the one to be removed, once you remove it, the next element will now be #49, but you will be checking for #50 (as i was still incremented) - so you're probably missing elements from your check.
There are a few ways to handle this. You could remove the i++ from your for loop (leaving only the trailing semi-colon), then do this:
if (calculatedDif > 100) {
listOfBankItems.remove(i);
} else {
i++;
}
Alternatively, you could use an Iterator and its remove() method, which would handle this for you automatically.
You can also improve the performance of this by not obtaining newValue and recalculating calculatedDif on every step of the loop. Declare these 2 lines above the for loop.

Categories