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

发布于 2024-09-17 12:17:44 字数 554 浏览 5 评论 0 原文

我想编写一个方法,从集合中删除遵循特定模式的所有元素。在函数式语言中,我会将 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 收藏集的情况下实现此目的?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

疯了 2024-09-24 12:18:29

对于那些遇到这个线程并且可能正在使用 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。如果您不使用 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();

For those of you who come across this thread and might be working on Android with RxJava/RxAndroid, there's a quick way to do this without adding the Apache Commons Collections dependency:

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

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

Note that I also happen to be using lambda expressions with Retrolambda. If you aren't using Retrolambda, you can express the same thing using the following:

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();
紅太極 2024-09-24 12:18:26

随着Java8引入lambda表达式,实现过滤变得更加容易以更实用的方式构建集合。

我给你写了一个例子。另请注意使用 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
}

With Java8 introducing lambda expressions, it's much easier to implement filtering on a collection in a more functional approach.

I wrote an example for you. Please also note how nicer it is to print Collection content using forEach:

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
}
樱娆 2024-09-24 12:18:23
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();
    }
}

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

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

Pass a list of some generic type to this function with a predicate to remove unwanted elements.

旧夏天 2024-09-24 12:18:19

这确实是一篇旧文章,但是如何使用 Oracle Java 教程中给出的方法。

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

It's really an old post but how abt using the way given in Oracle Java tutorial.

static void filter(Collection<?>c) {
    for (Iterator<?>it = c.iterator(); it.hasNext(); )
         if (!cond(it.next()))
             it.remove();
}
榕城若虚 2024-09-24 12:18:16

我非常喜欢 Vivien Barousse 和 gpeche 提供的迭代器解决方案。但我想指出的是,您不必实际从集合中删除任何元素,只需防止过滤器返回它们即可。这样,您基本上就可以拥有同一集合的多个视图,这非常方便和高效。 Filter 对象基本上是您的 lamda 表达式,或者与 Java 版本 7 之前的最接近的表达式...

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...

深居我梦 2024-09-24 12:18:13

您总是可以向后退并删除元素。

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

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

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.

土豪 2024-09-24 12:18:11

了解 lambdaj 的过滤选项是否可以为您提供帮助。

See if lambdaj's filter option can help you.

软甜啾 2024-09-24 12:18:08

以下是 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();
            }
        }
    }
}

Here is the Android way to implement a generic solution for this:

Usage:

Remove all null strings from my list

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

Source:

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();
            }
        }
    }
}
吃→可爱长大的 2024-09-24 12:18:04

您可以使用 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; }
});

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; }
});
大海や 2024-09-24 12:18:01

您可以使用 ListIterator,它有一个 删除 方法。

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

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.

羞稚 2024-09-24 12:17:58

也许你可以使用迭代器,它的效率更高一些:

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

});

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

});
我不是你的备胎 2024-09-24 12:17:54

使用 Java 8,您可以使用 Collection.removeIf

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

With Java 8, you can filter with a lambda expression using Collection.removeIf.

cars.removeIf(c -> c.getCarColor() == Color.BLUE);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文