Как вы запрашиваете коллекции объектов в Java (критерии/SQL-подобные)?

Предположим, что у вас есть коллекция из нескольких сотен объектов в памяти, и вам нужно запросить этот список, чтобы возвращать объекты, соответствующие запросу SQL или критериям, подобным запросу. Например, у вас могут быть объекты "Список автомобилей", и вы хотите вернуть все автомобили, сделанные в 1960-х годах, с номером номерного знака, который начинается с AZ, заказанного по названию модели автомобиля.

Я знаю о JoSQL, кто-нибудь использовал это или имел какой-либо опыт с другими /homegrown решениями?

Ответ 1

Я использовал Apache Commons JXPath в производственном приложении. Это позволяет применять выражения XPath к графикам объектов в Java.

Ответ 2

Фильтрация - это один из способов сделать это, как описано в других ответах.

Фильтрация пока не масштабируется. На поверхности сложность времени представляется O (n) (т.е. уже не масштабируемой, если количество объектов в коллекции будет расти), но на самом деле потому, что один или несколько тестов должны применяться к каждому объекту в зависимости от запроса, время сложность более точно равна O (nt), где t - количество тестов, применяемых к каждому объекту.

Таким образом, производительность ухудшится по мере добавления дополнительных объектов в коллекцию и/или по мере увеличения количества тестов в запросе.

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

Один из подходов состоит в создании индексов в полях внутри объектов, хранящихся в вашей коллекции, и которые впоследствии будут проверяться в вашем запросе.

Скажем, у вас есть коллекция объектов Car, и каждый объект Car имеет поле color. Скажем, ваш запрос эквивалентен "SELECT * FROM cars WHERE Car.color = 'blue'". Вы можете создать индекс на Car.color, который будет выглядеть примерно так:

'blue' -> {Car{name=blue_car_1, color='blue'}, Car{name=blue_car_2, color='blue'}}
'red'  -> {Car{name=red_car_1, color='red'}, Car{name=red_car_2, color='red'}}

Затем, задав запрос WHERE Car.color = 'blue', набор синих автомобилей можно было бы найти в сложности времени O (1). Если в вашем запросе были дополнительные тесты, вы можете проверить каждый автомобиль в этом кандидате, чтобы проверить, соответствует ли он оставшимся результатам в вашем запросе. Поскольку набор кандидатов, вероятно, будет значительно меньше, чем весь сбор, временная сложность меньше O (n) (в техническом смысле см. Комментарии ниже). Производительность не ухудшается настолько, когда дополнительные объекты добавляются в коллекцию. Но это все еще не идеально, читайте дальше.

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

Индекс постоянного запроса будет похож на регистрацию запроса с помощью какой-либо интеллектуальной коллекции, так что, поскольку объекты добавляются и удаляются из коллекции, коллекция автоматически проверяет каждый объект на все постоянные запросы, которые были зарегистрированы с этим. Если объект совпадает с постоянным запросом, коллекция может добавлять/удалять его в/из набора, предназначенного для хранения объектов, соответствующих этому запросу. Впоследствии объекты, соответствующие любому зарегистрированному запросу, могут быть получены в сложной сложности O (1).

Вышеприведенная информация взята из CQEngine (Engine Query Engine). Это в основном механизм запросов NoSQL для извлечения объектов из коллекций Java с использованием запросов, подобных SQL, без накладных расходов на итерацию через коллекцию. Он построен вокруг идей выше, плюс еще несколько. Отказ от ответственности: Я автор. Это с открытым исходным кодом и в центральном центре. Если вы сочтете это полезным, пожалуйста, поддержите этот ответ!

Ответ 3

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

Я думаю, что это хорошая проблема для решения проблемы с LambdaJ. Вы можете найти это здесь: http://code.google.com/p/lambdaj/

Здесь у вас есть пример:

СМОТРЕТЬ ДЛЯ АКТИВНЫХ КЛИЕНТОВ//(Итерируемая версия)

List<Customer> activeCustomers = new ArrayList<Customer>();  
for (Customer customer : customers) {  
  if (customer.isActive()) {  
    activeCusomers.add(customer);  
  }  
}  

версия LambdaJ

List<Customer> activeCustomers = select(customers, 
                                        having(on(Customer.class).isActive()));  

Конечно, наличие такой красоты влияет на производительность (немного... в среднем 2 раза), но вы можете найти более читаемый код?

В нем много функций, другой пример может быть отсортирован:

Сортировка итераций

List<Person> sortedByAgePersons = new ArrayList<Person>(persons);
Collections.sort(sortedByAgePersons, new Comparator<Person>() {
        public int compare(Person p1, Person p2) {
           return Integer.valueOf(p1.getAge()).compareTo(p2.getAge());
        }
}); 

Сортировка с лямбдой

List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge()); 

Ответ 4

Продолжая тему Comparator, вы также можете взглянуть на API Google Collections. В частности, они имеют интерфейс Predicate, который выполняет аналогичную роль с Comparator, поскольку это простой интерфейс, который может использоваться методом фильтрации, например Sets.filter. Они включают в себя целую кучу составных предикатных реализаций, для выполнения AND, OR и т.д.

В зависимости от размера вашего набора данных может быть более целесообразным использовать этот подход, чем подход SQL или внешней реляционной базы данных.

Ответ 5

Если вам нужно одно конкретное соответствие, вы можете реализовать класс Comparator, а затем создать автономный объект со всеми хешированными полями и использовать его для возврата индекса соответствия. Если вы хотите найти более одного (потенциально) объекта в коллекции, вам придется обратиться к библиотеке, такой как JoSQL (которая хорошо работала в тривиальных случаях, в которых я ее использовал).

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

Ответ 6

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

Ответ 7

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

if (Car car : cars) {
    if (1959 < car.getYear() && 1970 > car.getYear() &&
            car.getLicense().startsWith("AZ")) {
        result.add(car);
    }
}

Тогда есть сортировка... которая может быть болью на обратной стороне, но, к счастью, существует класс Collections и его методы sort, один из которых получает Comparator...