在Java中过滤元素的简单方法?

时间:2010-09-07 09:39:52

标签: java collections

我想编写一个方法,删除遵循特定模式的集合中的所有元素。在函数式语言中,我会将 filter()与lambda表达式一起使用。但是,在Java中,似乎我坚持这个:

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

直接删除元素会导致ConcurrentModificationException。有没有更好的方法来做到这一点而不诉诸Google Collections

12 个答案:

答案 0 :(得分:16)

使用Java 8,您可以使用Collection.removeIf来过滤lambda表达式。

cars.removeIf(c -> c.getCarColor() == Color.BLUE);

答案 1 :(得分:13)

您可以使用ListIterator方法遍历列表,其中remove方法。

顺便说一句,你应该将你的列表声明为List<Car> - 接口程序,而不是实现。

答案 2 :(得分:13)

也许你可以使用更高效的迭代器:

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

另外,如果你想让这个解决方案更通用,我建议你这样:

public interface Filter<T> {

    public boolean shouldRemove(T t);

}

你可以像这样使用它:

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

你的方法被调用如下:

removeCars(new Filter<Car>() {

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

});

答案 3 :(得分:4)

您可以使用CollectionUtils.filter()。它适用于Iterator,因此直接从Collection删除项目应该没有任何问题。这是另一个依赖。如果您希望代码独立,那就是:

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; }
});

答案 4 :(得分:3)

查看lambdaj的过滤器选项是否可以为您提供帮助。

答案 5 :(得分:2)

你总是可以倒退并删除元素..

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

编辑:注意到它是一个LinkedList,可能使我的回答有点不相关。

答案 6 :(得分:2)

我是Vivien Barousse提供的Iterator解决方案的忠实粉丝。但是我想指出你不必从集合中删除任何元素,只需要阻止过滤器返回它们。这样,您基本上可以拥有相同集合的多个视图,这可以非常方便和高效。 Filter对象基本上就是你的lamda表达式,或者就像你将要用Java一样接近版本7 ...

答案 7 :(得分:2)

它确实是一个老帖子,但是如何使用Oracle Java教程中给出的方式。

static void filter(Collection<?>c) {
    for (Iterator<?>it = c.iterator(); it.hasNext(); )
         if (!cond(it.next()))
             it.remove();
}

答案 8 :(得分:2)

以下是实现此通用解决方案的Android方法:

用法:

从列表中删除所有空字符串

    LinkedList<String> list = ...
    ListUtils.filter(list, new ListUtils.Filter<String>() {
        @Override
        public boolean keepItem(String item) {
            return item != null;
        }
    });

来源:

public class ListUtils {
    public interface Filter<T>{
        boolean keepItem(T item);
    }

    public static <T> void filter(@NonNull List<T> items, @NonNull Filter<T> filter) {
        for (Iterator<T> iterator = items.iterator(); iterator.hasNext();){
            if(!filter.keepItem(iterator.next())){
                iterator.remove();
            }
        }
    }
}

答案 9 :(得分:1)

使用Java8引入lambda expressions,以更实用的方法实现对集合的过滤要容易得多。

我为你写了一个例子。另请注意使用forEach打印Collection内容有多好:

public class Java8Filtering {

    public static void main(String[] argc) {
        LinkedList<Car> cars = new LinkedList<>();
        cars.add(new Car("car 1", Color.BLUE));
        cars.add(new Car("car 2", Color.GREEN));
        cars.add(new Car("car 3", Color.RED));
        cars.add(new Car("car 4", Color.BLUE));

        List<Car> filteredCars = cars.stream()
                .filter(car -> car.getCarColor() != Color.BLUE)
                .collect(Collectors.toList());

        filteredCars.forEach(car -> System.out.println("Car: " + car.getCarName() + " with color: " + car.getCarColor()));
    }
}

class Car {

    private Color carColor;
    private String carName;

    public Car(String carName, Color carColor) {
        this.carName = carName;
        this.carColor = carColor;
    }

    public Color getCarColor() {
        return carColor;
    }

    public void setCarColor(Color carColor) {
        this.carColor = carColor;
    }

    public String getCarName() {
        return carName;
    }

    public void setCarName(String carName) {
        this.carName = carName;
    }
}

enum Color {
    BLUE, GREEN, RED
}

答案 10 :(得分:0)

对于那些遇到过这个主题并且可能使用RxJava / RxAndroid在Android上工作的人,可以快速完成此操作,而无需添加Apache Commons Collections依赖项:

cars = Observable.from(cars).filter(car -> {
  if (car.getCarColor() == Color.BLUE) {
    return false;
  }

  return true;
}).toList().toBlocking().first();

请注意,我也恰好使用带有Retrolambda的lambda表达式。如果您没有使用Retrolambda,您可以使用以下内容表达相同的内容:

cars = Observable.from(cars).filter(new Func1<Car, Boolean>() {
      @Override
      public Boolean call(Car car) {
        if (car.getCarColor() == Color.BLUE) {
          return false;
        }

        return true;
      }
}).toList().toBlocking().first();

答案 11 :(得分:0)

public static <T> void filter(List<T> list, Predicate<? super T> removeIf) {
    if(list == null) return;

    Iterator<T> iterator = list.iterator();
    while (iterator.hasNext()) {
        if (removeIf.apply(iterator.next())) iterator.remove();
    }
}

使用谓词将一些泛型类型的列表传递给此函数,以删除不需要的元素。