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


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

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

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

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

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

12 123

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

кстати это не выглядит синтаксически правильно:

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