LinkedList: remove an object - java

Is this a valid way to find and remove item from a LinkedList in Java using a for each loop, is it possible that inconsistency may arise:
for(ObjectType ob : obList) {
if(ob.getId() == id) {
obList.remove(ob);
break;
}
}

Others have mentioned the valid point that normally this is not how you remove an object from a collection. HOWEVER, in this case it's fine since you break out of the loop once you remove.
If you want to keep iterating after a remove, though, you need to use an iterator. Otherwise you'll get a ConcurrentModificationException, or in the more general case, undefined behavior.
So yes, if you break out of the foreach after you remove, you'll be fine.
To those who's saying that this will fail because you can't modify a collection in a foreach -- this is true only if you want to keep iterating. That's not the case here, so this shortcut is fine.
A ConcurrentModificationException is checked and thrown by the iterator. Here, after the remove (which qualifies as concurrent modification), you break out of the loop. The iterator doesn't even get a chance to detect it.
It may be best if you add a comment on the break, why it's absolutely necessary, etc, because if this code is later modified to continue iterating after a remove, it will fail.
I would treat this idiom similar to goto (or rather, labeled break/continue): it may seem wrong at first, but when used wisely, it makes for a cleaner code.

It is best to use an iterator and use it's remove method when searching for an object by iterating over a collection in order to remove it. This is because
The collection could be, for example, a linked list (and in your case it is) whose remove method means searching for the object all over again, which search could have O(n) complexity.
You can't continue iteration after the remove unless you use the iterator's remove method. Right now you are removing the first occurrence - in future you might need to remove all matching occurrences, in which case you then have to rewrite the loop.
I recommend, on principle, foregoing the enhanced for and using something like this instead:
for(Iterator<ObjectType> it=obList.iterator(); it.hasNext(); ) {
if(it.next().getId()==id) {
it.remove();
break;
}
}
That way you are not making assumptions about the underlying list that could change in the future.
Compare the code to remove the last entry called by the iterator remove (formatting Sun's):
private E remove(Entry<E> e) {
if (e == header)
throw new NoSuchElementException();
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
return result;
}
against what remove(Object) must do:
public boolean remove(Object o) {
if (o==null) {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (e.element==null) {
remove(e);
return true;
}
}
} else {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (o.equals(e.element)) {
remove(e);
return true;
}
}
}
return false;
}

You should use iterator.remove():
Removes from the underlying collection
the last element returned by the
iterator (optional operation). This
method can be called only once per
call to next. The behavior of an
iterator is unspecified if the
underlying collection is modified
while the iteration is in progress in
any way other than by calling this
method.

Edit: Indeed, it will not fail thanks to the break. See polygenelubricant's answer for details.
However, this is dangerous way to do. To concurrently iterate and modify a collection in Java, you must use the "ListIterator" object, and use the iterator's own "add()" and "remove()" methods, and not use the ones on the collection.
You can check the java doc for the "java.util.Iterator" and "java.util.ListIterator" classes

Try something like this:
Iterator<ObjectType> iter = obList.iterator();
while (iter.hasNext()) {
ObjectType ob = iter.next();
if(ob.getId() == id) {
iter.remove();
break;
}
}
That's one of the last places where an Iterator cannot be replaced by a foreach loop.

To avoid a ConcurrentModifiationException, you could do:
final Iterator<ObjectType> i = obList.iterator();
while (i.hasNext()) {
if (i.next().getId() == id) {
i.remove();
}
}
or
for (int i = 0; i < obList.size(); i++) {
if (obList[i].getId() == id) {
obList.remove(i);
}
}
I would prefer the first. Handling indices is more errorprone and the iterator may be implemented efficiently. And the first suggestion works with Iterable while the second requires a List.

The above second loop should be changed a bit
for (int i = 0; i < obList.size(); ) {
if (obList.get(i).getId() == id) {
obList.remove(i);
continue
}
++i;
}
or
for (int i = obList.size() - 1; i >= 0; --i) {
if (obList.get(i).getId() == id) {
obList.remove(i);
}
}

A CopyOnWriteArrayList might be what you're looking for. When mutative operations are performed, a copy of the underlying array is made. This allows modification of list elements while inside a for-each loop. Remember though that this is not a linked list and can be quite inefficient.
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Main {
public static void main(String[] args) {
List<String> myList = new CopyOnWriteArrayList<String>();
myList.add("a");
myList.add("b");
myList.add("c");
// Will print [a, b, c]
System.out.println(myList);
for (String element : myList) {
if (element.equals("a")) {
myList.remove(element);
}
}
// Will print [b, c]
System.out.println(myList);
}
}

in java8 you can just use Collection#removeIf
like this:
List<String> myList = new ArrayList<String>();
myList.removeIf(tex-> Objects.isNull(tex)); // myList.removeIf(Objects::isNull);

Related

Java how to remove element from List efficiently

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.

Is there an Iterable which allows Iterable.remove() to be called by another instance?

Is there a Guava Iterator (or methodology) for List objects which allows two iterator instances to exist - in the same memory scope - while allowing the remove() operation? (Bonus point: if it works for a Collection).
Example use case: an outer and an inner iteration through a collection, where the inner loop might decide to remove an element and the outer loop would subsequently skip it.
Imagine how it would benefit the following concept code (which uses Guava static imports) by reducing the number of elements to compare in the loop and also removing the need to remove empty sets from the list at the end:
private <T> Set<Set<T>> disjointify(Collection<Set<T>> sets) {
List<Set<T>> disjoint = newArrayList(sets);
for (Set<T> set1 : disjoint) {
for (Set<T> set2 : filter(disjoint, not(equalTo(set1)))) {
if (!intersection(set1, set2).isEmpty()) {
// this wouldn't be safe for a Set<Set<T>>
set1.addAll(set2);
set2.clear();
}
}
}
return newHashSet(filter(disjoint, NO_EMPTIES));
}
private static final Predicate<Set<?>> NO_EMPTIES = new Predicate<Set<?>>() {
#Override
public boolean apply(Set<?> input) {
if (input == null || input.isEmpty()) {
return false;
}
return true;
}
};
Note: one can easily imagine creating the implementation - especially for LinkedList - I'm just asking if one already exists here.
For the record, if an efficient Iterable did already exist, and worked for Sets, then the use case would look like the following (I have created my own very inefficient Iterable which achieves this, but it is 50 lines long and ludicrously inefficient - so I use the original code above):
private <T> void disjointify(Set<Set<T>> sets) {
for (Set<T> set1 : nestable(sets)) {
Iterator<Set<T>> it = filter(nestable(sets), not(equalTo(set1))).iterator();
while (it.hasNext()) {
Set<T> set2 = it.next();
if (!intersection(set1, set2).isEmpty()) {
set1.addAll(set2);
it.remove();
}
}
}
}
It would appear that such an implementation does not exist in standard libraries.
Why not just filter the outer look with NO_EMPTIES? Since filter is evaluated during iteration, any newly empty sets would not be returned in the filtered list / outer loop.
Otherwise, I don't think so. You would get a ConcurrentModificationException on the outter loop.

concurrent modification exception java, I dont think I can use iterator here?

I have a list I need to iterate over and delete certain items. I can't use an iterator because I need to call methods for each item (such as ls.getStatus()) which doesn't work with an iterator. If ls.getStatus() == 0 I need to delete that item. How can I avoid the ConcurrentModificationException?
for (MyList ls : list) {
if (ls.getStatus() == 0) {
ls.run();
list.remove();
} else {
ls.create();
}
}
Thanks
Why don't you think you can use an iterator?
Iterator<MyList> i = list.iterator();
while (i.hasNext()) {
MyList ls = i.next();
//... all your other code which uses ls...
i.remove();
}
This approach is also likely to be faster, since using iterator.remove() avoids having to search for the item in the list which is necessary with list.remove(item).
You can use an iterator, but only by abandoning the enhanced for loop:
for (Iterator<MyList> iterator = list.iterator(); iterator.hasNext(); ) {
MyList ls = iterator.next();
if (ls.getStatus() == 0) {
lo.run(zo);
iterator.remove();
} else {
ls.create();
}
}
Of course, that assumes that list refers to a type which supports the remove operation.
In case your iterator does not support remove operation you could use following algorithm:
In first step you can iterate over list creating list of indices of elements to be deleted. Next step would be iterating over list of indices backward and deleting elements by index.

Using iterator on a TreeSet

SITUATION: I have a TreeSet of custom Objects and I have also used a custom Comparator. I have created an iterator to use on this TreeSet.
TreeSet<Custom> ts=new TreeSet<Custom>();
Iterator<Custom> itr=ts.iterator();
while(itr.hasNext()){
Custom c=itr.next();
//Code to add a new element to the TreeSet ts
}
QUESTION: Well I want to know that if I add a new element to the TreeSet within the while loop, then will that new element get sorted immediately. In other words, if I add a new element within the while loop and it is less than the one which I am currently holding in c, then in the next iteration will I be getting the same element in c as in the last iteration?(since after sorting, the newly added element will occupy a place somewhere before the current element).
If you add an element during your iteration, your next iterator call will likely throw a ConcurrentModificationException. See the fail-fast behavior in TreeSet docs.
To iterate and add elements, you could copy first to another set:
TreeSet<Custom> ts = ...
TreeSet<Custom> tsWithExtra = new TreeSet(ts);
for (Custom c : ts) {
// possibly add to tsWithExtra
}
// continue, using tsWithExtra
or create a separate collection to be merged with ts after iteration, as Colin suggests.
You will get a java.util.ConcurrentModificationException if you add an element into the TreeSet inside while loop.
Set<String> ts = new TreeSet<>();
ts.addAll(Arrays.asList(new String[]{"abb", "abd", "abg"}));
Iterator<String> itr = ts.iterator();
while(itr.hasNext()){
String s = itr.next();
System.out.println("s: " + s);
if (s.equals("abd"))
ts.add("abc");
}
###Output
Exception in thread "main" java.util.ConcurrentModificationException
public static void main(String[] args) {
TreeSet<Integer> ts=new TreeSet<Integer>();
ts.add(2);
ts.add(4);
ts.add(0);
Iterator<Integer> itr=ts.iterator();
while(itr.hasNext()){
Integer c=itr.next();
System.out.println(c);
//Code
ts.add(1);
}
}
Exception in thread "main" java.util.ConcurrentModificationException
This will come to all collections like List , Map , Set
Because when iterator starts it may be putting some lock on it .
if you iterate list using iterator then this exception will come. I think otherwise this loop will be infinite as you are adding element whole iterating.
Consider without iterator:
public static void main(String[] args) {
List<Integer> list=new ArrayList<Integer>();
list.add(2);
list.add(4);
list.add(0);
for (int i = 0; i < 3; i++) {
System.out.println(list.get(i));
list.add(3);
}
System.out.println("Size" +list.size());
}
this will be fine .
In order to avoid the ConcurrentModificationException you might want to check out my UpdateableTreeSet. I have even added a new test case showing how to add elements during a loop. To be more exact, you mark new elements for a later, deferred update of the set. This works quite nicely. Basically you do something like
for (MyComparableElement element : myUpdateableTreeSet) {
if (someCondition) {
// Add new element (deferred)
myUpdateableTreeSet.markForUpdate(
new MyComparableElement("foo", "bar", 1, 2)
);
}
}
// Perform bulk update
myUpdateableTreeSet.updateMarked();
I guess this is quite exactly what you need. :-)
To prevent the ConcurrentModificationException while walking.
Below is my version to allow high frequency insertion into the TreeSet() and allow concurrently iterate on it. This class use a extra queue to store the inserting object when the TreeSet is being iterating.
public class UpdatableTransactionSet {
TreeSet <DepKey> transactions = new TreeSet <DepKey> ();
LinkedList <DepKey> queue = new LinkedList <DepKey> ();
boolean busy=false;
/**
* directly call it
* #param e
*/
void add(DepKey e) {
boolean bb = getLock();
if(bb) {
transactions.add(e);
freeLock();
} else {
synchronized(queue) {
queue.add(e);
}
}
}
/**
* must getLock() and freeLock() while call this getIterator function
* #return
*/
Iterator<DepKey> getIterator() {
return null;
}
synchronized boolean getLock() {
if(busy) return false;
busy = true;
return true;
}
synchronized void freeLock() {
synchronized(queue) {
for(DepKey e:queue) {
transactions.add(e);
}
}
busy = false;
}
}
While the question has already been answered, I think the most satisfactory answer lies in javadoc of TreeSet itself
The iterators returned by this class's iterator method are fail-fast: if the set is modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, >generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
To avoid the concurrent modification error that's bound to occur when you're doing the insertion, you could also create a temporary copy of the Set, iterate through the copy instead, and modify the original.

Java: Alternative to iterator.hasNext() if using for-each to loop over a collection

I'm trying to replace an iterator-based loop over a Java list with a for-each statement, but the code uses at some point iterator.hasNext() to check if it reached the last element in the list.
Is there something similar for the for-each alternative?
for (Object current : objectList) {
if (last-element)
do-something-special
}
for-each is just syntactic sugar for iterator version and if you check compiled bytecode, then you'll notice that compilator actually change it into iterator version.
With a for-each form you can't check whether you'll have more elements or not.
Just stay with explicit iterator use if you need that feature.
In addition to Luno's answer:
Iterator<MyClass> it = myCollection.iterator();
while(it.hasNext()) {
MyClass myClass = it.next():
// do something with myClass
}
translates to:
for (MyClass myClass:myCollection) {
// do something with myClass
}
As others have said - this isn't possible.
Just remember that the foreach construct isn't the be-all and end-all. It was introduced to make the very common task of performing the same operations on each element of a collection simpler to denote.
In your case, you don't want to do exactly the same thing to each element - and thus a foreach loop is not the right tool for the job. Trying to "hack" it into doing this is silly, just use an explicit iterator in a classic for loop.
The foreach loop (or enhanced for loop) does not have facilities to keep track of which element is being iterated on at the moment. There is no way to find out which index of a Collection is being worked on, or whether there are more elements to be processed in an Iterable.
That said, one workaround that would work is to keep a reference to the object which is being iterated on at the moment in the foreach loop.
By keeping a reference of what it being worked on at the current iteration, one would be able to keep the reference once the foreach loop ends, and what is left in the variable will be the last element.
This workaround will only work if-and-only-if the last element is the only element which is needed.
For example:
String lastString = null;
for (String s : new String[] {"a", "b", "c"}) {
// Do something.
// Keep the reference to the current object being iterated on.
lastString = s;
}
System.out.println(lastString);
Output:
c
Unfortunately, the for each idiom does not allow you to check if an element is first or last in the list. This is a known limitation of the for each loop.
I suggest you just keep using the iterator.
If you can also check for the first element instead of the last one, for example if you're doing String concatenation, you could change to something like:
boolean first = true;
for (Element e : list) {
if (!first) {
//do optional operation
}
//do other stuff
first = false;
}
but I would prefer using the iterator.
If you want to stay with for-each maybe something like this:
if (objectList.indexOf(current)==objectList.size()-1) break;
int nElts = objectList.size();
int n = 0;
for (...) {
if (++n == nElts) ...
is the best I can think of.
There are two possible cases where you would like to do this.
You need to do something after the last element has been reached: in this case you just need to put your code outside of the loop.
for(Object item:theLinkedList){
}
System.out.println("something special")
you need to modify the last element in some way or use information related to the last element. In this case you should use the **LinkedList** to access the last element
for(Object item:theLinkedList){
}
Object last=theLinkedList.getLast();
System.out.println("last");
yes you can, here's how i would do it if you don't want to use the explicit iterator syntax:
for (Object current : objectList) {
if (objectList.getLast().equals(current))
do-something-special
}
In Addition to bayer you have to do it a bit different because there is no method getLast(). But instead of it you can use this objectList.size() - 1.
for (Object current : objectList) {
if (objectList.get(objectList.size() - 1).equals(current))
do-something-special
}
Just save loop repeat count, sample :
int loop = 0;
for(Item item : items) {
if(loop == 0) {
//Is First Item
}
if(loop != items.size()-1) {
//Has Next Item
}
if(loop == items.size()-1) {
//Is Last Item
}
//Must Be Last Statement
loop++;
}
It's similar to for(int i = 0; i < items.size(); i++) loop;
items.size() is used for lists and items.length for arrays;

Categories

Resources