查找集合中具有给定属性的所有对象

时间:2009-02-25 19:15:28

标签: java collections

我有一些复杂的物体,例如猫,它具有许多属性,例如年龄,喜欢的猫食等等。

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

20 个答案:

答案 0 :(得分:62)

尝试使用commons集合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接口的实现。

答案 1 :(得分:55)

我一直在使用Google Collections(现在称为Guava)来解决此类问题。有一个名为Iterables的类可以将名为Predicate的接口作为一个非常有用的方法的参数。

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

检查here

答案 2 :(得分:52)

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

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

概念上与Guava谓词方法相同,但使用lambda

看起来更清晰

可能不是OP的有效答案,但值得注意的是有相似需求的人。 :)

答案 3 :(得分:45)

您可以编写一个方法,该方法接受一个定义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;
}

当然,和其他人一样,这就是为...制作关系数据库的。

答案 4 :(得分:11)

我建议使用Jxpath,它允许您对对象图进行查询,就好像它在xpath之类的

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

答案 5 :(得分:5)

再次使用commons集合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))

答案 6 :(得分:4)

您可以使用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来进行一些函数式编程很有趣。看看源代码也是如此,它非常科幻。

答案 7 :(得分:3)

仅供参考,对于使用番石榴的这个问题,还有其他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;
    }
});

答案 8 :(得分:3)

使用Commons Collections:

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

答案 9 :(得分:1)

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

答案 10 :(得分:1)

使用Google Guava。

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

我认为要使用这种方法。

答案 11 :(得分:1)

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

使用CollectionUtils类在集合中进行搜索。有几种方法,但特别是检查select()方法。 使用以下类构建谓词,或编写自己的谓词:PredicatePredicateUtilsBeanPredicate

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

答案 12 :(得分:1)

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

这听起来像你想要的,还有额外的好处,可以做更复杂的查询。

答案 13 :(得分:1)

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

虽然还没有针对java的“真正的”LINQ实现,但您可能希望查看Quaere,它可以轻松地完成您所描述的内容。

答案 14 :(得分:0)

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。
    • { “了LineItem”:{ “lineAmount”: “1”}}
      • 其中参数化类型的lineItems集合属性的lineAmount等于1。
    • {“$ and”:[{“id”:“0”},{“billingAddress”:{“city”:“DEL”}}}}
      • 其中id属性为0,billingAddress.city属性为DEL。
    • {“lineItems”:{“tax”:{“key”:{“code”:“GST”},“value”:{“$ gt”:“1.01”}}}}
      • 其中参数化类型的lineItems集合属性具有参数化类型的税图属性,其代码等于GST值大于1.01。
    • {'$'或':[{'code':'10'},{'skus':{'$ and':[{'price':{'$ in':['20','40' ''}},{'code':'RedApple'}]}}}}
      • 选择所有产品代码为10或sku价格为20和40且sku代码为“RedApple”的产品。

答案 15 :(得分:0)

当涉及到这类问题时,Guava具有非常强大的搜索功能。例如,如果您的区域基于其中一个属性搜索对象,则可以考虑:

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

如果Tom cat可能不在listOfCats中,它将被返回,从而允许你避免NPE。

答案 16 :(得分:0)

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

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;

}

}

希望这有帮助

答案 17 :(得分:0)

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

您可以进一步存储属性的名称,可能在具有所需值的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.
  }

}

答案 18 :(得分:0)

您可以从列表中搜索项目,如下所示。 祝你好运!

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;

}

答案 19 :(得分:0)

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