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

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

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

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
30
0
33 724
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

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

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

stian 18.09.2008 20:29

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

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

Встраивание базы данных в памяти, такой как Derby, звучит как хорошая идея, особенно потому, что Derby теперь является частью JDK. Введение Hibernate в микс было бы для меня излишним. Я бы просто пошел с SQL / JDBC, я думаю.

stian 18.09.2008 20:19
Ответ принят как подходящий

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

это интерпретатор выражения XPath

Eric Weilnau 30.04.2013 22:01

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

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

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

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

stian 18.09.2008 20:24

Продолжая тему Comparator, вы также можете взглянуть на Коллекции Google API. В частности, у них есть интерфейс под названием Предикат, который выполняет ту же роль, что и Comparator, в том смысле, что это простой интерфейс, который можно использовать с помощью метода фильтрации, такого как Наборы. Фильтр. Они включают в себя целый ряд реализаций составных предикатов для выполнения операций И, ИЛИ и т. д.

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

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

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

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

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

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

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

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

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

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

Хороший ответ, но вам следует отредактировать следующее утверждение: «Поскольку набор кандидатов, вероятно, будет значительно меньше, чем вся коллекция, временная сложность меньше O (n)». Это неверно. Предположим, у вас есть 5 разных цветов. Тогда размер набора кандидатов составляет в среднем 0,2n. Это приводит к O (0,2n) и O (0,2n) = O (n), см. en.wikipedia.org/wiki/…. Масштабируемость улучшается только в том случае, если количество значений разные значительно увеличивается (например, вы получаете существенно больше разных цветов по мере роста общего набора).

Chris Lercher 29.09.2012 15:15

Интересно. Моя функция временной сложности задумана как настоящая формула инженерного стиля. Если мы будем следовать строгому правилу Big O Notation для «умножения на скаляр» в Википедии и, таким образом, изменим формулу с O (0.2n) на O (n), то мы отбросим информацию о достоинствах этого подхода по сравнению с другими, когда n <бесконечность. Я предполагаю, что мое утверждение больше с инженерной точки зрения, чем с точки зрения теоретической информатики. Я полагаю, что функция действительно должна быть O (избирательность (c, n) n) или что-то в любом случае. Спасибо за интересный момент, я посмотрю на переделку.

npgall 29.09.2012 21:23

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

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

Обновлять: после java 8 вы можете использовать стандартные лямбда-выражения, например:

List<Customer> activeCustomers = customers.stream()
                                          .filter(Customer::isActive)
                                          .collect(Collectors.toList());                                      

Я слышал, что вы можете использовать его, но есть некоторые ошибки. Вы должны опубликовать форум con lambdaj, прежде чем пробовать его, так как это может быть рискованно. Кстати, имейте в виду, что использование lambdaj влияет на производительность. В одних примерах для достижения поставленных задач требуется в 6 раз больше, в других - в 1,5 раза.

Federico Piazza 02.05.2014 18:50

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