Список На Java.содержит (объект со значением поля, равным x)

Я хочу проверить, является ли List содержит объект, имеющий поле с определенным значением. Теперь я мог использовать цикл, чтобы пройти и проверить, но мне было любопытно, есть ли что-нибудь более эффективное.

что-то вроде:

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

Я знаю, что приведенный выше код ничего не делает, он просто демонстрирует примерно то, что я пытаюсь достичь.

кроме того, просто чтобы уточнить, причина, по которой я не хочу использовать простой цикл, заключается в том, что этот код будет в настоящее время идут внутри цикла внутри цикла, который находится внутри петли. Для удобства чтения я не хочу продолжать добавлять циклы к этим циклам. Поэтому я задался вопросом, есть ли какие-либо простые(иш) альтернативы.

12 ответов


потоки

если вы используете 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> содержит MyObject на имя name. Если вы хотите выполнить операцию на каждом из MyObjects это 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 представляет собой MyObject пример.


у вас есть два варианта.

1. Первый вариант, который предпочтительнее, - переопределить метод 'equals ()' в вашем классе объектов.

допустим, например, у вас есть этот класс объектов:

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

теперь предположим, что вас волнует только имя MyObject, что оно должно быть уникальным, поэтому, если два MyObject имеют одинаковое имя, они должны считаться равными. В этом случае вы захотите переопределить метод` equals () ' (а также метод метод " hashcode ()"), чтобы он сравнивал имена для определения равенства.

как только вы это сделаете, вы можете проверить, содержит ли коллекция MyObject с именем "foo", например:

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;
    }

к сожалению нет лучшего способа вокруг него.


Google Guava

если вы используете гуавы можно взять функциональный подход и сделать следующее

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

, который выглядит немного многословен. Однако предикат является объектом, и вы можете предоставить разные варианты для разных поисков. Обратите внимание, как сама библиотека разделяет итерацию коллекции и функцию, которую вы хотите применить. Вам не нужно переопределять equals() для определенного поведения.

как отмечено ниже, java.утиль.Поток framework, встроенный в Java 8 и более поздние версии, предоставляет что-то подобное.


Бинарный Поиск

можно использовать сборники.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 объекта. При этом вы можете искать объекты с различными стратегиями поиска.


Collection.contains() реализуется путем вызова equals() на каждом объекте, пока не вернется true.

таким образом, один из способов реализовать это-переопределить equals() но конечно, вы можете иметь только один равен.

фреймворки, такие как гуавы поэтому используйте предикаты для этого. С Iterables.find(list, predicate), вы можете искать произвольные поля, помещая тест в предикат.

другие языки построены на ВМ есть такая встроенная. В в 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 и того, как другие языки делают лучше.


Коллекции Eclipse

если вы используете Коллекции Eclipse можно использовать 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.


карта

вы можете создать Hashmap<String, Object> использование одного из значений в качестве ключа, а затем просмотр, если yourHashMap.keySet().contains(yourValue) возвращает true.


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());
     }
});

contains способ использования equals внутренне. Поэтому вам нужно переопределить equals метод для вашего класса согласно вашей потребности.

BtW это не выглядит syntatically правильно:

new Object().setName("John")

Если вам нужно выполнить эту 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);


несмотря на 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 );

вот решение, использующее гуавы

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);
        }
    });
}