Поиск всех объектов, имеющих заданное свойство внутри коллекции

У меня есть сложный объект, такой как Cat, который обладает многими свойствами, такими как возраст, любимая еда для кошек и т.д.

Букет кошек хранится в коллекции Java, и мне нужно найти всех кошек, которые в возрасте 3 лет, или тех, чья любимая еда для кошек - Whiskas. Разумеется, я могу написать собственный метод, который найдет тех Кошек с определенным свойством, но это становится громоздким со многими свойствами; есть ли общий способ сделать это?

Ответ 1

Вы можете написать метод, который принимает экземпляр интерфейса, который определяет метод check(Cat), где этот метод может быть реализован с помощью любой требуемой проверки свойств.

Еще лучше, сделайте его общим:

public interface Checker<T> {
    public boolean check(T obj);
}

public class CatChecker implements Checker<Cat> {
    public boolean check(Cat cat) {
        return (cat.age == 3); // or whatever, implement your comparison here
    }
}

// put this in some class
public static <T> Collection<T> findAll(Collection<T> coll, Checker<T> chk) {
    LinkedList<T> l = new LinkedList<T>();
    for (T obj : coll) {
         if (chk.check(obj))
             l.add(obj);
    }
    return l;
}

Конечно, как говорят другие люди, это то, что реляционные базы данных были созданы для...

Ответ 2

Попробуйте API коллекций коллекций:

List<Cat> bigList = ....; // master list

Collection<Cat> smallList = CollectionUtils.select(bigList, new Predicate() {
    public boolean evaluate(Object o) {
        Cat c = (Cat)o;
        return c.getFavoriteFood().equals("Wiskas") 
            && c.getWhateverElse().equals(Something);
    }
});

Конечно, вам не обязательно использовать анонимный класс каждый раз, вы можете создавать реализации интерфейса Predicate для обычно используемых поисков.

Ответ 3

Я использую Google Collections (теперь называемый Guava) для такого рода проблем. Существует класс под названием Iterables, который может использовать интерфейс Predicate как параметр метода, который действительно полезен.

Cat theOne = Iterables.find(cats, new Predicate<Cat>() {
    public boolean apply(Cat arg) { return arg.age() == 3; }
});

Отметьте здесь!

Ответ 4

С выражением лямбда Java 8 вы можете сделать что-то вроде

cats.stream()
    .filter( c -> c.getAge() == 3 && c.getFavoriteFood() == WHISKAS )
    .collect(Collectors.toList());

Концептуально то же, что и подход Guava Predicate, но он выглядит намного чище с lambda

Вероятно, не правильный ответ для OP, но стоит отметить для людей с аналогичной потребностью.:)

Ответ 5

Я предлагаю использовать Jxpath, он позволяет делать запросы на графах объектов, как если бы он где xpath вроде

JXPathContext.newContext(cats).
     getValue("//*[@drinks='milk']")

Ответ 6

Снова с коллекциями коллекций API: Вы получаете код типа "checker" при одновременном выполнении Predicate: -

public class CatPredicate implements Predicate {

    private int age; 


    public CatPredicate(int age) {
        super();
        this.age = age;
    }


    @Override
    public boolean evaluate(Object o) {
        Cat c (Cat)o;
        return c.getAge()==this.age;
    }

}

который используется как: -

CollectionUtils.filter(catCollectionToFilter, new CatPredicate(3))

Ответ 7

Вы можете использовать lambdaj. Такие вещи тривиальны, синтаксис действительно плавный:

Person me = new Person("Mario", "Fusco", 35);
Person luca = new Person("Luca", "Marrocco", 29);
Person biagio = new Person("Biagio", "Beatrice", 39);
Person celestino = new Person("Celestino", "Bellone", 29);
List<Person> meAndMyFriends = asList(me, luca, biagio, celestino);
List<Person> oldFriends = filter(having(on(Person.class).getAge(), greaterThan(30)), meAndMyFriends);

и вы можете делать гораздо более сложные вещи. Он использует hamcrest для совпадений. Некоторые утверждают, что это не стиль Java, но это забавно, как этот парень скрутил Java, чтобы сделать немного функционального программирования. Посмотрите также на исходный код, это довольно научно-фантастический.

Ответ 8

Использование Коллекций Commons:

EqualPredicate nameEqlPredicate = new EqualPredicate(3);
BeanPredicate beanPredicate = new BeanPredicate("age", nameEqlPredicate);
return CollectionUtils.filter(cats, beanPredicate);

Ответ 9

Просто FYI есть 3 других ответа на этот вопрос, которые используют Guava, но никто не отвечает на вопрос. Заявитель сказал, что хочет найти всех Кошек с соответствующим свойством, например. возраст 3. Iterables.find будет соответствовать только одному, если таковые имеются. Для достижения этой цели вам понадобится Iterables.filter, если вы используете Guava, например:

Iterable<Cat> matches = Iterables.filter(cats, new Predicate<Cat>() {
    @Override
    public boolean apply(Cat input) {
        return input.getAge() == 3;
    }
});

Ответ 10

Звучит очень похоже на то, что вы используете LINQ для .NET.

Пока нет никакой "реальной" реализации LINQ для java, вы можете посмотреть Quaere, который мог бы сделать то, что вы описываете достаточно легко.

Ответ 11

Вы можете использовать что-то вроде JoSQL и писать "SQL" для своих коллекций: http://josql.sourceforge.net/

Звучит так, как вы хотите, с дополнительным преимуществом, позволяющим выполнять более сложные запросы.

Ответ 12

Вы можете попробовать некоторые из общего кода в проекте Apache Commons. Подпроект Collections предоставляет код для поиска объектов, соответствующих конкретному Predicate, а также большое количество предикатов (равно, null, instanceof, и т.д). Подпроект BeanUtils позволяет создавать предикаты, проверяющие свойства beans.

Используйте класс CollectionUtils для поиска в коллекции. Для этого существует несколько методов, но, в частности, проверьте метод select(). Используйте следующие классы для построения предикатов или напишите свой собственный: Predicate, PredicateUtils, BeanPredicate.

Это все иногда немного громоздко, но, по крайней мере, это родовое!: -)

Ответ 13

Решения на основе предикатов, фильтров и пользовательских итераторов/компараторов хороши, но они не обеспечивают аналог с индексом базы данных. Например: я хотел бы искать коллекцию кошек по-разному: по полу и возрасту и по возрасту, так что это выглядит как два индекса: 1) [пол, возраст] 2) [возраст]. Значения, доступ к которым по этим индексам могут быть хэшированы, для быстрого поиска, без повторения всей коллекции. Где-нибудь есть такое решение?

Ответ 14

Использовать Google Guava.

final int lastSeq = myCollections.size();
Clazz target = Iterables.find(myCollections, new Predicate<Clazz>() {
    @Override
    public boolean apply(@Nullable Clazz input) {
      return input.getSeq() == lastSeq;
    }
});

Я думаю использовать этот метод.

Ответ 15

Вы можете сохранить эти объекты в базе данных. Если вы не хотите накладных расходов на полномасштабный сервер базы данных, вы можете использовать встроенный, такой как HSQLDB. Затем вы можете использовать Hibernate или BeanKeeper (более простой в использовании) или другой ORM для сопоставления объектов с таблицами. Вы продолжаете использовать модель OO и получаете расширенные возможности хранения и запросов из базы данных.

Ответ 16

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

Вы можете пойти дальше и сохранить имена свойств, а также, возможно, на карте с требуемыми значениями. В этом случае вы должны использовать отражение класса Cat для вызова соответствующих методов с именами свойств.

public class CatSearcher {

  private Integer ageToFind = null;
  private String  foodToFind = null;

  public CatSearcher( Integer age, String food ) {
    this.ageToFind = age;
    this.foodToFind = food;
  }

  private boolean isMatch(Cat cat) {
    if ( this.ageToFind != null && !cat.getAge().equals(this.ageToFind) ) {
       return false;
    }
    if ( this.foodToFind != null && !cat.getFood().equals(this.foodToFind) {
       return false;
    }
    return true;
  }

  public Collection<Cat> search( Collection<Cat> listToSearch ) {
    // details left to the imagination, but basically iterate over
    // the input list, call isMatch on each element, and if true
    // add it to a local collection which is returned at the end.
  }

}

Ответ 17

JFilter http://code.google.com/p/jfilter/ соответствует вашим требованиям.

JFilter - это простая и высокопроизводительная библиотека с открытым исходным кодом для запроса коллекции Java beans.

Основные функции

  • Поддержка свойств коллекции (java.util.Collection, java.util.Map и Array).
  • Поддержка коллекции внутри коллекции любой глубины.
  • Поддержка внутренних запросов.
  • Поддержка параметризованных запросов.
  • Может фильтровать 1 миллион записей за несколько минут.
  • Фильтр (запрос) задается в простом формате json, это похоже на запросы Mangodb. Ниже приведены некоторые примеры.
    • { "id" : { "$ le": "10" }
      • где свойство id объекта меньше, чем 10.
    • { "id" : { "$ in": [ "0", "100" ]}}
      • где свойство id объекта равно 0 или 100.
    • { "lineItems": { "lineAmount": "1" }}
      • где свойство collectionItems параметризованного типа имеет значение lineAmount равно 1.
    • { "$ and": [{ "id" : "0" }, { "billingAddress": { "city": "DEL" }}]}
      • где id - значение 0, а свойство billingAddress.city - DEL.
    • { "lineItems": { "tax": { "key": { "code": "GST" }, "value": { "$ gt": "1.01" }}}}
      • где свойство collectionItems параметризованного типа, у которого есть свойство типа карты налогов с параметризованным типом, имеет код, равный значению GST больше 1.01.
    • {'$ or': [{'code': '10'}, {'skus': {'$ and': [{'price': {'$ in': ['20', '40 ']}}, {' code ':' RedApple '}]}}]}
      • Выбрать все продукты, в которых код продукта равен 10 или sku в 20 и 40, а код sku - "RedApple".

Ответ 18

Guava обладает очень мощными возможностями поиска, когда дело доходит до таких проблем. Например, если ваша область ищет объект на основе одного из его свойств, вы можете рассмотреть:

Iterables.tryFind(listOfCats, new Predicate<Cat>(){
    @Override
    boolean apply(@Nullable Cat input) {
        return "tom".equalsIgnoreCase(input.name());
    }
}).or(new Cat("Tom"));

если возможно, что Tom cat не находится в спискеOfCats, он будет возвращен, что позволит вам избежать NPE.

Ответ 19

Очень распространенная проблема, и я использовал сбор google и вот мой код

public class FindByIdPredicate implements Predicate<IDObject> {

private Long entityId;

public FindByIdPredicate(final Long entityId) {
    this.entityId = entityId;

}

@Override
public boolean apply(final IDObject input) {
    return input.getId().equals(this.entityId);
}

/**
     * Pass in the Collection
 * @param Collection
 * @return IdObject if present or null
 */
public IDObject getEntity(final Collection<? extends IDObject> collection) {

    for (IDObject idObject : collection) {
        if (this.apply(idObject)) {
            return idObject;
        }
    }
    return null;

}

/**
 * @param Set
 * @return IdObject if present or null
 */
@SuppressWarnings("unchecked")
public <T> T getEntity(final Set<? extends IDObject> set) {

    for (IDObject idObject : set) {
        if (this.apply(idObject)) {
            return (T) idObject;
        }
    }
    return null;

}

}

Надеюсь, что это поможет

Ответ 20

вы можете искать элемент из списка следующим образом. удачи!

int _searchList(List<T> list,T item) {
int idx = -1;
idx = Collections.binarySearch(list,item,
        new Comparator<T>() {
            public int compare(Titm1,
                    Titm2) {

                // key1
                if (itm1.key1.compareTo(itm2.key1) != 0) {
                    return itm1.key2.compareTo(itm2.key2);
                }

                // key2
                return itm1.key2
                        .compareTo(itm2.key2);
            }
        });

return idx;

}