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




Я бы использовал компаратор, который принимает в качестве входных параметров диапазон лет и образец номерного знака. Затем просто переберите свою коллекцию и скопируйте соответствующие объекты. Скорее всего, при таком подходе вы создадите целый пакет настраиваемых компараторов.
Если вам нужно одно конкретное совпадение, вы можете реализовать класс Comparator, а затем создать автономный объект со всеми включенными хешированными полями и использовать его для возврата индекса совпадения. Если вы хотите найти более одного (потенциально) объекта в коллекции, вам придется обратиться к такой библиотеке, как JoSQL (которая хорошо себя зарекомендовала в тех тривиальных случаях, для которых я ее использовал).
В общем, я обычно встраиваю Derby даже в свои небольшие приложения, использую аннотации Hibernate для определения классов моделей и позволяю Hibernate заниматься схемами кэширования, чтобы все было быстро.
Встраивание базы данных в памяти, такой как Derby, звучит как хорошая идея, особенно потому, что Derby теперь является частью JDK. Введение Hibernate в микс было бы для меня излишним. Я бы просто пошел с SQL / JDBC, я думаю.
Я использовал Apache Commons JXPath в производственном приложении. Он позволяет применять выражения XPath к графам объектов в Java.
это интерпретатор выражения XPath
Вариант Comparator неплох, особенно если вы используете анонимные классы (чтобы не создавать избыточные классы в проекте), но в конечном итоге, когда вы смотрите на поток сравнений, это в значительной степени похоже на цикл по всей коллекции, указав точно условия для сопоставления предметов:
if (Car car : cars) {
if (1959 < car.getYear() && 1970 > car.getYear() &&
car.getLicense().startsWith("AZ")) {
result.add(car);
}
}
Затем есть сортировка ... это может быть головной болью, но, к счастью, есть класс Collections и его методы sort, один из которых получает Comparator ...
Это тот подход, который я использую сейчас, и он быстро становится громоздким, когда критерии повышаются. Но, наверное, для простого примера это нормально.
Продолжая тему 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/…. Масштабируемость улучшается только в том случае, если количество значений разные значительно увеличивается (например, вы получаете существенно больше разных цветов по мере роста общего набора).
Интересно. Моя функция временной сложности задумана как настоящая формула инженерного стиля. Если мы будем следовать строгому правилу Big O Notation для «умножения на скаляр» в Википедии и, таким образом, изменим формулу с O (0.2n) на O (n), то мы отбросим информацию о достоинствах этого подхода по сравнению с другими, когда n <бесконечность. Я предполагаю, что мое утверждение больше с инженерной точки зрения, чем с точки зрения теоретической информатики. Я полагаю, что функция действительно должна быть O (избирательность (c, n) n) или что-то в любом случае. Спасибо за интересный момент, я посмотрю на переделку.
да, я знаю, что это старый пост, но технологии появляются каждый день, и ответ со временем изменится.
Я думаю, что это хорошая проблема, чтобы решить ее с помощью 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 раза.
Не могли бы вы уточнить, пожалуйста? Я понимаю, как создать собственный компаратор и реализовать метод сравнения для сравнения по некоторому свойству. Но я не уверен, как это сделать с несколькими входными параметрами?