Concerns with listiterator remove java - java

I am trying to work the Josephus problem in an arrayList data structure and the task is to use ListIterator. I am getting stuck at the removing part.
Lets say I have following 10 soldiers - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
And I have to step every 3rd soldier - which means remove 3, then 6, then 9, and so on.
I am able to successfully remove 3, but next time it removes 7 and not 6. Can you please review my code and tell me what I am doing wrong:
private static int suicide (List<Integer> list, int step) {
ListIterator<Integer> itr = list.listIterator();
while(itr.hasNext()&& list.size() != 1){
if((itr.nextIndex()+1) % step == 0) {
System.out.println(itr.previousIndex()+1);
itr.next();
itr.remove();
itr.next();
}
else {
itr.next();
}
if(!itr.hasNext() ){
itr=list.listIterator();
}
}
return 0;
}

One possibility is, that you make a copy of the list, and remove the items in that copy, still iterating the "original" (and skipping on iterator step in the copy).
ListIterator itr = list.listIterator();
ListIterator itr2 = copy.listIterator();
while(itr.hasNext()&& list.size() != 1){
if((itr.nextIndex()+1) % step == 0) {
System.out.println(itr.previousIndex()+1);
itr.next();
itr2.next();
itr2.remove();
itr.next();
}
else {
itr.next();
itr2.next();
}

Try this;
int i = 3;
int step = 1;
while (list.size() > i) {
list.remove(i);
step++;
i = 3 * step - step + 1;
}

Revert you design logic !
Building a new List with accepted elements only is a really best approach.
Removing is always harder to understand and to maintain.
For example you methode suicide should return a new List<> with the elements you want.
Your code will be really simpler :
List<Integer> cleanedList = new ArrayList<Integer>();
for(Integer soldier : list) {
if(solider%3!=0){
cleanedList.add(soldier);
}
}
return cleanedList;
The most of the time I prefer API that creates new objets. If you apply this rule you code will always get simpler code ! And updating parameters is not a good behavior : if you call the method twice, you obtains two differents results.

Related

remove (and count) duplicates from a list

Is it possible to iterate between two iterators of a same list and remove an item within the nested iterator?
Version 1 (does not work):
var i = all.iterator();
while (i.hasNext()) {
var a = i.next();
var j = all.iterator();
while (j.hasNext()) {
var b = j.next();
if (!a.shouldBRemoved(b)) {
a.setDuplicates(a.getDuplicates + 1);
// I want to remove the element on the fly
// because on each iteration the iterated elements will have a decreased size and will iterate faster (because of fewer elements)
// However: this does NOT work because of ConcurrentModificationException:
j.remove();
}
}
}
I get a java.util.ConcurrentModificationException, because I modify an element within the same iterator..
I can solve this issue by using another list removableItems and put those items in it:
Version 2 (works):
for (var a : all) {
for (var b : all) {
if (!a.shouldBRemoved(b)) {
a.setDuplicates(a.getDuplicates + 1);
// this works,
// however I must use an additation list to keep track of the items to be removed
// it's also not more performant than removing the elements on the fly
// because on each iteration the iterated elements has the same size
removableItems.add(b);
}
}
}
all.removeAll(removableItems);
Is there a way to solve this without needing an intermediate list removableItems? I want to remove the element on the fly.
Try to Use CopyOnWriteArrayList which support concurrent modification
List < String > myList = new CopyOnWriteArrayList < String > ();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
for (int i = 0; i < myList.size(); i++) {
System.out.println("List value: " + myList.get(i));
if (myList.get(i).equals("3")) {
myList.remove(i);
i--;
myList.add("6");
}
}
System.out.println("List Size:" + myList.size());
I found a good solution so far (Version 3):
List<Item> removeDuplicates(List<Item> all) {
var uniqueResults = new ArrayList<Item>();
for (var a : all) {
for (var b : all) {
// check if "a" and "b" is not the same instance, but have equal content
if (!a.equals(b) && a.isDeepEqualTo(b)) {
if (a.duplicates == 0 && b.duplicates == 0) {
// "a" has duplicates:
// Add only "a" and discard "b" for the rest of the loops.
uniqueResults.add(a);
}
// count the number of duplicates
a.duplicates = a.duplicates + 1;
}
}
// "a" has no duplicates, add it.
if (a.duplicates == 0 && !uniqueResults.contains(a)) {
uniqueResults.add(a);
}
}
return uniqueResults;
}
It works so far - I don't see any edge cases where this would wrongly (not) remove.
It's also better than using version 2 (with its removableItems()-list) as this is more performant (especially for huge lists) because we do not use remove or removAll, we only add items (which has O(1)).

ConcurrentModificationException When removing element using list iterator java

I have an issue removing the 1st and 2nd element of my list even by using the iterator.
I have read the following threads but can't fix my issue (those were the most relevant but I checked other material as well):
ConcurrentModificationException when trying remove element from list
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
So my code looks like this:
List<List<String>> list = cnf.read();
List<List<String>> nlist = new ArrayList<>();
for (List<String> l : list) {
if (l.size() <= 3) {
nlist.add(l);
} else {
int size = l.size();
while (size > 3) {
List<String> three = l.subList(0, 2);
three.add("Y" + (count++));
//Iterator itr = l.iterator();
ListIterator itr = l.listIterator();
int v = 0;
while (itr.hasNext()) {
itr.next();
if (v == 0 || v == 1) {
itr.remove();
v++;
}
}
l.add(0, "Y" + (count++));
size--;
nlist.add(three);
}
nlist.add(l);
}
}
for (List<String> l : nlist) {
System.out.println(l.toString());
System.out.println(l.size());
}
I get a ConcurrentModificationException at the print statement here :
System.out.println(l.toString());
I tried using iterators for my 2 for loops as well but It doesn't seem to make a difference!
I am new to posting questions so let me know If I am doing it right!
Thank you.
After A long debugging, here is the solution.
The sublist function passes by reference and not by value, a sublist created by ArrayList.subList call keeps a reference to the original list and accesses its elementData array directly.
For this reason, when adding an element to the "three" list, we alter the state of the original list. this happens here:
three.add("Y" + (count++));
A way of fixing it for this specific case is to create and initialize the "three" list the following way:
String one = l.get(0);
String two = l.get(1);
List<String> three = new ArrayList<>();
three.add(one);
three.add(two);
three.add("Y" + (count));
This allows us to manipulate our lists without getting Concurrency Exceptions (ConcurrentModificationException). However, if you are manipulating big lists, I would suggest you use another less hardcoded method for list creation.
I will mark this thread as answered and hope it helps people.

Comparing two object arraylists

First time here so I hope this makes sense!
I have two object arrays say l1 and l2, I want to run a compare between these two lists and get a the unmatched value in say in l3.
User class contains 2 Strings:
userEnteredValue
valueReturnedFromDatabase
Say, l1 contains: Java, JSF, JAXR, foo
l2 contains: JSF, JAXR
I could run a compare for matching values, but for not for non-matching values. The logic seems to be flawed. Any help?
For matching values:
for(User u1 : l1) {
for(User u2: l2) {
if(u1.getUserEnteredValue().equals(u2.getValueReturnedFromDatabase())) {
l3.add(u1);
}
}
But, for the non-matching when I say not equal to, instead of getting only the unique values I get all values.
A couple of similar posts on Stackoverflow suggest to implement the equals and hashcode method in the User class. Is this necessary, since my arraylist size don't go beyond 5 to 10.
You can do something like this:
for(User u1 : l1) {
boolean unique = true;
for(User u2: l2) {
if(u1.getUserEnteredValue().equals(u2.getValueReturnedFromDatabase())) {
unique = false;
break;
}
}
if(unique){
l3.add(u1);
}
}
You can use the contains() method from java.util.ArrayList to determine whether your list contains the object.
Example:
for(User u1:l1) {
if(!l2.contains(u1)) {
l3.add(u1);
}
}
https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#contains(java.lang.Object)
Iterate through one array and check if it "contains()" the element from other array, using the above method provided with ArrayList in java.
If you can use Java 8 and you would like some shorter format:
List<String> l1 = Arrays.asList("a", "b", "c");
List<String> l2 = Arrays.asList("b");
List<String> l3 = l1.stream().filter(e -> !l2.contains(e)).collect(Collectors.toList());
Let's make your problem even smaller.
I have 2 arrays
l1 = [1, 2]
l2 = [2, 3]
for(int i : l1) {
for(int j : l2) {
if(i != j) {
l3.add(i);
}
}
}
Now let's examine this code.
When i = 1, j = 2 then i != j i.e. 1 != 2 is true and 1 will be inserted in l3.
When i = 1, j = 3 then i != j i.e. 1 != 3 is true and 1 again will be inserted in l3.
When i = 2, j = 2 then i != j i.e. 2 != 2 is false
When i = 2, j = 3 then i != j i.e. 1 != 2 is true and 2 will be inserted in l3.
So the final array will be l3 = [1, 2] (if it is a set. or [1, 1, 2] if it is a list) i.e. all the elements of l1 will be inserted in l2.
To get unique elements of l1 you will have to check all elements of l2 for same element in l1 1 by 1 and if it is not found in complete l2 then add it in l3.
rootloop:
for(int i : l1) {
for(int j : l2) {
if(i == j) {
continue rootloop;
}
l3.add(i);
}
}
Now change the above code to work for your problem.
But these kind of searching are so common that their implementation is already given in collection framework.
for(int i : l1) {
if(!l2.contains(i)){
l3.add(i);
}
}
One could use the Array of matching entities for the creation of unmatched entities as follows:
User[] matchingUsers = ...
User[] AllUsers = ...
List<User> listOfMatchingUsers = Arrays.asList(matchingUsers);
List<User> listOfAllUsers = Arrays.asList(allUsers);
List<User> unmatchedUsers = listOfAllUsers.removeAll(listOfMatchingUsers);
I see many answers using list.contains, i do not agree as contains use direct loop so the performance will be an issue for large lists O(n^2).
Instead you can use maps, add each list to a map with key and value the same then do the following:
Iterator it = map1.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
if(map2.get(pair.getValue())!=null) {
list3.add(map2.get(pair.getValue()));
}
}

ConcurrentModification Exception while Iterating [duplicate]

We all know you can't do the following because of ConcurrentModificationException:
for (Object i : l) {
if (condition(i)) {
l.remove(i);
}
}
But this apparently works sometimes, but not always. Here's some specific code:
public static void main(String[] args) {
Collection<Integer> l = new ArrayList<>();
for (int i = 0; i < 10; ++i) {
l.add(4);
l.add(5);
l.add(6);
}
for (int i : l) {
if (i == 5) {
l.remove(i);
}
}
System.out.println(l);
}
This, of course, results in:
Exception in thread "main" java.util.ConcurrentModificationException
Even though multiple threads aren't doing it. Anyway.
What's the best solution to this problem? How can I remove an item from the collection in a loop without throwing this exception?
I'm also using an arbitrary Collection here, not necessarily an ArrayList, so you can't rely on get.
Iterator.remove() is safe, you can use it like this:
List<String> list = new ArrayList<>();
// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
// Iterator<String> iterator = list.iterator();
// while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
String string = iterator.next();
if (string.isEmpty()) {
// Remove the current element from the iterator and the list.
iterator.remove();
}
}
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.
Source: docs.oracle > The Collection Interface
And similarly, if you have a ListIterator and want to add items, you can use ListIterator#add, for the same reason you can use Iterator#remove — it's designed to allow it.
In your case you tried to remove from a list, but the same restriction applies if trying to put into a Map while iterating its content.
This works:
Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
if (iter.next() == 5) {
iter.remove();
}
}
I assumed that since a foreach loop is syntactic sugar for iterating, using an iterator wouldn't help... but it gives you this .remove() functionality.
With Java 8 you can use the new removeIf method. Applied to your example:
Collection<Integer> coll = new ArrayList<>();
//populate
coll.removeIf(i -> i == 5);
A simple test as example:
#Test
public void testRemoveIfOneList() {
List<String> outer = new ArrayList<>();
outer.add("one");
outer.add("two");
outer.add("three");
outer.removeIf(o -> o.length() == 3);
assertEquals(1, outer.size());
}
It even works when you compare two lists and want to remove from both.
#Test
public void testRemoveIfTwoLists() {
List<String> outer = new ArrayList<>();
outer.add("one");
outer.add("two");
outer.add("three");
List<String> inner = new ArrayList<>();
inner.addAll(outer);
// first, it removes from inner, and if anything is removed, then removeIf() returns true,
// leading to removing from outer
outer.removeIf(o -> inner.removeIf(i -> i.equals(o)));
assertEquals(0, outer.size());
assertEquals(0, inner.size());
}
However, if one of the list has duplicates, make sure it's iterated in the inner loop, because for inner list, it will remove all elements meeting the criteria, but for outer list, when any element is removed, it will return immediately and stops checking.
This test will fail:
#Test
public void testRemoveIfTwoListsInnerHasDuplicates() {
List<String> outer = new ArrayList<>();
outer.add("one");
outer.add("one");
outer.add("two");
outer.add("two");
outer.add("three");
outer.add("three");
List<String> inner = new ArrayList<>();
inner.addAll(outer); // both have duplicates
// remove all elements from inner(executed twice), then remove from outer
// but only once! if anything is removed, it will return immediately!!
outer.removeIf(o -> inner.removeIf(i -> i.equals(o)));
assertEquals(0, inner.size()); // pass, inner all removed
assertEquals(0, outer.size()); // will fail, outer has size = 3
}
Since the question has been already answered i.e. the best way is to use the remove method of the iterator object, I would go into the specifics of the place where the error "java.util.ConcurrentModificationException" is thrown.
Every collection class has a private class which implements the Iterator interface and provides methods like next(), remove() and hasNext().
The code for next looks something like this...
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch(IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
Here the method checkForComodification is implemented as
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
So, as you can see, if you explicitly try to remove an element from the collection. It results in modCount getting different from expectedModCount, resulting in the exception ConcurrentModificationException.
You can either use the iterator directly like you mentioned, or else keep a second collection and add each item you want to remove to the new collection, then removeAll at the end. This allows you to keep using the type-safety of the for-each loop at the cost of increased memory use and cpu time (shouldn't be a huge problem unless you have really, really big lists or a really old computer)
public static void main(String[] args)
{
Collection<Integer> l = new ArrayList<Integer>();
Collection<Integer> itemsToRemove = new ArrayList<>();
for (int i=0; i < 10; i++) {
l.add(Integer.of(4));
l.add(Integer.of(5));
l.add(Integer.of(6));
}
for (Integer i : l)
{
if (i.intValue() == 5) {
itemsToRemove.add(i);
}
}
l.removeAll(itemsToRemove);
System.out.println(l);
}
In such cases a common trick is (was?) to go backwards:
for(int i = l.size() - 1; i >= 0; i --) {
if (l.get(i) == 5) {
l.remove(i);
}
}
That said, I'm more than happy that you have better ways in Java 8, e.g. removeIf or filter on streams.
Same answer as Claudius with a for loop:
for (Iterator<Object> it = objects.iterator(); it.hasNext();) {
Object object = it.next();
if (test) {
it.remove();
}
}
With Eclipse Collections, the method removeIf defined on MutableCollection will work:
MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.lessThan(3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);
With Java 8 Lambda syntax this can be written as follows:
MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.cast(integer -> integer < 3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);
The call to Predicates.cast() is necessary here because a default removeIf method was added on the java.util.Collection interface in Java 8.
Note: I am a committer for Eclipse Collections.
Make a copy of existing list and iterate over new copy.
for (String str : new ArrayList<String>(listOfStr))
{
listOfStr.remove(/* object reference or index */);
}
People are asserting one can't remove from a Collection being iterated by a foreach loop. I just wanted to point out that is technically incorrect and describe exactly (I know the OP's question is so advanced as to obviate knowing this) the code behind that assumption:
for (TouchableObj obj : untouchedSet) { // <--- This is where ConcurrentModificationException strikes
if (obj.isTouched()) {
untouchedSet.remove(obj);
touchedSt.add(obj);
break; // this is key to avoiding returning to the foreach
}
}
It isn't that you can't remove from the iterated Colletion rather that you can't then continue iteration once you do. Hence the break in the code above.
Apologies if this answer is a somewhat specialist use-case and more suited to the original thread I arrived here from, that one is marked as a duplicate (despite this thread appearing more nuanced) of this and locked.
With a traditional for loop
ArrayList<String> myArray = new ArrayList<>();
for (int i = 0; i < myArray.size(); ) {
String text = myArray.get(i);
if (someCondition(text))
myArray.remove(i);
else
i++;
}
ConcurrentHashMap or ConcurrentLinkedQueue or ConcurrentSkipListMap may be another option, because they will never throw any ConcurrentModificationException, even if you remove or add item.
Another way is to use a copy of your arrayList just for iteration:
List<Object> l = ...
List<Object> iterationList = ImmutableList.copyOf(l);
for (Object curr : iterationList) {
if (condition(curr)) {
l.remove(curr);
}
}
A ListIterator allows you to add or remove items in the list. Suppose you have a list of Car objects:
List<Car> cars = ArrayList<>();
// add cars here...
for (ListIterator<Car> carIterator = cars.listIterator(); carIterator.hasNext(); )
{
if (<some-condition>)
{
carIterator().remove()
}
else if (<some-other-condition>)
{
carIterator().add(aNewCar);
}
}
Now, You can remove with the following code
l.removeIf(current -> current == 5);
I know this question is too old to be about Java 8, but for those using Java 8 you can easily use removeIf():
Collection<Integer> l = new ArrayList<Integer>();
for (int i=0; i < 10; ++i) {
l.add(new Integer(4));
l.add(new Integer(5));
l.add(new Integer(6));
}
l.removeIf(i -> i.intValue() == 5);
Java Concurrent Modification Exception
Single thread
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String value = iter.next()
if (value == "A") {
list.remove(it.next()); //throws ConcurrentModificationException
}
}
Solution: iterator remove() method
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String value = iter.next()
if (value == "A") {
it.remove()
}
}
Multi thread
copy/convert and iterate over another one collection. For small collections
synchronize[About]
thread safe collection[About]
I have a suggestion for the problem above. No need of secondary list or any extra time. Please find an example which would do the same stuff but in a different way.
//"list" is ArrayList<Object>
//"state" is some boolean variable, which when set to true, Object will be removed from the list
int index = 0;
while(index < list.size()) {
Object r = list.get(index);
if( state ) {
list.remove(index);
index = 0;
continue;
}
index += 1;
}
This would avoid the Concurrency Exception.
for (Integer i : l)
{
if (i.intValue() == 5){
itemsToRemove.add(i);
break;
}
}
The catch is the after removing the element from the list if you skip the internal iterator.next() call. it still works! Though I dont propose to write code like this it helps to understand the concept behind it :-)
Cheers!
Example of thread safe collection modification:
public class Example {
private final List<String> queue = Collections.synchronizedList(new ArrayList<String>());
public void removeFromQueue() {
synchronized (queue) {
Iterator<String> iterator = queue.iterator();
String string = iterator.next();
if (string.isEmpty()) {
iterator.remove();
}
}
}
}
I know this question assumes just a Collection, and not more specifically any List. But for those reading this question who are indeed working with a List reference, you can avoid ConcurrentModificationException with a while-loop (while modifying within it) instead if you want to avoid Iterator (either if you want to avoid it in general, or avoid it specifically to achieve a looping order different from start-to-end stopping at each element [which I believe is the only order Iterator itself can do]):
*Update: See comments below that clarify the analogous is also achievable with the traditional-for-loop.
final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
list.add(i);
}
int i = 1;
while(i < list.size()){
if(list.get(i) % 2 == 0){
list.remove(i++);
} else {
i += 2;
}
}
No ConcurrentModificationException from that code.
There we see looping not start at the beginning, and not stop at every element (which I believe Iterator itself can't do).
FWIW we also see get being called on list, which could not be done if its reference was just Collection (instead of the more specific List-type of Collection) - List interface includes get, but Collection interface does not. If not for that difference, then the list reference could instead be a Collection [and therefore technically this Answer would then be a direct Answer, instead of a tangential Answer].
FWIWW same code still works after modified to start at beginning at stop at every element (just like Iterator order):
final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
list.add(i);
}
int i = 0;
while(i < list.size()){
if(list.get(i) % 2 == 0){
list.remove(i);
} else {
++i;
}
}
One solution could be to rotate the list and remove the first element to avoid the ConcurrentModificationException or IndexOutOfBoundsException
int n = list.size();
for(int j=0;j<n;j++){
//you can also put a condition before remove
list.remove(0);
Collections.rotate(list, 1);
}
Collections.rotate(list, -1);
Try this one (removes all elements in the list that equal i):
for (Object i : l) {
if (condition(i)) {
l = (l.stream().filter((a) -> a != i)).collect(Collectors.toList());
}
}
You can use a while loop.
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry<String, String> entry = iterator.next();
if(entry.getKey().equals("test")) {
iterator.remove();
}
}
I ended up with this ConcurrentModificationException, while iterating the list using stream().map() method. However the for(:) did not throw the exception while iterating and modifying the the list.
Here is code snippet , if its of help to anyone:
here I'm iterating on a ArrayList<BuildEntity> , and modifying it using the list.remove(obj)
for(BuildEntity build : uniqueBuildEntities){
if(build!=null){
if(isBuildCrashedWithErrors(build)){
log.info("The following build crashed with errors , will not be persisted -> \n{}"
,build.getBuildUrl());
uniqueBuildEntities.remove(build);
if (uniqueBuildEntities.isEmpty()) return EMPTY_LIST;
}
}
}
if(uniqueBuildEntities.size()>0) {
dbEntries.addAll(uniqueBuildEntities);
}
If using HashMap, in newer versions of Java (8+) you can select each of 3 options:
public class UserProfileEntity {
private String Code;
private String mobileNumber;
private LocalDateTime inputDT;
// getters and setters here
}
HashMap<String, UserProfileEntity> upMap = new HashMap<>();
// remove by value
upMap.values().removeIf(value -> !value.getCode().contains("0005"));
// remove by key
upMap.keySet().removeIf(key -> key.contentEquals("testUser"));
// remove by entry / key + value
upMap.entrySet().removeIf(entry -> (entry.getKey().endsWith("admin") || entry.getValue().getInputDT().isBefore(LocalDateTime.now().minusMinutes(3)));
The best way (recommended) is use of java.util.concurrent package. By
using this package you can easily avoid this exception. Refer
Modified Code:
public static void main(String[] args) {
Collection<Integer> l = new CopyOnWriteArrayList<Integer>();
for (int i=0; i < 10; ++i) {
l.add(new Integer(4));
l.add(new Integer(5));
l.add(new Integer(6));
}
for (Integer i : l) {
if (i.intValue() == 5) {
l.remove(i);
}
}
System.out.println(l);
}
Iterators are not always helpful when another thread also modifies the collection. I had tried many ways but then realized traversing the collection manually is much safer (backward for removal):
for (i in myList.size-1 downTo 0) {
myList.getOrNull(i)?.also {
if (it == 5)
myList.remove(it)
}
}
In case ArrayList:remove(int index)- if(index is last element's position) it avoids without System.arraycopy() and takes not time for this.
arraycopy time increases if(index decreases), by the way elements of list also decreases!
the best effective remove way is- removing its elements in descending order:
while(list.size()>0)list.remove(list.size()-1);//takes O(1)
while(list.size()>0)list.remove(0);//takes O(factorial(n))
//region prepare data
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<Integer> toRemove = new ArrayList<Integer>();
Random rdm = new Random();
long millis;
for (int i = 0; i < 100000; i++) {
Integer integer = rdm.nextInt();
ints.add(integer);
}
ArrayList<Integer> intsForIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsDescIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsIterator = new ArrayList<Integer>(ints);
//endregion
// region for index
millis = System.currentTimeMillis();
for (int i = 0; i < intsForIndex.size(); i++)
if (intsForIndex.get(i) % 2 == 0) intsForIndex.remove(i--);
System.out.println(System.currentTimeMillis() - millis);
// endregion
// region for index desc
millis = System.currentTimeMillis();
for (int i = intsDescIndex.size() - 1; i >= 0; i--)
if (intsDescIndex.get(i) % 2 == 0) intsDescIndex.remove(i);
System.out.println(System.currentTimeMillis() - millis);
//endregion
// region iterator
millis = System.currentTimeMillis();
for (Iterator<Integer> iterator = intsIterator.iterator(); iterator.hasNext(); )
if (iterator.next() % 2 == 0) iterator.remove();
System.out.println(System.currentTimeMillis() - millis);
//endregion
for index loop: 1090 msec
for desc index: 519 msec---the best
for iterator: 1043 msec
you can also use Recursion
Recursion in java is a process in which a method calls itself continuously. A method in java that calls itself is called recursive method.

Deleting while iterating in java! but deleting other then the current object

iterator.remove(). is good however my question: if I want while to iterate, for a kind of condition, to delete another object from the array List.
Example (al being the arraylist)
for (Iterator i = al.iterator(); i.hasNext(); ){
IEvent event =(IEvent) i.next();
if (nbSendingWTS > 0 || nbSendingCTS > 0){
i.remove();
al.remove(swtsee);
al.remove(sdctsee);
System.out.println("dropping evtg");
}
This is giving me an error: Exception in thread "main" java.util.ConcurrentModificationException
Also the normal iteration:
for(IEVEnt event:al){}
is giving an error
To be more clear the swtsee a d sdctsee are taken from previous iterations on the arraylist and saved so i can delete if I have the new condition. So is there a way when I detect them to shift them to higher indexes and then I use a reverse iteration?
What to do?
You can't remove element as discussed by you.
Do not delete while iterating.
Keep a Hash for all the objects you want to delete.
Do a second iteration which delete using .remove() if the object is in Hash.
You can't delete if you are using for each style or iterator.
Use normal for loop like following
for(int i=0; i<al.size ; i++){
if(something){
al.remove(i)
i--;
}
}
This will work.
To remove with an Iterator, you collect your stuff in a new Collection, and remove in a final step, for example:
// list := List (1, 2, 4, 3, 4, 9, 6, 5, 7, 8);
List <Integer> toRemove = new ArrayList <Integer> ();
for (int i : list)
if (i % 2 == 0)
toRemove.add (i);
list.removeAll (toRemove);
I can't see how a1 is connected to your i. As long as it isn't iterated over, it should be secure to call those 2 a1.remove (...)-ings while iterating.
For your reference Java Collections
your code should work fine commenting the following lines
for (Iterator i = al.iterator(); i.hasNext(); )
{
IEvent event =(IEvent) i.next();
if (nbSendingWTS > 0 || nbSendingCTS > 0)
{
// You have got the iterator for the underlying array list(al)
**only remove the elements through iterator.**
i.remove();
// after remove thru iterator
// you are structurally modifiying arraylist directly(al.remove())
// which gives u concurrent modification
// al.remove(swtsee);
// al.remove(sdctsee);
System.out.println("dropping evtg");
}
}
and the best way to do is
List<Integer> l = new ArrayList<Integer>();
List<Integer> itemsToRemove = new ArrayList<Integer>();
for (int i=0; i < 10; ++i) {
l.add(new Integer(1));
l.add(new Integer(2));
}
for (Integer i : l)
{
if (i.intValue() == 2)
itemsToRemove.add(i);
}
l.removeAll(itemsToRemove);
System.out.println(l);

Categories