我想检查List
是否包含具有特定值的字段的对象。现在,我可以使用一个循环来检查,但我很好奇是否有更多的代码有效。
喜欢的东西;
if(list.contains(new Object().setName("John"))){
//Do some stuff
}
我知道上面的代码没有做任何事情,只是为了大致展示我想要实现的目标。
另外,为了澄清,我不想使用简单循环的原因是因为此代码当前将进入循环内循环内部的循环。为了便于阅读,我不想继续为这些循环添加循环。所以我想知道是否有任何简单的(ish)替代品。
答案 0 :(得分:187)
如果您使用的是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();
}
如果true
包含名称为List<MyObject>
的{{1}},则此方法将返回MyObject
。如果您想对name
的每个MyObject
执行操作,那么您可以尝试这样的操作:
getName().equals(name)
public void perform(final List<MyObject> list, final String name){
return list.stream().filter(o -> o.getName().equals(name)).forEach(
o -> {
//...
}
);
}
代表o
个实例。
答案 1 :(得分:77)
您有两种选择。
1。第一个选择,更可取的是覆盖Object类中的`equals()`方法。
比方说,你有这个Object类:
public class MyObject {
private String name;
private String location;
//getters and setters
}
现在假设您只关心MyObject的名称,它应该是唯一的,所以如果两个`MyObject`具有相同的名称,它们应该被认为是相同的。在这种情况下,您可能希望覆盖`equals()`方法(以及`hashcode()`方法),以便比较名称以确定相等。
完成此操作后,您可以检查一个Collection是否包含一个名为“foo”的MyObject,如下所示:
MyObject object = new MyObject();
object.setName("foo");
collection.contains(object);
但是,如果出现以下情况,这可能不适合您:
如果是这种情况之一,您需要选项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;
}
不幸的是,没有比这更好的方法了。
答案 2 :(得分:24)
如果您正在使用Guava,则可以采用功能方法并执行以下操作
FluentIterable.from(list).find(new Predicate<MyObject>() {
public boolean apply(MyObject input) {
return "John".equals(input.getName());
}
}).Any();
看起来有点冗长。但是谓词是一个对象,您可以为不同的搜索提供不同的变体。请注意库本身如何分离集合的迭代和您希望应用的函数。您不必为特定行为覆盖equals()
。
如下所述,Java 8及更高版本中内置的java.util.Stream框架提供了类似的功能。
答案 3 :(得分:19)
您可以使用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
。通过这种方式,您可以搜索具有不同搜索策略的对象。
答案 4 :(得分:18)
Collection.contains()
是通过在每个对象上调用equals()
来实现的,直到有人返回true
。
所以实现这一点的一种方法是覆盖equals()
,但当然,你只能有一个等于。
Guava之类的框架因此使用谓词。使用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());
如果您关心这样的事情,我建议您阅读“Beyond Java”一书。它包含许多关于Java的众多缺点以及其他语言如何做得更好的例子。
答案 5 :(得分:9)
这是使用Java 8+的方法:
boolean johnExistance = list.stream().anyMatch(o -> o.getName().equals("John"));
答案 6 :(得分:8)
您可以使用其中一个值作为键创建Hashmap<String, Object>
,然后查看yourHashMap.keySet().contains(yourValue)
是否返回true。
答案 7 :(得分:6)
如果您使用的是Eclipse Collections,则可以使用anySatisfy()
方法。如果可能,请在List
中调整ListAdapter
或将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 ollections的提交者。
答案 8 :(得分:5)
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类。
以我的方式做这件事有什么好处?你有一个方法来处理任何类型集合中的搜索。如果你想通过不同的字段搜索,你不必创建单独的方法。你需要做的是提供不同的谓词,一旦它不再有用就可以被销毁/
如果你想使用它,你需要做的就是调用方法并定义tyour谓词
CollectionUtil.find(list, new Predicate<MyObject>{
public boolean contains(T item){
return "John".equals(item.getName());
}
});
答案 9 :(得分:3)
contains
方法在内部使用equals
。因此,您需要根据需要覆盖班级的equals
方法。
顺便说一下,这看起来不是合理的:
new Object().setName("John")
答案 10 :(得分:3)
以下是使用Guava
的解决方案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);
}
});
}
答案 11 :(得分:1)
如果您需要反复执行此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);
答案 12 :(得分:0)
尽管有JAVA 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 );