Как вы запрашиваете коллекции объектов в Java (Criteria / SQL-like)? - PullRequest
29 голосов
/ 18 сентября 2008

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

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

Ответы [ 7 ]

24 голосов
/ 30 июля 2012

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

Хотя фильтрация не масштабируется. На первый взгляд сложность может показаться равной 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 (механизм сбора запросов) . По сути, это механизм запросов NoSQL для извлечения объектов из Java-коллекций с использованием SQL-подобных запросов без дополнительных затрат на итерацию всей коллекции. Он построен на основе идей, приведенных выше, плюс еще немного. Отказ от ответственности: я автор. Это с открытым исходным кодом и в Maven Central. Если вы найдете это полезным, пожалуйста, проголосуйте за этот ответ!

12 голосов
/ 18 сентября 2008

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

5 голосов
/ 06 марта 2014

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

Я думаю, что это хорошая проблема, чтобы решить ее с 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()); 
3 голосов
/ 18 сентября 2008

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

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

2 голосов
/ 18 сентября 2008

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

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

1 голос
/ 18 сентября 2008

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

0 голосов
/ 18 сентября 2008

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...