This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 2 years ago.
I have a String List.On a satisfied Condition ,I need to group strings and remove those before iterating.
For example;
List<String> test = new ArrayList();
List<String> newLst = new ArrayList();
test.add("A1");
test.add("A2");
test.add("A3");
test.add("A1B1");
test.add("C1");
for(String s: test){
if(s.startsWith("A"){
newLst.add(s);
test.remove(s);
}
}
Once A1 reaches the loop,collect related string in new list and remove it from existing.
Getting Concurrent Modification exception.Kindly help to solve this.
Output:
newLst: A1,A2,A3
test : A1B1,C1
newLst : A1B1
test:C1
newLst : C1
You could use an explicit Iterator to iterate over the List, and use that Iterator's remove() method inside the loop (instead of List's remove()), but it would be simpler to remove from test all the elements added to newLst after the loop:
for(String s: test){
if(s.startsWith("A"){
newLst.add(s);
}
}
test.removeAll(newLst);
You can filter out elements starting with A using stream:
List<String> result = test.stream()
.filter(line -> line.startsWith("A"))
.collect(Collectors.toList());
result.forEach(System.out::println); //This is for printing elements
Why you are getting ConcurrentModificationException ?
That's because you are trying to modify the collection while iterating over it's items. The only safe way to modify your collection during iteration Iterator.remove(); The same applies for Iterator.add() (Whether you delete or add an item it counts as a modification).
JDK < 8 SOLUTION
List<String> sourceList = new ArrayList();
List<String> destList = new ArrayList();
sourceList.add("A1");
sourceList.add("A2");
sourceList.add("A3");
sourceList.add("A1B1");
sourceList.add("C1");
Iterator<String> iterator = sourceList.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if(s.startsWith("A"){
destList.add(s);
iterator.remove(s);
}
}
JDK > 8 SOLUTION
List<String> destList = sourceList.stream()
.filter(item -> item.startsWith("A"))
.collect(Collectors.toList());
Note that Java streams use Spliterator which quite different than an Iterator.
A stream source is described by an abstraction called Spliterator. As its name suggests, Spliterator combines two behaviors: accessing the elements of the source (iterating), and possibly decomposing the input source for parallel execution (splitting).
For further details , i advise you to check this interesting post about how streams works under the hood
Related
This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 8 years ago.
I'm trying to remove some elements from an ArrayList while iterating it like this:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
Of course, I get a ConcurrentModificationException when trying to remove items from the list at the same time when iterating myArrayList. Is there some simple solution to solve this problem?
Use an Iterator and call remove():
Iterator<String> iter = myArrayList.iterator();
while (iter.hasNext()) {
String str = iter.next();
if (someCondition)
iter.remove();
}
As an alternative to everyone else's answers I've always done something like this:
List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
if (someCondition) {
toRemove.add(str);
}
}
myArrayList.removeAll(toRemove);
This will avoid you having to deal with the iterator directly, but requires another list. I've always preferred this route for whatever reason.
Java 8 user can do that: list.removeIf(...)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.removeIf(e -> (someCondition));
It will remove elements in the list, for which someCondition is satisfied
You have to use the iterator's remove() method, which means no enhanced for loop:
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
iterator.next();
if (someCondition) {
iterator.remove();
}
}
No, no, NO!
In single threated tasks you don't need to use Iterator, moreover, CopyOnWriteArrayList (due to performance hit).
Solution is much simpler: try to use canonical for loop instead of for-each loop.
According to Java copyright owners (some years ago Sun, now Oracle) for-each loop guide, it uses iterator to walk through collection and just hides it to make code looks better. But, unfortunately as we can see, it produced more problems than profits, otherwise this topic would not arise.
For example, this code will lead to java.util.ConcurrentModificationException when entering next iteration on modified ArrayList:
// process collection
for (SomeClass currElement: testList) {
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
}
}
But following code works just fine:
// process collection
for (int i = 0; i < testList.size(); i++) {
SomeClass currElement = testList.get(i);
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
i--; //to avoid skipping of shifted element
}
}
So, try to use indexing approach for iterating over collections and avoid for-each loop, as they are not equivalent!
For-each loop uses some internal iterators, which check collection modification and throw ConcurrentModificationException exception. To confirm this, take a closer look at the printed stack trace when using first example that I've posted:
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 TestFail.main(TestFail.java:43)
For multithreading use corresponding multitask approaches (like synchronized keyword).
While other suggested solutions work, If you really want the solution to be made thread safe you should replace ArrayList with CopyOnWriteArrayList
//List<String> s = new ArrayList<>(); //Will throw exception
List<String> s = new CopyOnWriteArrayList<>();
s.add("B");
Iterator<String> it = s.iterator();
s.add("A");
//Below removes only "B" from List
while (it.hasNext()) {
s.remove(it.next());
}
System.out.println(s);
If you want to modify your List during traversal, then you need to use the Iterator. And then you can use iterator.remove() to remove the elements during traversal.
List myArrayList = Collections.synchronizedList(new ArrayList());
//add your elements
myArrayList.add();
myArrayList.add();
myArrayList.add();
synchronized(myArrayList) {
Iterator i = myArrayList.iterator();
while (i.hasNext()){
Object object = i.next();
}
}
One alternative method is convert your List to array, iterate them and remove them directly from the List based on your logic.
List<String> myList = new ArrayList<String>(); // You can use either list or set
myList.add("abc");
myList.add("abcd");
myList.add("abcde");
myList.add("abcdef");
myList.add("abcdefg");
Object[] obj = myList.toArray();
for(Object o:obj) {
if(condition)
myList.remove(o.toString());
}
You can use the iterator remove() function to remove the object from underlying collection object. But in this case you can remove the same object and not any other object from the list.
from here
I want to remove from a list of Employee (list1) objects that are not present in another list of Employee (list2) by their id and add removed objects from list1 into another list (list3) using Java 8.
Example :
List<Employee> list1 = Stream.of(
new Employee("100","Boston","Massachusetts"),
new Employee("400","Atlanta","Georgia"),
new Employee("300","pleasanton","California"),
new Employee("200","Decatur","Texas"),
new Employee("500","Cumming","Atlanta"),
new Employee("98","sula","Maine"),
new Employee("156","Duluth","Ohio"))
.collect(Collectors.toList());
From the above list need to remove Employee object based on id of below list.
List<Employee> list2 = Stream.of(
new Employee("100","Boston","Massachusetts"),
new Employee("800","pleasanton","California"),
new Employee("400","Atlanta","Georgia"),
new Employee("10","Decatur","Texas"),
new Employee("500","Cumming","Atlanta"),
new Employee("50","sula","Maine"),
new Employee("156","Duluth","Ohio"))
.collect(Collectors.toList());
Expected Output :
list1 and list3
List<Employee> list1 = Stream.of(
new Employee("100","Boston","Massachusetts"),
new Employee("400","Atlanta","Georgia"),
new Employee("500","Cumming","Atlanta"),
new Employee("156","Duluth","Ohio"))
.collect(Collectors.toList());
List<Employee> list3 = Stream.of(
new Employee("300","pleasanton","California"),
new Employee("200","Decatur","Texas"),
new Employee("98","sula","Maine")
)
.collect(Collectors.toList());
Tried below way but not working as expected
List<Employee> list3 = new ArrayList<>();
if(CollectionUtils.isNotEmpty(list1) && CollectionUtils.isNotEmpty(list2)){
list2.stream().forEachOrdered( l2 -> {
Optional<Employee> nonMatch = list1.stream().filter(l1 -> !l1.getId().equalsIgnoreCase(l2.getId())).findAny();
if(nonMatch.isPresent()){
list3.add(nonMatch.get());
list1.removeIf(l1 -> l1.getId().equalsIgnoreCase(nonMatch.get().getId()));
}
});
}
System.out.println(list1);
System.out.println(list3);
Here come two possible solutions.
This one is short and concise, but does in fact not remove elements from list1 but utilizes a partitioning collector to create the two lists. Think of the partitioning collector as kind of a two-way filter: if your predicate is fulfilled, collect to one list, if it's not, collect to the other list. The predicate in our case actually is "does list2 contain an employee with the same ID as the stream element from list1?". In order to lower the actual overhead, the code prepares a list of IDs from list2 up-front.
final List<String> list2Ids = list2.stream()
.map(Employee::getId)
.collect(Collectors.toList());
Map<Boolean, List<Employee>> partitioned = list1.stream()
.collect(Collectors.partitioningBy(e -> list2Ids.contains(e.getId())));
list1 = partitioned.get(true);
List<Employee> list3 = partitioned.get(false);
If you need to keep list1 - e.g. for memory foot-print reasons - and really have to remove the elements from it, I'd say you will have to stick to the really old-fashioned iterator. The reason for that is that iterators allow you to iterate some collection and remove elements while doing so. The next sample does exactly this. Note, that I prepared a list of IDs of list2 up-front again.
final List<String> list2Ids = list2.stream()
.map(Employee::getId)
.collect(Collectors.toList());
final List<Employee> list3 = new LinkedList<>();
for (Iterator<Employee> iterator = list1.iterator(); iterator.hasNext();) {
Employee next = iterator.next();
if (!list2Ids.contains(next.getId())) {
list3.add(next);
iterator.remove();
}
}
Issues with your current code:
Remember as the rule of thumb: every time you're finding yourself changing something outside the stream using forEach, most likely something is going wrong (*have a look at the API documentation. You can invoke forEach() method on any type of collection without creating a stream, but keep in mind that using a multiline lambda inside forEach() doesn't bring you any advantage over a plain for loop.
it's highly advisable to give your variable clear self-explanatory names. Like emplDeparmentX, empleOldDB, etc. Names list1 makes more difficult to reason about the code.
You can address this problem in the following steps (time complexity of each step is linear):
create a Set of id contained in the list2 (remainder: the time complexity of the contains check is O(1));
generate the list of Employee (denoted as nonCommonId in the code) from the list1 that have no common id with Employee contained in the list2 by checking every id from the list1 against the Set obtained at the previous step.
Removing employees with different id from a list separately causes additional performance overhead because each removal has a linear time complexity. A better option will be to use removeAll().
apply removeAll() on the list id discard all the employee that are present in the list obtained at the previous step.
The overall time complexity O(n + m) (n and m - are numbers of elements in the list1 and list2).
Set<String> idSet = list2.stream()
.map(Employee::id)
.collect(Collectors.toSet());
List<Employee> nonCommonId = list1.stream()
.filter(emp -> !idSet.contains(emp.id()))
.collect(Collectors.toList());
list1.removeAll(new HashSet<>(nonCommonId)); // list `nonCommonId` is wrapped by the HashSet to improve performance, because `removeAll()` relies on the `contains()` checks
// diplaying results
System.out.println("List1:");
list1.forEach(System.out::println);
System.out.println("nonCommonId:");
nonCommonId.forEach(System.out::println);
Output:
List1:
Employee[id=100, city=Boston, state=Massachusetts]
Employee[id=400, city=Atlanta, state=Georgia]
Employee[id=500, city=Cumming, state=Atlanta]
Employee[id=156, city=Duluth, state=Ohio]
nonCommonId:
Employee[id=300, city=pleasanton, state=California]
Employee[id=200, city=Decatur, state=Texas]
Employee[id=98, city=sula, state=Maine]
A link to Online Demo
First get the distinct id list from list2
idList = list2.stream().map(Employee::getID).distinct().collect(Collectors.toList())
Remove the employees having common id from list1 and get list3.
ArrayList<Employee> list3 = list1.stream().filter(e->!idList .contains(e.getID())).collect(Collectors.toList());
list1 = list1.stream().filter(e->idList.contains(e.getID())).collect(Collectors.toList());
This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 8 years ago.
I'm trying to remove some elements from an ArrayList while iterating it like this:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
Of course, I get a ConcurrentModificationException when trying to remove items from the list at the same time when iterating myArrayList. Is there some simple solution to solve this problem?
Use an Iterator and call remove():
Iterator<String> iter = myArrayList.iterator();
while (iter.hasNext()) {
String str = iter.next();
if (someCondition)
iter.remove();
}
As an alternative to everyone else's answers I've always done something like this:
List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
if (someCondition) {
toRemove.add(str);
}
}
myArrayList.removeAll(toRemove);
This will avoid you having to deal with the iterator directly, but requires another list. I've always preferred this route for whatever reason.
Java 8 user can do that: list.removeIf(...)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.removeIf(e -> (someCondition));
It will remove elements in the list, for which someCondition is satisfied
You have to use the iterator's remove() method, which means no enhanced for loop:
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
iterator.next();
if (someCondition) {
iterator.remove();
}
}
No, no, NO!
In single threated tasks you don't need to use Iterator, moreover, CopyOnWriteArrayList (due to performance hit).
Solution is much simpler: try to use canonical for loop instead of for-each loop.
According to Java copyright owners (some years ago Sun, now Oracle) for-each loop guide, it uses iterator to walk through collection and just hides it to make code looks better. But, unfortunately as we can see, it produced more problems than profits, otherwise this topic would not arise.
For example, this code will lead to java.util.ConcurrentModificationException when entering next iteration on modified ArrayList:
// process collection
for (SomeClass currElement: testList) {
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
}
}
But following code works just fine:
// process collection
for (int i = 0; i < testList.size(); i++) {
SomeClass currElement = testList.get(i);
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
i--; //to avoid skipping of shifted element
}
}
So, try to use indexing approach for iterating over collections and avoid for-each loop, as they are not equivalent!
For-each loop uses some internal iterators, which check collection modification and throw ConcurrentModificationException exception. To confirm this, take a closer look at the printed stack trace when using first example that I've posted:
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 TestFail.main(TestFail.java:43)
For multithreading use corresponding multitask approaches (like synchronized keyword).
While other suggested solutions work, If you really want the solution to be made thread safe you should replace ArrayList with CopyOnWriteArrayList
//List<String> s = new ArrayList<>(); //Will throw exception
List<String> s = new CopyOnWriteArrayList<>();
s.add("B");
Iterator<String> it = s.iterator();
s.add("A");
//Below removes only "B" from List
while (it.hasNext()) {
s.remove(it.next());
}
System.out.println(s);
If you want to modify your List during traversal, then you need to use the Iterator. And then you can use iterator.remove() to remove the elements during traversal.
List myArrayList = Collections.synchronizedList(new ArrayList());
//add your elements
myArrayList.add();
myArrayList.add();
myArrayList.add();
synchronized(myArrayList) {
Iterator i = myArrayList.iterator();
while (i.hasNext()){
Object object = i.next();
}
}
One alternative method is convert your List to array, iterate them and remove them directly from the List based on your logic.
List<String> myList = new ArrayList<String>(); // You can use either list or set
myList.add("abc");
myList.add("abcd");
myList.add("abcde");
myList.add("abcdef");
myList.add("abcdefg");
Object[] obj = myList.toArray();
for(Object o:obj) {
if(condition)
myList.remove(o.toString());
}
You can use the iterator remove() function to remove the object from underlying collection object. But in this case you can remove the same object and not any other object from the list.
from here
This question already has answers here:
Concurrent Modification Exception : adding to an ArrayList
(10 answers)
Closed 5 years ago.
When I try to modify a collection while iterating through it, will result in ConcurrentModification exception (i.e., when using forEach and forEachRemaining)
But below code won't why?
List<String> list=new ArrayList<>();
list.add("ram");
list.add("ravi");
Iterator<String> it=list.iterator();
while(it.hasNext()){
System.out.printing(it.next());
it.remove();
}
You ask why
List<String> list = new ArrayList<>();
Iterator<String> it = list.iterator();
while(it.hasNext()) {
System.out.printing(it.next());
}
does not throw a CCME.
The answer is simple. A CCME is thrown if you modify a list while iterating it. You are iterating the list, but you are not modifying it at the same time. Therefore the condition for throwing a CCME is not satisfied.
(The same reasoning applies in the case of a non-empty list too ...)
Let me break it down for you. You said:
When I try to modify a collection while iterating through it, will result in ConcurrentModification exception.
(This is correct as a general statement, by the way. But not universally.)
Reducing that to simple propositional logic we get:
exception_thrown = iterating_collection AND modifying_collection
In your example
iterating_collection is TRUE
modifying_collection is FALSE
but
TRUE AND FALSE is FALSE
therefore
exception_thrown is FALSE
In English, CCME is not thrown.
Now for your updated code:
List<String> list = new ArrayList<>();
list.add("ram");
list.add("ravi");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.printing(it.next());
it.remove();
}
In this case, you are modifying the collection using the iterator. This is the one situation where you are allowed to modify a collection while iterating it.
Think about it. If Iterator.remove() was not permitted to remove an element from the collection that is being iterated, it would be a fundamentally useless operation!
This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 7 years ago.
I've stumbled upon this code, which throws a ConcurrentModificationException
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
for (String s : list) {
if (s.equals("a"))
list.remove(s);
}
If you add an Iterator and use a while-loop, the code works fine:
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String s = iter.next();
if (s.equals("a")) {
iter.remove();
}
}
I dont understand, why it is necessary, to use Iterator<String> in this case.
Is it because of the ArrayList not having some sort of ability to get iterated, altough it is a subclass of Collection?
Is it necessary to use the while-loop, or could you produce a version with a for-loop?
If a Collection is modified while iterating over it, in most of the implementations, a ConcurrentModificationException is thrown.
The "foreach" version:
for (String s : list) {
if (s.equals("a"))
list.remove(s);
}
internally is equivalent to
for(Iterator<String> i = list.iterator(); i.hasNext(); ) {
String s = i.next();
if (s.equals("a"))
list.remove(s);
}
As you can see, an interator is created, but the list is modified directly. The list can only be modified by the iterator i used to iterate over it.
Your link actually explains it pretty well:
In the foreach loop, compiler will make the .next() called after the operation of removing element, which caused the ConcurrentModificationException.
The for uses an iterator internally. But you delete on the list directly => CME. The internal call to next after the deletion will throw it because it finds the list modified.
In the example with while, you delete through the iterator, which works fine.
When removing object from collections, use ITERATORS
Your code does not work because you are modifying the collection while looping over it. According to Oracle's javadoc the only safe way to do it is using iterators.
Note that Iterator.remove() is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.
Have a look at this for further infos