Java List.contains(字段值等于x的对象)

我想检查List是否包含具有特定值的字段的对象。 现在,我可以使用一个循环来检查,但我很好奇,如果有更多的代码效率。

就像是;

 if(list.contains(new Object().setName("John"))){ //Do some stuff } 

我知道上面的代码没有做任何事情,只是为了大致展示我想实现的目标。

此外,只是为了澄清,我不想使用一个简单的循环的原因是因为这段代码目前进入一个内循环内的循环内循环。 为了可读性,我不想在这些循环中添加循环。 所以我想知道是否有任何简单的(ish)select。

你有两个select。

1.第一个select是可取的,那就是覆盖Object类中的equals()方法。

比方说,你有这个Object类:

 public class MyObject { private String name; private String location; //getters and setters } 

现在让我们假设你只关心MyObject的名字,它应该是唯一的,所以如果两个MyObject具有相同的名字,他们应该被认为是相等的。 在这种情况下,你会想重写equals()方法(也是`hashcode()`方法),以便比较名称以确定相等性。

一旦你这样做了,你可以检查一下,如果一个集合包含一个名为“foo”的MyObject,像这样:

 MyObject object = new MyObject(); object.setName("foo"); collection.contains(object); 

但是,如果出现以下情况,这可能不是您的select。

  • 您正在使用名称和位置来检查相等性,但您只想检查一个集合是否具有任何具有某个位置的“MyObject”。 在这种情况下,你已经覆盖了`equals()`。
  • “MyObject”是API的一部分,您无权更改。

如果其中任何一种情况,您都需要选项2:

2.编写你自己的实用方法:

 public static boolean containsLocation(Collection<MyObject> c, String location) { for(MyObject o : c) { if(o != null && o.getLocation.equals(location)) { return true; } } return false; } 

或者,您可以扩展ArrayList(或其他集合),然后添加自己的方法:

 public boolean containsLocation(String location) { for(MyObject o : this) { if(o != null && o.getLocation.equals(location)) { return true; } } return false; } 

不幸的是没有更好的方法。

stream

如果你正在使用Java 8,也许你可以尝试这样的事情:

 public boolean containsName(final List<MyObject> list, final String name){ return list.stream().filter(o -> o.getName().equals(name)).findFirst().isPresent(); } 

或者,你可以尝试这样的事情:

 public boolean containsName(final List<MyObject> list, final String name){ return list.stream().map(MyObject::getName).filter(name::equals).findFirst().isPresent(); } 

如果List<MyObject>包含具有名称nameMyObject ,则此方法将返回true 。 如果你想对getName().equals(name)MyObject的每一个执行一个操作,那么你可以尝试这样的事情:

 public void perform(final List<MyObject> list, final String name){ return list.stream().filter(o -> o.getName().equals(name)).forEach( o -> { //... } ); } 

o代表一个MyObject实例。

Google Guava

如果您正在使用番石榴 ,您可以采取function性的方法,并执行以下操作

 FluentIterable.from(list).find(new Predicate<MyObject>() { public boolean apply(MyObject input) { return "John".equals(input.getName()); } }).Any(); 

看起来有点冗长。 但谓词是一个对象,您可以为不同的search提供不同的变体。 注意库本身是如何分离集合的迭代和你想要应用的函数的。 您不必为特定的行为重写equals()

如下所述,内置于Java 8及更高版本的java.util.Stream框架提供了类似的东西。

二进制search

您可以使用Collections.binarySearchsearch列表中的元素(假设列表已sorting):

 Collections.binarySearch(list, new YourObject("a1", "b", "c"), new Comparator<YourObject>() { @Override public int compare(YourObject o1, YourObject o2) { return o1.getName().compareTo(o2.getName()); } }); 

如果对象不在集合中,则返回负数,否则返回对象的index 。 有了这个,你可以search不同的search策略的对象。

Collection.contains()通过调用每个对象的equals()直到返回true

所以实现这个的一个方法是重写equals()但是当然,你只能有一个equals。

因此像番石榴这样的框架使用谓词。 使用Iterables.find(list, predicate) ,可以通过将testing放入谓词来search任意字段。

在VM之上构build的其他语言已经内置了这个function。例如,在Groovy中 ,您只需编写:

 def result = list.find{ it.name == 'John' } 

Java 8让我们的生活变得更加简单:

 List<Foo> result = list.stream() .filter(it -> "John".equals(it.getName()) .collect(Collectors.toList()); 

如果你关心这样的事情,我build议书“超越Java”。 它包含许多Java示例以及其他语言如何更好的示例。

Eclipse集合

如果您正在使用Eclipse集合 ,则可以使用anySatisfy()方法。 要么在ListAdapter调整List ,要么在可能的情况下将List更改为ListAdapter

 ListIterable<MyObject> list = ...; boolean result = list.anySatisfy(myObject -> myObject.getName().equals("John")); 

如果你经常这样做,最好是提取一个方法来判断types是否有属性。

 public class MyObject { private final String name; public MyObject(String name) { this.name = name; } public boolean named(String name) { return Objects.equals(this.name, name); } } 

您可以使用anySatisfyWith()的替代forms与方法引用一起使用。

 boolean result = list.anySatisfyWith(MyObject::named, "John"); 

如果您不能将List更改为ListAdapter ,那么您将如何使用ListAdapter

 boolean result = ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John"); 

注意:我是Eclipse的提交者。

contains方法在内部使用equals 。 所以你需要根据你的需要重写你的类的equals方法。

顺便说一句,这不看上去是合理的正确的:

 new Object().setName("John") 

Predicate

如果你不使用Java 8,或者为你提供更多的处理集合的function的库,你可以实现一些比你的解决scheme更可重用的东西。

 interface Predicate<T>{ boolean contains(T item); } static class CollectionUtil{ public static <T> T find(final Collection<T> collection,final Predicate<T> predicate){ for (T item : collection){ if (predicate.contains(item)){ return item; } } return null; } // and many more methods to deal with collection } 

我正在使用类似的东西,我有谓词接口,并且将它传递给我的util类。

我这样做的好处是什么? 你有一个方法处理search任何types的集合。 而且如果您想按不同的字段进行search,则不必创build单独的方法。 所有你需要做的是提供不同的谓词,一旦它不再使用/

如果你想使用它,所有你需要做的就是调用方法并定义一个tyour谓词

 CollectionUtil.find(list, new Predicate<MyObject>{ public boolean contains(T item){ return "John".equals(item.getName()); } }); 

地图

您可以创build一个Hashmap<String, Object>使用其中一个值作为键,然后查看您的yourHashMap.keySet().contains(yourValue)返回true。

如果您需要重复执行此List.contains(Object with field value equal to x) ,一个简单而有效的解决方法是:

 List<field obj type> fieldOfInterestValues = new ArrayList<field obj type>; for(Object obj : List) { fieldOfInterestValues.add(obj.getFieldOfInterest()); } 

那么List.contains(Object with field value equal to x)将具有与fieldOfInterestValues.contains(x);相同的结果fieldOfInterestValues.contains(x);

尽pipeJAVA 8 SDK有很多的收集工具库可以帮助你使用,例如: http : //commons.apache.org/proper/commons-collections/

 Predicate condition = new Predicate() { boolean evaluate(Object obj) { return ((Sample)obj).myField.equals("myVal"); } }; List result = CollectionUtils.select( list, condition );