2008-09-18 2 views
27

Предположим, у вас есть коллекция из нескольких сотен объектов в памяти, и вам нужно запросить этот список, чтобы возвращать объекты, соответствующие запросу SQL или критериям, подобным запросу. Например, у вас могут быть объекты «Список автомобилей», и вы хотите вернуть все автомобили, сделанные в 1960-х годах, с номером номерного знака, который начинается с AZ, заказанного по названию модели автомобиля.Как вы запрашиваете коллекции объектов в Java (Criteria/SQL-like)?

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

ответ

11

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

+0

Это переводчик xpath? – 2013-04-29 09:33:20

+0

это интерпретатор выражения XPath – 2013-04-30 18:01:38

1

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

+0

Не могли бы вы уточнить, пожалуйста? Я понимаю, как создать пользовательский Comparator и реализовать метод сравнения для сравнения по некоторому свойству. Но я не уверен, как это сделать с несколькими входными параметрами? – stian 2008-09-18 16:29:02

2

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

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

+0

Встраивание базы данных в памяти, например, Derby, кажется хорошей идеей, особенно, поскольку Derby теперь является частью JDK. Введение Hibernate в микс было бы немного излишним для моего использования. Я бы просто пошел с SQL/JDBC, я думаю. – stian 2008-09-18 16:19:25

0

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

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

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

+0

Это тот подход, который я использую сейчас, и он быстро становится неустойчивым, когда увеличивается критерий. Но это, вероятно, хорошо для простого примера. – stian 2008-09-18 16:24:22

3

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

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

22

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

Фильтрация пока не масштабируема. На поверхности сложность времени будет выглядеть как 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() временная сложность. Если в вашем запросе были дополнительные тесты, вы могли бы затем проверить каждый автомобиль в этом кандидат, чтобы проверить, соответствует ли он оставшимся результатам в вашем запросе. Поскольку набор кандидатов, вероятно, будет значительно меньше, чем весь сбор, сложность времени составляет менее O (n) (в техническом смысле см. Комментарии ниже). Производительность не ухудшает столько же, когда к коллекции добавляются дополнительные объекты. Но это все еще не идеально, читайте дальше.

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

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

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

+0

Хороший ответ, но вы должны отредактировать следующий оператор: «Поскольку набор кандидатов, вероятно, будет значительно меньше, чем весь сбор, временная сложность меньше O (n)». Это неверно. Предположим, у вас есть 5 разных цветов. Тогда размер набора кандидатов составляет в среднем 0.2n. Это приводит к O (0,2n) и O (0,2n) = O (n), см. Http://en.wikipedia.org/wiki/Big_O_notation#Multiplication_by_a_constant. Масштабируемость улучшается только в том случае, если количество * разных * значений значительно возрастает (например, вы получаете * значительно * более разные цвета по мере роста общего набора). – 2012-09-29 11:15:08

5

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

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

Вот вам пример:

Ищут активных клиентов // (Iterable версия)

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

Конечно, имея такой вид красоты y влияет на производительность (немного ... в среднем 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()); 
Смежные вопросы