首页 文章

过滤Java Collection的最佳方法是什么?

提问于
浏览
585

我想基于谓词过滤 java.util.Collection .

27 回答

  • 2

    简单的Java8之前的解决方案:

    ArrayList<Item> filtered = new ArrayList<Item>(); 
    for (Item item : items) if (condition(item)) filtered.add(item);
    

    不幸的是,这个解决方案不是完全通用的,输出列表而不是给定集合的类型 . 此外,引入库或编写包含此代码的函数对我来说似乎有些过分,除非条件很复杂,但是您可以为条件编写函数 .

  • 2

    我会在环中抛出RxJava,这也可以在Android上找到 . RxJava可能并不总是最佳选择,但如果您希望在集合中添加更多转换或在过滤时处理错误,它将为您提供更大的灵活性 .

    Observable.from(Arrays.asList(1, 2, 3, 4, 5))
        .filter(new Func1<Integer, Boolean>() {
            public Boolean call(Integer i) {
                return i % 2 != 0;
            }
        })
        .subscribe(new Action1<Integer>() {
            public void call(Integer i) {
                System.out.println(i);
            }
        });
    

    输出:

    1
    3
    5
    

    有关RxJava的 filter 的更多详细信息,请参见here .

  • 5

    假设您正在使用Java 1.5,并且您无法添加Google Collections,我会做一些与Google员工非常相似的事情 . 这是Jon的评论略有不同 .

    首先将此接口添加到您的代码库 .

    public interface IPredicate<T> { boolean apply(T type); }
    

    当某个谓词对某种类型成立时,它的实现者可以回答它 . 例如 . 如果 T UserAuthorizedUserPredicate<User> 实现了 IPredicate<T> ,那么 AuthorizedUserPredicate#apply 将返回 User 中的传入是否被授权 .

    然后在一些实用类中,你可以说

    public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
        Collection<T> result = new ArrayList<T>();
        for (T element: target) {
            if (predicate.apply(element)) {
                result.add(element);
            }
        }
        return result;
    }
    

    所以,假设你已经使用了以上可能

    Predicate<User> isAuthorized = new Predicate<User>() {
        public boolean apply(User user) {
            // binds a boolean method in User to a reference
            return user.isAuthorized();
        }
    };
    // allUsers is a Collection<User>
    Collection<User> authorizedUsers = filter(allUsers, isAuthorized);
    

    如果关注线性检查的性能,那么我可能希望拥有一个具有目标集合的域对象 . 具有目标集合的域对象将具有用于初始化,添加和设置目标集合的方法的过滤逻辑 .

    更新:

    在实用程序类(比如谓词)中,我添加了一个select方法,当谓词没有返回预期值时,默认值选项,以及在新IPredicate中使用的params的静态属性 .

    public class Predicate {
        public static Object predicateParams;
    
        public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
            Collection<T> result = new ArrayList<T>();
            for (T element : target) {
                if (predicate.apply(element)) {
                    result.add(element);
                }
            }
            return result;
        }
    
        public static <T> T select(Collection<T> target, IPredicate<T> predicate) {
            T result = null;
            for (T element : target) {
                if (!predicate.apply(element))
                    continue;
                result = element;
                break;
            }
            return result;
        }
    
        public static <T> T select(Collection<T> target, IPredicate<T> predicate, T defaultValue) {
            T result = defaultValue;
            for (T element : target) {
                if (!predicate.apply(element))
                    continue;
                result = element;
                break;
            }
            return result;
        }
    }
    

    以下示例查找集合之间缺少的对象:

    List<MyTypeA> missingObjects = (List<MyTypeA>) Predicate.filter(myCollectionOfA,
        new IPredicate<MyTypeA>() {
            public boolean apply(MyTypeA objectOfA) {
                Predicate.predicateParams = objectOfA.getName();
                return Predicate.select(myCollectionB, new IPredicate<MyTypeB>() {
                    public boolean apply(MyTypeB objectOfB) {
                        return objectOfB.getName().equals(Predicate.predicateParams.toString());
                    }
                }) == null;
            }
        });
    

    以下示例在集合中查找实例,并在未找到实例时将集合的第一个元素作为默认值返回:

    MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
    public boolean apply(MyType objectOfMyType) {
        return objectOfMyType.isDefault();
    }}, collectionOfMyType.get(0));
    

    更新(在Java 8发布之后):

    我(艾伦)第一次发布这个答案已经好几年了,我仍然无法相信我正在为这个答案收集SO分 . 无论如何,现在Java 8已经引入了语言的闭包,我的答案现在会有很大的不同,而且更简单 . 使用Java 8,不需要一个独特的静态实用程序类 . 所以如果你想找到与你的谓词匹配的第一个元素 .

    final UserService userService = ... // perhaps injected IoC
    final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
        boolean isAuthorized = userService.isAuthorized(u);
        return isAuthorized;
    }).findFirst();
    

    可选项的JDK 8 API具有 get() ,_ 117623orElse(defaultUser)orElseGet(userSupplier)orElseThrow(exceptionSupplier) 以及 mapflatMapfilter 等其他'monadic'函数的功能 .

    如果您只想收集与谓词匹配的所有用户,请使用 Collectors 终止所需集合中的流 .

    final UserService userService = ... // perhaps injected IoC
    final List<UserModel> userOption = userCollection.stream().filter(u -> {
        boolean isAuthorized = userService.isAuthorized(u);
        return isAuthorized;
    }).collect(Collectors.toList());
    

    有关Java 8流如何工作的更多示例,请参见here .

  • 3

    JFilter http://code.google.com/p/jfilter/最适合您的要求 .

    JFilter是一个简单而高性能的开源库,用于查询Java bean的集合 .

    主要特点

    • 支持集合(java.util.Collection,java.util.Map和Array)属性 .

    • 支持任何深度的集合内的集合 .

    • 支持内部查询 .

    • 支持参数化查询 .

    • 可以在几百毫秒内过滤100万条记录 .

    • 过滤器(查询)以简单的json格式给出,就像Mangodb查询一样 . 以下是一些例子 .

    • {"id":{"$le":"10"}

    • 其中object id属性小于等于10 .

    • {"id":{"$in":["0","100"]}}

    • 其中object id属性为0或100 .

    • {"lineItems":{"lineAmount":"1"}}

    • 其中参数化类型的lineItems集合属性的lineAmount等于1 .

    • {"$and":[{_ "id":"0"},{"billingAddress":{"city":"DEL"}}]}

    • 其中id属性为0,billingAddress.city属性为DEL .

    • {"lineItems":{"taxes":{"key":{"code":"GST"},"value":{"$gt":"1.01"}}}}

    • 其中参数化类型的lineItems集合属性具有参数化类型的税映射类型属性,其代码等于GST值大于1.01 .

    • {'$or':[{_ 'code':'10'},{'skus':{'$and':[{_ 'price':{_ '$in':['20','40']}},{'code':'RedApple'}]}}]}

    • 选择所有产品代码为10或sku价格为20和40且sku代码为"RedApple"的产品 .

  • 1

    使用来自Apache Commons的CollectionUtils.filter(Collection,Predicate) .

  • 7

    一些简单而直接的Java怎么样?

    List<Customer> list ...;
     List<Customer> newList = new ArrayList<>();
     for (Customer c : list){
        if (c.getName().equals("dd")) newList.add(c);
     }
    

    简单,易读且易于使用(适用于Android!)但是,如果您使用的是Java 8,则可以在一行中完成:

    List<Customer> newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());
    

    请注意,toList()是静态导入的

  • 11

    等待Java 8:

    List<Person> olderThan30 = 
      //Create a Stream from the personList
      personList.stream().
      //filter the element to select only those with age >= 30
      filter(p -> p.age >= 30).
      //put those filtered elements into a new List.
      collect(Collectors.toList());
    
  • 2

    运用 java 8 ,特别是 lambda expression ,你可以像下面的例子那样做:

    myProducts.stream().filter(prod -> prod.price>10).collect(Collectors.toList())
    

    如果 myProducts 集合中的每个 product ,如果 prod.price>10 ,则将此产品添加到新的筛选列表中 .

  • 0
  • 7
    In Java 8, You can directly use this filter method and then do that.
    
    
    
     List<String> lines = Arrays.asList("java", "pramod", "example");
    
            List<String> result = lines.stream()              
                    .filter(line -> !"pramod".equals(line))     
                    .collect(Collectors.toList());              
    
            result.forEach(System.out::println);
    
  • 6

    Java 8(2014)在一行代码中使用流和lambdas解决了这个问题:

    List<Person> beerDrinkers = persons.stream()
        .filter(p -> p.getAge() > 16).collect(Collectors.toList());
    

    这是tutorial .

    使用Collection#removeIf来修改集合 . (注意:在这种情况下,谓词将删除满足谓词的对象):

    persons.removeIf(p -> p.getAge() <= 16);
    

    lambdaj允许过滤集合而无需编写循环或内部类:

    List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
        greaterThan(16)));
    

    你能想象更具可读性的东西吗?

    Disclaimer: 我是lambdaj的贡献者

  • 217

    java 9 Collectors.filtering 启用以来:

    public static <T, A, R>
        Collector<T, ?, R> filtering(Predicate<? super T> predicate,
                                     Collector<? super T, A, R> downstream)
    

    因此过滤应该是:

    collection.stream().collect(Collectors.filtering(predicate, collector))
    

    例:

    List<Integer> oddNumbers = List.of(1, 19, 15, 10, -10).stream()
                .collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));
    
  • 61

    设置:

    public interface Predicate<T> {
      public boolean filter(T t);
    }
    
    void filterCollection(Collection<T> col, Predicate<T> predicate) {
      for (Iterator i = col.iterator(); i.hasNext();) {
        T obj = i.next();
        if (predicate.filter(obj)) {
          i.remove();
        }
      }
    }
    

    用法:

    List<MyObject> myList = ...;
    filterCollection(myList, new Predicate<MyObject>() {
      public boolean filter(MyObject obj) {
        return obj.shouldFilter();
      }
    });
    
  • 24

    使用ForEach DSL,您可以编写

    import static ch.akuhn.util.query.Query.select;
    import static ch.akuhn.util.query.Query.$result;
    import ch.akuhn.util.query.Select;
    
    Collection<String> collection = ...
    
    for (Select<String> each : select(collection)) {
        each.yield = each.value.length() > 3;
    }
    
    Collection<String> result = $result();
    

    给出[The,quick,brown,fox,jumps,over,the,lazy,dog]的集合,这会导致[quick,brown,jumps,over,lazy],即所有字符串超过三个字符 .

    ForEach DSL支持的所有迭代样式都是

    • AllSatisfy

    • AnySatisfy

    • Collect

    • Counnt

    • CutPieces

    • Detect

    • GroupedBy

    • IndexOf

    • InjectInto

    • Reject

    • Select

    有关详细信息,请参阅https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach

  • 61
  • 1

    这里有一些非常好的答案 . 我,我想保持尽可能简单易读:

    public abstract class AbstractFilter<T> {
    
        /**
         * Method that returns whether an item is to be included or not.
         * @param item an item from the given collection.
         * @return true if this item is to be included in the collection, false in case it has to be removed.
         */
        protected abstract boolean excludeItem(T item);
    
        public void filter(Collection<T> collection) {
            if (CollectionUtils.isNotEmpty(collection)) {
                Iterator<T> iterator = collection.iterator();
                while (iterator.hasNext()) {
                    if (excludeItem(iterator.next())) {
                        iterator.remove();
                    }
                }
            }
        }
    }
    
  • 576

    从Java 8的早期版本开始,您可以尝试以下方法:

    Collection<T> collection = ...;
    Stream<T> stream = collection.stream().filter(...);
    

    例如,如果您有一个整数列表,并且想要过滤大于10的数字然后将这些数字打印到控制台,您可以执行以下操作:

    List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
    numbers.stream().filter(n -> n > 10).forEach(System.out::println);
    
  • 5

    请考虑Google Collections以获取支持泛型的更新集合框架 .

    UPDATE :谷歌馆藏库现已弃用 . 您应该使用最新版本的Guava . 它仍然具有集合框架的所有相同扩展,包括基于谓词进行过滤的机制 .

  • 2

    用 Guava :

    Collection<Integer> collection = Lists.newArrayList(1, 2, 3, 4, 5);
    
    Iterators.removeIf(collection.iterator(), new Predicate<Integer>() {
        @Override
        public boolean apply(Integer i) {
            return i % 2 == 0;
        }
    });
    
    System.out.println(collection); // Prints 1, 3, 5
    
  • 7

    让我们看一下如何使用Eclipse Collections(以前的GS Collections)过滤内置JDK列表和MutableList .

    List<Integer> jdkList = Arrays.asList(1, 2, 3, 4, 5);
    MutableList<Integer> ecList = Lists.mutable.with(1, 2, 3, 4, 5);
    

    如果要过滤小于3的数字,您可能会得到以下输出 .

    List<Integer> selected = Lists.mutable.with(1, 2);
    List<Integer> rejected = Lists.mutable.with(3, 4, 5);
    

    以下是使用匿名内部类作为 Predicate 过滤的方法 .

    Predicate<Integer> lessThan3 = new Predicate<Integer>()
    {
        public boolean accept(Integer each)
        {
            return each < 3;
        }
    };
    
    Assert.assertEquals(selected, Iterate.select(jdkList, lessThan3));
    
    Assert.assertEquals(selected, ecList.select(lessThan3));
    

    以下是使用Predicates工厂过滤JDK列表和Eclipse Collections MutableLists的一些替代方法 .

    Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));
    
    Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));
    

    这是一个不为谓词分配对象的版本,使用Predicates2工厂而不是 selectWith 方法,该方法需要 Predicate2 .

    Assert.assertEquals(
        selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));
    

    有时您想要在负面条件下进行过滤 . Eclipse Collections中有一个名为 reject 的特殊方法 .

    Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));
    
    Assert.assertEquals(rejected, ecList.reject(lessThan3));
    

    以下是使用Java 8 lambda作为_177671进行过滤的方法 .

    Assert.assertEquals(selected, Iterate.select(jdkList, each -> each < 3));
    Assert.assertEquals(rejected, Iterate.reject(jdkList, each -> each < 3));
    
    Assert.assertEquals(selected, gscList.select(each -> each < 3));
    Assert.assertEquals(rejected, gscList.reject(each -> each < 3));
    

    方法 partition 将返回两个集合,其中包含由 Predicate 选择和拒绝的元素 .

    PartitionIterable<Integer> jdkPartitioned = Iterate.partition(jdkList, lessThan3);
    Assert.assertEquals(selected, jdkPartitioned.getSelected());
    Assert.assertEquals(rejected, jdkPartitioned.getRejected());
    
    PartitionList<Integer> ecPartitioned = gscList.partition(lessThan3);
    Assert.assertEquals(selected, ecPartitioned.getSelected());
    Assert.assertEquals(rejected, ecPartitioned.getRejected());
    

    注意:我是Eclipse Collections的提交者 .

  • 88

    这与缺乏真正的闭包相结合,是我对Java最大的抱怨 . 老实说,上面提到的大多数方法都很容易阅读,真正有效;然而,在花费时间与.Net,Erlang等之后......在语言层面集成的列表理解使得一切都变得更加清晰 . 如果没有语言级别的补充,Java就不能像这个领域的许多其他语言一样干净 .

    如果性能是一个巨大的问题,谷歌集合是要走的路(或编写自己的简单谓词实用程序) . 对于某些人来说,Lambdaj语法更具可读性,但效率却不高 .

    然后有一个我写的图书馆 . 我会忽略任何有关其效率的问题(是的,它那么糟糕)......是的,我知道它基于清晰的反思,并且我不会实际使用它,但它确实有效:

    LinkedList<Person> list = ......
    LinkedList<Person> filtered = 
               Query.from(list).where(Condition.ensure("age", Op.GTE, 21));
    

    OR

    LinkedList<Person> list = ....
    LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");
    
  • 0

    “最佳”方式是一个太宽泛的要求 . 它是“最短的”吗? “最快的”? “读”?过滤到另一个集合?

    最简单(但不是最可读)的方法是迭代它并使用Iterator.remove()方法:

    Iterator<Foo> it = col.iterator();
    while( it.hasNext() ) {
      Foo foo = it.next();
      if( !condition(foo) ) it.remove();
    }
    

    现在,为了使其更具可读性,您可以将其包装到实用程序方法中 . 然后发明一个IPredicate接口,创建该接口的匿名实现,并执行以下操作:

    CollectionUtils.filterInPlace(col,
      new IPredicate<Foo>(){
        public boolean keepIt(Foo foo) {
          return foo.isBar();
        }
      });
    

    其中filterInPlace()迭代集合并调用Predicate.keepIt()以了解要保存在集合中的实例 .

    我真的没有理由为这项任务引入第三方库 .

  • 3

    我需要根据列表中已存在的值过滤列表 . 例如,删除小于的所有值当前值 . {2 5 3 4 7 5} - > {2 5 7} . 或者例如删除所有重复项{3 5 4 2 3 5 6} - > {3 5 4 2 6} .

    public class Filter {
        public static <T> void List(List<T> list, Chooser<T> chooser) {
            List<Integer> toBeRemoved = new ArrayList<>();
            leftloop:
            for (int right = 1; right < list.size(); ++right) {
                for (int left = 0; left < right; ++left) {
                    if (toBeRemoved.contains(left)) {
                        continue;
                    }
                    Keep keep = chooser.choose(list.get(left), list.get(right));
                    switch (keep) {
                        case LEFT:
                            toBeRemoved.add(right);
                            continue leftloop;
                        case RIGHT:
                            toBeRemoved.add(left);
                            break;
                        case NONE:
                            toBeRemoved.add(left);
                            toBeRemoved.add(right);
                            continue leftloop;
                    }
                }
            }
    
            Collections.sort(toBeRemoved, new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2 - o1;
                }
            });
    
            for (int i : toBeRemoved) {
                if (i >= 0 && i < list.size()) {
                    list.remove(i);
                }
            }
        }
    
        public static <T> void List(List<T> list, Keeper<T> keeper) {
            Iterator<T> iterator = list.iterator();
            while (iterator.hasNext()) {
                if (!keeper.keep(iterator.next())) {
                    iterator.remove();
                }
            }
        }
    
        public interface Keeper<E> {
            boolean keep(E obj);
        }
    
        public interface Chooser<E> {
            Keep choose(E left, E right);
        }
    
        public enum Keep {
            LEFT, RIGHT, BOTH, NONE;
        }
    }
    

    这将像这样使用 .

    List<String> names = new ArrayList<>();
    names.add("Anders");
    names.add("Stefan");
    names.add("Anders");
    Filter.List(names, new Filter.Chooser<String>() {
        @Override
        public Filter.Keep choose(String left, String right) {
            return left.equals(right) ? Filter.Keep.LEFT : Filter.Keep.BOTH;
        }
    });
    
  • 11

    您确定要过滤Collection本身而不是迭代器吗?

    org.apache.commons.collections.iterators.FilterIterator

    或者使用apache commons的第4版org.apache.commons.collections4.iterators.FilterIterator

  • 1

    我写了an extended Iterable class,支持应用功能算法而不复制集合内容 .

    用法:

    List<Integer> myList = new ArrayList<Integer>(){ 1, 2, 3, 4, 5 }
    
    Iterable<Integer> filtered = Iterable.wrap(myList).select(new Predicate1<Integer>()
    {
        public Boolean call(Integer n) throws FunctionalException
        {
            return n % 2 == 0;
        }
    })
    
    for( int n : filtered )
    {
        System.out.println(n);
    }
    

    上面的代码实际上会执行

    for( int n : myList )
    {
        if( n % 2 == 0 ) 
        {
            System.out.println(n);
        }
    }
    
  • 0

    https://code.google.com/p/joquery/

    支持不同的可能性,

    鉴于收藏,

    Collection<Dto> testList = new ArrayList<>();
    

    类型,

    class Dto
    {
        private int id;
        private String text;
    
        public int getId()
        {
            return id;
        }
    
        public int getText()
        {
            return text;
        }
    }
    

    Filter

    Java 7

    Filter<Dto> query = CQ.<Dto>filter(testList)
        .where()
        .property("id").eq().value(1);
    Collection<Dto> filtered = query.list();
    

    Java 8

    Filter<Dto> query = CQ.<Dto>filter(testList)
        .where()
        .property(Dto::getId)
        .eq().value(1);
    Collection<Dto> filtered = query.list();
    

    也,

    Filter<Dto> query = CQ.<Dto>filter()
            .from(testList)
            .where()
            .property(Dto::getId).between().value(1).value(2)
            .and()
            .property(Dto::grtText).in().value(new string[]{"a","b"});
    

    Sorting (也适用于Java 7)

    Filter<Dto> query = CQ.<Dto>filter(testList)
            .orderBy()
            .property(Dto::getId)
            .property(Dto::getName)
        Collection<Dto> sorted = query.list();
    

    Grouping (也适用于Java 7)

    GroupQuery<Integer,Dto> query = CQ.<Dto,Dto>query(testList)
            .group()
            .groupBy(Dto::getId)
        Collection<Grouping<Integer,Dto>> grouped = query.list();
    

    Joins (也适用于Java 7)

    鉴于,

    class LeftDto
    {
        private int id;
        private String text;
    
        public int getId()
        {
            return id;
        }
    
        public int getText()
        {
            return text;
        }
    }
    
    class RightDto
    {
        private int id;
        private int leftId;
        private String text;
    
        public int getId()
        {
            return id;
        }
    
        public int getLeftId()
            {
                return leftId;
            }
    
        public int getText()
        {
            return text;
        }
    }
    
    class JoinedDto
    {
        private int leftId;
        private int rightId;
        private String text;
    
        public JoinedDto(int leftId,int rightId,String text)
        {
            this.leftId = leftId;
            this.rightId = rightId;
            this.text = text;
        }
    
        public int getLeftId()
        {
            return leftId;
        }
    
        public int getRightId()
            {
                return rightId;
            }
    
        public int getText()
        {
            return text;
        }
    }
    
    Collection<LeftDto> leftList = new ArrayList<>();
    
    Collection<RightDto> rightList = new ArrayList<>();
    

    可以加入像,

    Collection<JoinedDto> results = CQ.<LeftDto, LeftDto>query().from(leftList)
                    .<RightDto, JoinedDto>innerJoin(CQ.<RightDto, RightDto>query().from(rightList))
                    .on(LeftFyo::getId, RightDto::getLeftId)
                    .transformDirect(selection ->  new JoinedDto(selection.getLeft().getText()
                                                         , selection.getLeft().getId()
                                                         , selection.getRight().getId())
                                     )
                    .list();
    

    Expressions

    Filter<Dto> query = CQ.<Dto>filter()
        .from(testList)
        .where()
        .exec(s -> s.getId() + 1).eq().value(2);
    
  • 1

    我的答案 Build 在Kevin Wong的基础之上,这里是一个使用 CollectionUtils 从 Spring 天开始的单线程和一个Java 8 lambda表达式 .

    CollectionUtils.filter(list, p -> ((Person) p).getAge() > 16);
    

    这与我见过的任何替代方案一样简洁易读(不使用基于方面的库)

    Spring CollectionUtils可从Spring版本4.0.2.RELEASE获得,并且记住您需要JDK 1.8和语言级别8 .

相关问题