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

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

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

7 ответов

Решение

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

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

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

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

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

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

Скажем, у вас есть коллекция Car объекты и каждый Car объект имеет поле color, Скажите, что ваш запрос эквивалентенSELECT * FROM cars WHERE Car.color = 'blue'Msgstr "Вы можете построить индекс на 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 (Collection Query Engine). По сути, это механизм запросов NoSQL для извлечения объектов из Java-коллекций с использованием SQL-подобных запросов без дополнительных затрат на итерацию всей коллекции. Он построен на основе идей, приведенных выше, а также некоторых других. Отказ от ответственности: я автор. Это с открытым исходным кодом и в Maven Central. Если вы найдете это полезным, пожалуйста, проголосуйте за этот ответ!

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам