Java清单。contains(字段值等于x的对象)

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

类似的;

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

我知道上面的代码没有做任何事情,它只是粗略地演示了我想要实现的目标。

另外,澄清一下,我不想使用简单循环的原因是,这段代码将进入一个循环,这个循环在一个循环中,而这个循环又在一个循环中。为了可读性,我不想一直在这些循环中添加循环。所以我想知道是否有什么简单的替代方案。

559646 次浏览

contains方法在内部使用equals。因此,您需要根据需要重写类的equals方法。

顺便说一句,这看起来语法不正确:

new Object().setName("John")

谷歌番石榴

如果你正在使用番石榴,你可以采用函数方法并执行以下操作

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

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

如下文所述,Java 8及以后版本中内置的java.util.Stream框架提供了类似的功能。

Collection.contains()通过在每个对象上调用equals()来实现,直到返回true

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

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

其他构建在VM之上的语言都内置了这个功能。例如,在Groovy中,你只需写:

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

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

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

如果你关心这些事情,我建议你读《超越Java》这本书。它包含了许多关于Java的缺点以及其他语言如何做得更好的例子。

如果你正在使用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)的__abc2执行一个操作,那么你可以尝试这样做:

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

其中o表示一个MyObject实例。

或者,正如注释所建议的(谢谢MK10),你可以使用Stream#anyMatch方法:

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

二分查找

你可以使用Collections.binarySearch来搜索列表中的元素(假设列表是有序的):

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。有了它,您可以使用不同的搜索策略搜索对象。

你有两个选择。

1. 首选的方法是重写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);

但是,在以下情况下,这可能不适合你:

  • 您同时使用名称和位置来检查是否相等,但您只想检查集合中是否有具有特定位置的' 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;
}

不幸的是,没有更好的办法了。

Eclipse集合

如果你正在使用Eclipse集合,你可以使用anySatisfy()方法。在ListAdapter中调整你的List,或者如果可能的话将你的List改为ListIterable

ListIterable<MyObject> list = ...;


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

如果要经常执行这样的操作,最好提取一个方法来回答该类型是否具有该属性。

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()和方法引用。

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

如果你不能将你的List更改为ListIterable,下面是你如何使用ListAdapter

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

注意:我是Eclipse集合的提交者。

如果你需要重复执行这个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);相同的结果

Predicate

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

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类。

用我的方式做这件事有什么好处?有一个方法可以处理任何类型集合中的搜索。如果你想通过不同的字段进行搜索,你不必创建单独的方法。你所需要做的就是提供不同的谓词,一旦它不再有用/就可以销毁

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

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

地图

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

尽管JAVA 8 SDK有很多收集工具库可以帮助你工作,例如: http://commons.apache.org/proper/commons-collections/ < / p >
Predicate condition = new Predicate() {
boolean evaluate(Object obj) {
return ((Sample)obj).myField.equals("myVal");
}
};
List result = CollectionUtils.select( list, condition );

下面是一个使用番石榴的解决方案

private boolean checkUserListContainName(List<User> userList, final String targetName){


return FluentIterable.from(userList).anyMatch(new Predicate<User>() {
@Override
public boolean apply(@Nullable User input) {
return input.getName().equals(targetName);
}
});
}

这是如何使用Java 8+做到这一点:

boolean isJohnAlive = list.stream().anyMatch(o -> "John".equals(o.getName());