I have an ArrayList that I loop and through some logic I would remove an element at a particular index.
However while I am looping the Arraylist and removing on the way, the ArrayList size and index of particular items are changing as well, resulting in unexpected results.
Anyway to circumvent this?
Here's the code for the iterator approach - substitute your own condition and add generics types <> as needed:
Iterator it = list.iterator();
while(it.hasNext()){
Object o = it.next();
if(someCondition(o)){
it.remove();
}
}
And, as JohnB said in the comments, ArrayList isn't very efficient if you are removing lots of items from large lists...
You can use an Iterator.remove() or iterate backwards.
List<String> list = ...
for(int i= list.size()-1; i>=0; i--)
if(test(list.get(i)))
list.remove(i); // values before `i` are untouched.
or you can decrement the counter.
List<String> list = ...
for(int i= 0; i < list.size(); i++)
if(test(list.get(i)))
list.remove(i--); // move i back as there is one less element.
You could use an iterator which has a remove() method to do exactly that.
Use Iterator for looping. It can be used to remove the elements from the collection.
Related
Ok, this is a proof-of-concept I have on my head that has been bugging me for a few days:
Let's say I have:
List<String> a = new ArrayList<String>();
a.add("foo");
a.add("buzz");
a.add("bazz");
a.add("bar");
for (int i = 0; i < a.size(); i++)
{
String str = a.get(i);
if (!str.equals("foo") || !str.equals("bar")) a.remove(str);
}
this would end up with the list ["foo", "bazz", "bar"] because it would read the string at index 1 ("buzz"), delete it, the string at index 2 ("bazz") would jump to index 1 and it would be bypassed without being verified.
What I came up with was:
List<String> a = new ArrayList<String>();
a.add("foo");
a.add("buzz");
a.add("bazz");
a.add("bar");
for (int i = 0; i < a.size(); i++)
{
String str = a.get(i);
boolean removed = false;
if (!str.equals("foo") || !str.equals("bar"))
{
a.remove(str);
removed = true;
}
if (removed) i--;
}
It should work this way (atleast it does in my head lol), but messing with for iterators is not really good practice.
Other way I thought would be creating a "removal list" and add items to that list that needed to be removed from list a, but that would be just plain resource waste.
So, what is the best practice to remove items from a list efficiently?
Use an Iterator instead and use Iterator#remove method:
for (Iterator<String> it = a.iterator(); it.hasNext(); ) {
String str = it.next();
if (!str.equals("foo") || !str.equals("bar")) {
it.remove();
}
}
From your question:
messing with for iterators is not really good practice
In fact, if you code oriented to interfaces and use List instead of ArrayList directly, using get method could become into navigating through all the collection to get the desired element (for example, if you have a List backed by a single linked list). So, the best practice here would be using iterators instead of using get.
what is the best practice to remove items from a list efficiently?
Not only for Lists, but for any Collection that supports Iterable, and assuming you don't have an index or some sort of key (like in a Map) to directly access to an element, the best way to remove an element would be using Iterator#remove.
You have three main choices:
Use an Iterator, since it has that handy remove method on it. :-)
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (/*...you want to remove `it.next()`...*/) {
it.remove();
}
}
Loop backward through the list, so that if you remove something, it doesn't matter for the next iteration. This also has the advantage of only calling list.size() once.
for (int index = list.size() - 1; index >= 0; --index) {
// ...check and optionally remove here...
}
Use a while loop instead, and only increment the index variable if you don't remove the item.
int index = 0;
while (index < list.size()) {
if (/*...you want to remove the item...*/) {
list.removeAt(index);
} else {
// Not removing, move to the next
++index;
}
}
Remember that unless you know you're dealing with an ArrayList, the cost of List#get(int) may be high (it may be a traversal). But if you know you're dealing with ArrayList (or similar), then...
Your first example will likely cause off-by-one errors, since once you remove an object your list's indexes will change. If you want to be quick about it, use an iterator or List's own .remove() function:
Iterator<String> itr = yourList.iterator();
while (itr.hasNext()) {
if ("foo".equals(itr.next()) {
itr.remove();
}
}
Or:
yourList.remove("foo");
yourList.removeAll("foo"); // removes all
ArrayList.retainAll has a "smart" implementation that does the right thing to be linear time. You can just use list.retainAll(Arrays.asList("foo", "bar")) and you'll get the fast implementation in that one line.
I am trying to iterate through a HashMap using 2 iterators. Firstly for every key(Integer) in the hash I compute "similar" numbers(it really doesn't matter what similar numbers are in this particular case) and then I have to delete the keys which are similar to the current key by making them values of the current key. I keep receiving this exception
Exception in thread "main" java.util.ConcurrentModificationException. What could be the cause? Do I have to use a ConcurrentHashMap?
This is the code I am compiling:
Set<Node> keySet = hash.keySet();
Iterator<Node> it = keySet.iterator();
while(it.hasNext()){
Node key = it.next();
ArrayList<Integer> similar = getAppropriateNum(key.getLabel(), 2);
for(int j = 0; j < similar.size(); j++){
Iterator<Node> it2 = keySet.iterator();
while(it2.hasNext()){
Node nod = it2.next();
if(nod.getLabel() == similar.get(j) && !nod.equals(key)){
it2.remove();
hash.put(key, nod);
}//end if
}//end while
}//end for
}//end while
The problem is that you are removing an item using an iterator (good practice) but the other iterator is not aware of that. Hence, the call to it.next() fails.
You should try to use only one iterator or remove items after looping.
EDIT : After analyzing your question, it seems that your need is to create a Collection of unique items. This makes me think that you would like to use a Set with a well-formed Comparator. In this way, adding all your items to your Set will automatically remove duplicates.
I want to iterate through some kind of list, checking if its elements meet a property, and if they don't deleting them from the array. What I've thought is something like this:
int index = 0;
for(int i = 0; i < list.size(); ++i) {
if(list.isProperty()) list.delete(index) //We delete the element at list[index]
else ++index;
}
Maybe those aren't the real methods of the list interface in java, but they're quite self-explanatory.
Is this a good approach? Which data structure would fit best if I have to run this operation many times? I don't think an arrayList would work as I'd have to be moving around elements each time I delete and I can't ensure the elements I'll remove are in the head or the tail of the list either.
You can achieve it using iterator.Without having concurrent modification exception.
Say your list consists of object A
List<A> list = new ArrayList<A>();
Iterator<A> iterator = list.iterator();
while (iterator.hasNext()) {
A current = iterator.next();
if(current.isProperty()) {
iterator.remove();;
}
}
You should remove an element from a List using an Iterator. You can use this with ArrayList.
List<YourDataType> yourList = new ArrayList<YourDataType>();
Iterator<YourDataType> it = yourList.iterator();
while (it.hasNext())
it.remove();
With this you can use if-else to specify the element, which should be removed.
This should give you some hints, why you should use an Iterator.
What is the best list/set/array in Java that combines the following aspects:
maintain order of added elements
make if possible to both iterate forwards and backwards
of course good performance
I thought about a LinkedList, I then could insert elements by add(0, element) which would simulate a reverse order. Most of the time I will be using backwards iteration, so using this I can just iterate trough.
And if not, I can list.listIterator().hasPrevious().
But are there better approaches?
ArrayList will probably be your best bet. You can iterate through it in the following manner:
for (ListIterator it = list.listIterator(list.size()); it.hasPrevious();) {
Object value = it.previous();
}
A LinkedList will work but it will have more object creation overhead since you need to instantiate a Link for each element you store.
If you can get by index and wish to iterate over the collection then you can use a List and get(index) allow you to get the object in that place in the list. Arrays allow you to do this, you can just reference the index as normal, however if your array might grow then a Collection is going to be easier to use.
You can use List.size() and element through the object using a for loop rather than using an Iterator object, this will allow you to iterator over the list both forwards and backwards. For example:
List<AnObject> myList = new ArrayList<AnObject>;
// Add things to the list
for (int i = 0 ; i < myList.size; i++) {
AnObject myObject = myList.get(i);
}
for (int i = myList.size()-1 ; i <= 0 ; i--) {
AnObject myObject = myList.get(i);
}
Set is not applicable as a Set does not maintain ordering.
I am attempting some very basic java here and have reached a bit of a head scratcher. Essentially,I need to read some element from a file into some type of array or list, sort them, eliminate duplicates, and then return the first three elements. TreeSet seemed like the perfect fit in so much as it does the sort and kills the duplicates. My issue is that I am confounded as to how to return only the first three elements. The iterator seems to run all the way through the set. Creating a while loop with a manual iterator to contain a while loop that holds the iterator loops seems confusing and unlikely to be successful. Is the answer here that I need to iterate through the treeset and place each element into an arraylist so that I can then access the first three elements? I mean, it seems that this would work but it seems highly convoluted. Tips?
Using Guava you could just do
return Lists.newArrayList(Iterables.limit(treeSet, 3));
Hm. What's wrong with the obvious?
ArrayList<MyType> buffer = new ArrayList<MyType>(3);
for( MyType elt: myTreeSet ) {
buffer.add(elt);
if( buffer.size() == 3 ) break;
}
Or
ArrayList<MyType> buffer = new ArrayList<MyType>(3);
Iterator<MyType> iter = myTreeSet.iterator();
while( iter.hasNext() && buffer.size() < 3 ) buffer.add(iter.next());
if you prefer the "desugared" version?
Example with Strings:
TreeSet<String> treeSet = new TreeSet<String>();
// you populate treeSet with data
String[] stringArray = new String[NUMBER_OF_NEEDED_RECORDS];
for(int i =0; i < NUMBER_OF_NEEDED_RECORDS; i++) {
stringArray[i] = treeSet.pollFirst();
}
I would use (expecting you use Java 1.6):
Arrays.copyOf(myTreeSet.toArray(), Math.min(3, myTreeset.size()));
Edit: to be bulletproof with the size I added Math.min()