【问题标题】:Finding all objects that have a given property inside a collection [duplicate]查找集合中具有给定属性的所有对象[重复]
【发布时间】:2010-10-09 21:36:08
【问题描述】:

我有一些复杂的对象,比如猫,它有很多属性,比如年龄、最喜欢的猫食等等。

一堆猫存储在一个 Java 集合中,我需要找到所有 3 岁的猫,或者那些最喜欢猫食是 Whiskas 的猫。当然,我可以编写一个自定义方法来查找具有特定属性的那些猫,但是这对于许多属性来说变得很麻烦;有一些通用的方法吗?

【问题讨论】:

  • 我有同样的stackoverflow问题,最终找到了自己的解决方案,不确定它是否100%成熟,还没有准备好生产,但你可以看看:code.google.com/p/tablej
  • @Shaman,尝试使用谷歌收藏
  • 天哪!数据库和 LINQ 的东西就够了。有时您只有一个内存集合,并且想要定位一两个对象。

标签: java collections


【解决方案1】:

试试公共集合 API:

List<Cat> bigList = ....; // master list

Collection<Cat> smallList = CollectionUtils.select(bigList, new Predicate() {
    public boolean evaluate(Object o) {
        Cat c = (Cat)o;
        return c.getFavoriteFood().equals("Wiskas") 
            && c.getWhateverElse().equals(Something);
    }
});

当然,您不必每次都使用匿名类,您可以为常用搜索创建Predicate 接口的实现。

【讨论】:

【解决方案2】:

使用 Java 8 lambda 表达式,您可以执行类似的操作

cats.stream()
    .filter( c -> c.getAge() == 3 && c.getFavoriteFood() == WHISKAS )
    .collect(Collectors.toList());

概念上与 Guava Predicate 方法相同,但使用 lambda 看起来更简洁

对于 OP 可能不是一个有效的答案,但对于有类似需求的人来说值得注意。 :)

【讨论】:

  • 如果您使用的是 Java 8,我喜欢这种方法。您可以使用 Groovy 做类似的事情。
【解决方案3】:

我一直在使用 Google Collections(现在称为 Guava)来解决这类问题。有一个名为 Iterables 的类,它可以将一个名为 Predicate 的接口作为方法的参数,这真的很有帮助。

Cat theOne = Iterables.find(cats, new Predicate<Cat>() {
    public boolean apply(Cat arg) { return arg.age() == 3; }
});

查看here

【讨论】:

    【解决方案4】:

    您可以编写一个方法,该方法接受一个定义check(Cat) 方法的接口实例,该方法可以通过您想要的任何属性检查来实现。

    更好的是,让它通用:

    public interface Checker<T> {
        public boolean check(T obj);
    }
    
    public class CatChecker implements Checker<Cat> {
        public boolean check(Cat cat) {
            return (cat.age == 3); // or whatever, implement your comparison here
        }
    }
    
    // put this in some class
    public static <T> Collection<T> findAll(Collection<T> coll, Checker<T> chk) {
        LinkedList<T> l = new LinkedList<T>();
        for (T obj : coll) {
             if (chk.check(obj))
                 l.add(obj);
        }
        return l;
    }
    

    当然,就像其他人所说的,这就是关系数据库的用途......

    【讨论】:

    • 我也会这么说,但建议使用 Comparator 而不是自定义界面。
    • 我没有使用 Comparator 的原因是 Comparator 应该对集合进行总排序,而我们只需要对某些条件进行布尔测试。被检查的属性可能没有任何有意义的顺序。
    • 这是怎么回事?没有人听说过 Commons Collections、Google Collections 或 Hamcrest collections 吗?我不敢相信这有这么多赞成或被接受。
    • (1) 很高兴知道它是如何实现的 (2) 通常不值得仅仅为了这样一个简单的方法而引入整个额外的库。
    • @David:参数(2)+1。看过一千次:为一个 20 行的单个类导入额外的 lib、额外的 jar、额外的 repo。保持分寸!
    【解决方案5】:

    我建议使用Jxpath,它允许您对对象图进行查询,就好像它在 xpath 中一样

    JXPathContext.newContext(cats).
         getValue("//*[@drinks='milk']")
    

    【讨论】:

    • +1 有趣(尽管 XPath 不是最容易学会有效使用的东西)
    • 嗯,我喜欢将 XPath 与 XML 结合使用,但这段代码闻起来很奇怪。我不想仅仅为了过滤列表而丢失静态类型!
    【解决方案6】:

    再次使用公共集合 API: 当您单独实现谓词时,您会得到“检查器”类型代码:-

    public class CatPredicate implements Predicate {
    
        private int age; 
    
    
        public CatPredicate(int age) {
            super();
            this.age = age;
        }
    
    
        @Override
        public boolean evaluate(Object o) {
            Cat c (Cat)o;
            return c.getAge()==this.age;
        }
    
    }
    

    它被用作:-

    CollectionUtils.filter(catCollectionToFilter, new CatPredicate(3))
    

    【讨论】:

    • 我最终使用了这种方法。我希望我可以将 Predicate 定义为匿名类,但我无法将过滤器值(本例中的年龄)传递给该类,因为匿名类不能有构造函数。
    【解决方案7】:

    您可以使用lambdaj。像这样的事情是微不足道的,语法真的很流畅:

    Person me = new Person("Mario", "Fusco", 35);
    Person luca = new Person("Luca", "Marrocco", 29);
    Person biagio = new Person("Biagio", "Beatrice", 39);
    Person celestino = new Person("Celestino", "Bellone", 29);
    List<Person> meAndMyFriends = asList(me, luca, biagio, celestino);
    List<Person> oldFriends = filter(having(on(Person.class).getAge(), greaterThan(30)), meAndMyFriends);
    

    你可以做更复杂的事情。它使用hamcrest 作为匹配器。有些人会争辩说这不是 Java 风格的,但有趣的是,这个家伙如何扭曲 Java 以进行一些函数式编程。也看看源代码,它很科幻。

    【讨论】:

    • 对于所需的导入,样本通常更有用。尤其是在使用静态导入时。
    • 它来自 Lambda 类或 Matchers 类,如所写。我认为在不查看文档的情况下复制粘贴代码并不是一个好主意,所以浏览一下文档不会有什么坏处。
    【解决方案8】:

    使用共享集合:

    EqualPredicate nameEqlPredicate = new EqualPredicate(3);
    BeanPredicate beanPredicate = new BeanPredicate("age", nameEqlPredicate);
    return CollectionUtils.filter(cats, beanPredicate);
    

    【讨论】:

      【解决方案9】:

      仅供参考,这个问题还有 3 个使用番石榴的其他答案,但没有一个回答这个问题。提问者说他​​希望找到所有具有匹配属性的猫,例如3 岁。Iterables.find 将只匹配一个,如果存在的话。如果使用 Guava,则需要使用 Iterables.filter 来实现此目的,例如:

      Iterable<Cat> matches = Iterables.filter(cats, new Predicate<Cat>() {
          @Override
          public boolean apply(Cat input) {
              return input.getAge() == 3;
          }
      });
      

      【讨论】:

        【解决方案10】:

        听起来很像在 .NET 中使用 LINQ 的东西

        虽然还没有针对 java 的“真正的”LINQ 实现,但您可能想看看Quaere,它可以很容易地完成您所描述的事情。

        【讨论】:

          【解决方案11】:

          您可以使用 JoSQL 之类的东西,并针对您的集合编写“SQL”: http://josql.sourceforge.net/

          这听起来像您想要的,另外还有一个好处是能够执行更复杂的查询。

          【讨论】:

            【解决方案12】:

            您可以尝试 Apache Commons 项目中的一些通用代码。 Collections 子项目提供了用于查找与特定谓词以及大量谓词(equals、null、instanceof 等)匹配的对象的代码。 BeanUtils 子项目允许您创建谓词来测试 bean 的属性。

            使用CollectionUtils 类在集合中搜索。有一些方法可以做到这一点,但请特别检查 select() 方法。 使用以下类来构造谓词,或者自己编写:PredicatePredicateUtilsBeanPredicate

            这有时有点麻烦,但至少它是通用的! :-)

            【讨论】:

            • 这很整洁。它不像我会回答的 jxpath 那样紧凑,但我喜欢你可以以编程方式构建东西。
            【解决方案13】:

            基于谓词、过滤器和自定义迭代器/比较器的解决方案很不错,但它们不提供数据库索引的类比。例如:我想以不同的方式搜索猫的集合:按性别和年龄以及按年龄,所以它看起来像两个索引: 1) [性别、年龄] 2) [年龄]。 通过该索引访问的值可以被散列,以实现快速查找,而无需迭代整个集合。哪里有这样的解决方案?

            【讨论】:

            【解决方案14】:

            使用谷歌番石榴。

            final int lastSeq = myCollections.size();
            Clazz target = Iterables.find(myCollections, new Predicate<Clazz>() {
                @Override
                public boolean apply(@Nullable Clazz input) {
                  return input.getSeq() == lastSeq;
                }
            });
            

            我想用这个方法。

            【讨论】:

            • 我认为已经有至少 2 个答案采用相同的方法(使用 Guava Iterables + Predicate)......这个答案有什么特别之处,其他答案没有涵盖吗?
            【解决方案15】:

            您可以将这些对象存储在数据库中。如果您不想要一个完整的数据库服务器的开销,您可以使用像 HSQLDB 这样的嵌入式服务器。然后可以使用 Hibernate 或 BeanKeeper(使用更简单)或其他 ORM 将对象映射到表。您继续使用 OO 模型并从数据库中获得高级存储和查询优势。

            【讨论】:

              【解决方案16】:

              这是一个搜索器类的想法,您可以使用要查找的特定值进行参数化。

              您还可以进一步存储属性的名称,可能在具有所需值的 Map 中。在这种情况下,您将使用 Cat 类的反射来调用给定属性名称的适当方法。

              public class CatSearcher {
              
                private Integer ageToFind = null;
                private String  foodToFind = null;
              
                public CatSearcher( Integer age, String food ) {
                  this.ageToFind = age;
                  this.foodToFind = food;
                }
              
                private boolean isMatch(Cat cat) {
                  if ( this.ageToFind != null && !cat.getAge().equals(this.ageToFind) ) {
                     return false;
                  }
                  if ( this.foodToFind != null && !cat.getFood().equals(this.foodToFind) {
                     return false;
                  }
                  return true;
                }
              
                public Collection<Cat> search( Collection<Cat> listToSearch ) {
                  // details left to the imagination, but basically iterate over
                  // the input list, call isMatch on each element, and if true
                  // add it to a local collection which is returned at the end.
                }
              
              }
              

              【讨论】:

                【解决方案17】:

                JFilter http://code.google.com/p/jfilter/ 满足您的要求。

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

                主要特点

                • 支持集合(java.util.Collection、java.util.Map 和 Array)属性。
                • 支持任意深度的集合内集合。
                • 支持内部查询。
                • 支持参数化查询。
                • 可以在 100 毫秒内过滤 100 万条记录。
                • 过滤器(查询)以简单的json格式给出,就像Mangodb查询一样。以下是一些例子。
                  • { "id":{"$le":"10"}
                    • 其中对象 id 属性小于等于 10。
                  • { "id": {"$in":["0", "100"]}}
                    • 其中对象 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 参数化类型的集合属性具有参数化类型的税收地图类型属性的代码等于大于 1.01 的 GST 值。
                  • {'$or':[{'code':'10'},{'skus': {'$and':[{'price':{'$in':['20', '40 ']}}, {'code':'RedApple'}]}}]}
                    • 选择产品代码为 10 或 sku 价格为 20 和 40 且 sku 代码为“RedApple”的所有产品。

                【讨论】:

                  【解决方案18】:

                  Guava 在遇到此类问题时具有非常强大的搜索功能。例如,如果您所在的区域根据其中一个属性搜索对象,您可能会考虑:

                  Iterables.tryFind(listOfCats, new Predicate<Cat>(){
                      @Override
                      boolean apply(@Nullable Cat input) {
                          return "tom".equalsIgnoreCase(input.name());
                      }
                  }).or(new Cat("Tom"));
                  

                  如果汤姆猫可能不在listOfCats中,它将被返回,从而使您可以避免NPE。

                  【讨论】:

                    【解决方案19】:

                    一个非常常见的问题,我使用了谷歌收藏,这是我的代码

                    public class FindByIdPredicate implements Predicate<IDObject> {
                    
                    private Long entityId;
                    
                    public FindByIdPredicate(final Long entityId) {
                        this.entityId = entityId;
                    
                    }
                    
                    @Override
                    public boolean apply(final IDObject input) {
                        return input.getId().equals(this.entityId);
                    }
                    
                    /**
                         * Pass in the Collection
                     * @param Collection
                     * @return IdObject if present or null
                     */
                    public IDObject getEntity(final Collection<? extends IDObject> collection) {
                    
                        for (IDObject idObject : collection) {
                            if (this.apply(idObject)) {
                                return idObject;
                            }
                        }
                        return null;
                    
                    }
                    
                    /**
                     * @param Set
                     * @return IdObject if present or null
                     */
                    @SuppressWarnings("unchecked")
                    public <T> T getEntity(final Set<? extends IDObject> set) {
                    
                        for (IDObject idObject : set) {
                            if (this.apply(idObject)) {
                                return (T) idObject;
                            }
                        }
                        return null;
                    
                    }
                    

                    }

                    希望对你有帮助

                    【讨论】:

                      【解决方案20】:

                      您可以使用以下功能从列表中搜索项目。 祝你好运!

                      int _searchList(List<T> list,T item) {
                      int idx = -1;
                      idx = Collections.binarySearch(list,item,
                              new Comparator<T>() {
                                  public int compare(Titm1,
                                          Titm2) {
                      
                                      // key1
                                      if (itm1.key1.compareTo(itm2.key1) != 0) {
                                          return itm1.key2.compareTo(itm2.key2);
                                      }
                      
                                      // key2
                                      return itm1.key2
                                              .compareTo(itm2.key2);
                                  }
                              });
                      
                      return idx;
                      

                      }

                      【讨论】:

                        猜你喜欢
                        • 2015-03-27
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2013-01-04
                        • 1970-01-01
                        • 2013-02-13
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多