views:

133

answers:

6

I want to write a method that removes all elements from a collection that follow a certain pattern. In functional languages, I would use filter() with a lambda expression. However, in Java, it seems I'm stuck with this:

public void removeAllBlueCars() {
    LinkedList<Car> carsToRemove = new LinkedList<Car>();
    for (Car c : cars) {
        if (c.getCarColor() == Color.BLUE) {
            carsToRemove.add(c);
        }
    }
    cars.removeAll(carsToRemove );
}

Removing elements directly causes a ConcurrentModificationException. Is there a better way to do this without resorting to Google Collections?

+6  A: 

You could iterate through the list using a ListIterator, which has a remove method.

Btw you should declare your list as List<Car> - program for interfaces, not implementation.

Péter Török
`Iterator` already has a `remove` method.
Steve Kuo
@Steve, correct. At first quick reading, I perceived that `ListIterator.remove` offers additional guarantees over the base interface's method, but at second check I see that I misunderstood. So you are right, an `Iterator` would suffice.
Péter Török
+1  A: 

See if lambdaj's filter option can help you.

Alberto Zaccagni
+2  A: 

Maybe you could use iterators, which are a little more efficient:

public void removeAllBlueCars() {
    Iterator<Car> carsIterator = cars.iterator();
    while (carsIterator.hasNext()) {
        Car c = carsIterator.next();
        if (c.getCarColor() == Color.BLUE) {
            carsIterator.remove();
        }
    }
}

Also, if you want to make this solution more generic, I'd suggest you something like:

public interface Filter<T> {

    public boolean shouldRemove(T t);

}

And you could use it like this:

public void removeCars(Filter<Car> filter) {
    Iterator<Car> carsIterator = cars.iterator();
    while (carsIterator.hasNext()) {
        Car c = carsIterator.next();
        if (filter.shouldRemove(c)) {
            carsIterator.remove();
        }
    }
}

Your method gets called like this:

removeCars(new Filter<Car>() {

    public boolean shouldRemove(Car car) {
        return car.getCarColor() == Color.BLUE;
    }

});
Vivien Barousse
+2  A: 

You could always go backwards and delete the elements..

    for (int i = array.size() - 1; i >= 0; i--) {
       if (array.get(i).getCarColor() == Color.BLUE)
                array.remove(i);
    }

edit: Noticed it was a LinkedList which might make my answer a bit non-relevant.

Marcus Johansson
No, A linkedlist can have indexes and anything. your solution is correct and short
vodkhang
@vodkhang, but its performance is terrible for big lists - O(n^2).
Péter Török
Ah yeah, I now noticed that it is O(n^2) because the remove method takes another O(n), and the loop is O(n) already.
vodkhang
That was what i was thinking. :)It is (n²) for both LinkedLists and ArrayLists, but if you just want short easy-to-read code, this might be a "good" choice.
Marcus Johansson
Using the iterator seems the right thing to do without resorting to libraries. Thanks!
Frederik
+1  A: 

You can use CollectionUtils.filter(). It works with an Iterator, so it should have no problems removing items directly from the Collection. It is another dependency though. If you want the code standalone it would be:

public interface Predicate {
    boolean evaluate(Object o);
}

public static void filter(Collection collection, Predicate predicate) {
if ((collection != null) && (predicate != null))
    for (Iterator it = collection.iterator(); it.hasNext(); )
        if (!predicate.evaluate(it.next()))
            it.remove();
}
...
filter(collection, new Predicate() {
    public boolean evaluate(Object o) { return whatever; }
});
gpeche
+1  A: 

I'm a big fan of the Iterator solution provided by Vivien Barousse and gpeche. But I wanted to point out that you don't have to actually remove any elements from the collection, you just need to prevent the filter from returning them. That way, you basically have multiple views of the same collection, which can be very convenient and efficient. The Filter object is basically your lamda expression, or as close as you're gonna get in Java until version 7...

romacafe