Вам нужно проверить Бун. :)
http://rick -hightower.blogspot.com / 2013/11 / что-если-ява-коллекции-и-java.html
Вы можете добавить n поисковых индексов и поисковых индексов. Это также позволяет эффективно запрашивать примитивные свойства.
Вот пример, взятый из вики (я автор).
repoBuilder.primaryKey("ssn")
.searchIndex("firstName").searchIndex("lastName")
.searchIndex("salary").searchIndex("empNum", true)
.usePropertyForAccess(true);
Вы можете переопределить это, указав истинный флаг в качестве второго аргумента для searchIndex.
Обратите внимание, что empNum является уникальным поисковым индексом.
Что если бы было легко запрашивать сложный набор объектов Java во время выполнения? Что если бы существовал API, который поддерживал ваши индексы объектов (на самом деле только TreeMaps и HashMaps) в синхронизации.? Ну, тогда у вас будет репозиторий Boon. В этой статье показано, как использовать утилиты репозитория Boon для запроса объектов Java. Это первая часть. Там может быть много, много частей. :)
Репозиторий Boon значительно упрощает выполнение запросов к коллекциям на основе индексов.
Почему Boon data repo
Репозиторий Boon позволяет вам рассматривать коллекции Java больше как базу данных, по крайней мере, когда дело доходит до запросов к коллекциям. Репозиторий Boon не является базой данных в памяти и не может заменить организацию ваших объектов в структуры данных, оптимизированные для вашего приложения.
Если вы хотите тратить свое время на предоставление клиентской ценности, создание своих объектов и классов и использование API коллекций для ваших структур данных, то DataRepo предназначена для вас. Это не исключает возможности использования книг Кнута и создания оптимизированной структуры данных. Это просто помогает упростить мирские дела, чтобы вы могли тратить время на то, чтобы делать сложные вещи.
Родился по надобности
Этот проект возник из необходимости. Я работал над проектом, который планировал хранить большую коллекцию доменных объектов в оперативной памяти, и кто-то задал очень важный вопрос, который я пропустил. Как мы собираемся запросить эти данные. Я ответил, что мы будем использовать API коллекций и API потоковой передачи. Тогда я попытался сделать это ... Хммм ...
Я также устал использовать потоковый API JDK 8 для большого набора данных, и это было медленно. (Репозиторий Boon работает с JDK7 и JDK8). Это был линейный поиск / фильтр. Это по замыслу, но для того, что я делал, это не сработало. Мне нужны индексы для поддержки произвольных запросов.
Репозиторий Boon дополняет потоковый API.
Репозиторий Boon не стремится заменить потоковый API JDK 8, и на самом деле он хорошо работает с ним. Репозиторий Boon позволяет создавать индексированные коллекции. Индексы могут быть любыми (это подключаемые).
На данный момент индексы репо данных Boon основаны на ConcurrentHashMap и ConcurrentSkipListMap.
По своему дизайну репозиторий Boon работает со стандартными библиотеками коллекций. Не планируется создавать набор пользовательских коллекций. Нужно иметь возможность подключить Guava, Concurrent Trees или Trove, если вы этого хотите.
Это обеспечивает упрощенный API для этого. Он позволяет выполнять линейный поиск для определения завершенности, но я рекомендую использовать его в первую очередь для использования индексов, а затем использовать потоковый API для остальных (для безопасности типов и скорости).
Пиковый пик перед пошаговым
Допустим, у вас есть метод, который создает 200 000 объектов сотрудников, таких как:
List<Employee> employees = TestHelper.createMetricTonOfEmployees(200_000);
Так что теперь у нас 200 000 сотрудников. Давайте поищем их ...
Первая упаковка Сотрудники в поисковом запросе:
employees = query(employees);
Сейчас ищем:
List<Employee> results = query(employees, eq("firstName", firstName));
Так в чем же основное отличие вышеперечисленного от потокового API?
employees.stream().filter(emp -> emp.getFirstName().equals(firstName)
Примерно в 20 000% быстрее использовать Boon's DataRepo! Ах, сила HashMaps и TreeMaps. :)
Существует API, который выглядит так же, как ваши встроенные коллекции. Существует также API, который больше похож на объект DAO или объект Repo.
Простой запрос с объектом Repo / DAO выглядит так:
List<Employee> employees = repo.query(eq("firstName", "Diana"));
Более сложный запрос будет выглядеть так:
List<Employee> employees = repo.query(
and(eq("firstName", "Diana"), eq("lastName", "Smith"), eq("ssn", "21785999")));
Или это:
List<Employee> employees = repo.query(
and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), lte("salary", 200_000),
gte("salary", 190_000)));
Или даже это:
List<Employee> employees = repo.query(
and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), between("salary", 190_000, 200_000)));
Или, если вы хотите использовать потоковый API JDK 8, это работает против него:
int sum = repo.query(eq("lastName", "Smith")).stream().filter(emp -> emp.getSalary()>50_000)
.mapToInt(b -> b.getSalary())
.sum();
Выше было бы намного быстрее, если бы число сотрудников было довольно большим. Это сузило бы сотрудников, чье имя начиналось с Смита и получало зарплату выше 50 000. Допустим, у вас было 100 000 сотрудников и только 50 по имени Смит, так что теперь вы быстро сужаетесь до 50, используя индекс, который эффективно вытягивает 50 сотрудников из 100 000, а затем мы фильтруем всего 50 вместо целых 100 000.
Вот контрольный прогон из репо линейного поиска по сравнению с индексированным поиском в наносекундах:
Name index Time 218
Name linear Time 3709120
Name index Time 213
Name linear Time 3606171
Name index Time 219
Name linear Time 3528839
Кто-то недавно сказал мне: «Но с потоковым API вы можете запустить фильтр в parralel).
Посмотрим, как работает математика:
3,528,839 / 16 threads vs. 219
201,802 vs. 219 (nano-seconds).
Индексы победили, но это было фото-финиш. НЕ! :)
Это было только на 9500% быстрее, а не на 40 000% быстрее. Так близко .....
Я добавил еще несколько функций. Они активно используют индексы. :)
repo.updateByFilter (values (value ("firstName", "Di")),
и (eq ("firstName", "Diana"),
eq ("lastName", "Smith"),
eq ("ssn", "21785999")));
Выше было бы эквивалентно
ОБНОВЛЕНИЕ Сотрудник e
SET e.firstName = 'Di'
ГДЕ e.firstName = 'Диана'
и e.lastName = 'Smith'
и e.ssn = '21785999'
Это позволяет вам устанавливать несколько полей одновременно для нескольких записей, поэтому, если вы выполняли массовое обновление.
Существуют перегруженные методы для всех основных типов, поэтому, если у вас есть одно значение для обновления для каждого элемента, возвращаемого фильтром:
repo.updateByFilter("firstName", "Di",
and( eq("firstName", "Diana"),
eq("lastName", "Smith"),
eq("ssn", "21785999") ) );
Вот некоторые основные возможности выбора:
List <Map<String, Object>> list =
repo.query(selects(select("firstName")), eq("lastName", "Hightower"));
Вы можете выбрать столько, сколько захотите. Вы также можете вернуть список отсортированным:
List <Map<String, Object>> list =
repo.sortedQuery("firstName",selects(select("firstName")),
eq("lastName", "Hightower"));
Вы можете выбрать свойства связанных свойств (например, employee.department.name).
List <Map<String, Object>> list = repo.query(
selects(select("department", "name")),
eq("lastName", "Hightower"));
assertEquals("engineering", list.get(0).get("department.name"));
Выше было бы попытаться использовать поля классов. Если вы хотите использовать фактические свойства (emp.getFoo () или emp.foo), то вам нужно использовать selectPropertyPath.
List <Map<String, Object>> list = repo.query(
selects(selectPropPath("department", "name")),
eq("lastName", "Hightower"));
Обратите внимание, что select ("отдела", "имя") намного быстрее, чем selectPropPath ("отдел", "имя"), что может иметь значение в узком цикле.
По умолчанию все поисковые индексы и индексы поиска допускают дублирование (кроме индекса первичного ключа).
repoBuilder.primaryKey("ssn")
.searchIndex("firstName").searchIndex("lastName")
.searchIndex("salary").searchIndex("empNum", true)
.usePropertyForAccess(true);
Вы можете переопределить это, указав истинный флаг в качестве второго аргумента для searchIndex.
Обратите внимание, что empNum является уникальным поисковым индексом.
Если вы предпочитаете или нуждаетесь, вы можете получить даже простой поиск в виде карт:
List<Map<String, Object>> employees = repo.queryAsMaps(eq("firstName", "Diana"));
Я не уверен, является ли это функцией или ошибкой. Я думал, что когда вы работаете с данными, вам нужно представлять эти данные таким образом, чтобы не связывать потребителей данных с вашим реальным API. Кажется, что для этого нужно иметь карту типа String / основных типов.
Обратите внимание, что преобразование объекта в карту идет глубоко, как в:
System.out.println(employees.get(0).get("department"));
Урожайность:
{class=Department, name=engineering}
Это может быть полезно для отладки и специальных запросов для инструментов. Я рассматриваю возможность добавления поддержки для простого преобразования в строку JSON.
Добавлена возможность запрашивать свойства коллекции. Это должно работать с коллекциями и массивами так глубоко, как вам нравится. Прочитайте это снова, потому что это была настоящая MF для реализации!
List <Map<String, Object>> list = repo.query(
selects(select("tags", "metas", "metas2", "metas3", "name3")),
eq("lastName", "Hightower"));
print("list", list);
assertEquals("3tag1", idx(list.get(0).get("tags.metas.metas2.metas3.name3"), 0));
Печать из вышеперечисленного выглядит так:
list [{tags.metas.metas2.metas3.name3=[3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3,
3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3]},
...
Я создал несколько классов отношений, чтобы проверить это:
public class Employee {
List <Tag> tags = new ArrayList<>();
{
tags.add(new Tag("tag1"));
tags.add(new Tag("tag2"));
tags.add(new Tag("tag3"));
}
...
public class Tag {
...
List<Meta> metas = new ArrayList<>();
{
metas.add(new Meta("mtag1"));
metas.add(new Meta("mtag2"));
metas.add(new Meta("mtag3"));
}
}
public class Meta {
...
List<Meta2> metas2 = new ArrayList<>();
{
metas2.add(new Meta2("2tag1"));
metas2.add(new Meta2("2tag2"));
metas2.add(new Meta2("2tag3"));
}
}
...
public class Meta2 {
List<Meta3> metas3 = new ArrayList<>();
{
metas3.add(new Meta3("3tag1"));
metas3.add(new Meta3("3tag2"));
metas3.add(new Meta3("3tag3"));
}
public class Meta3 {
...
Вы также можете искать по типу:
List<Employee> results = sortedQuery(queryableList, "firstName", typeOf("SalesEmployee"));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
Вышеприведенное находит всех сотрудников с простым именем класса SalesEmployee. Он также работает с полным именем класса, как в:
List<Employee> results = sortedQuery(queryableList, "firstName", typeOf("SalesEmployee"));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
Вы также можете искать по фактическому классу:
List<Employee> results = sortedQuery(queryableList, "firstName", instanceOf(SalesEmployee.class));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
Вы также можете запрашивать классы, которые реализуют определенные интерфейсы:
List<Employee> results = sortedQuery(queryableList, "firstName",
implementsInterface(Comparable.class));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
Вы также можете индексировать вложенные поля / свойства, и они могут быть полями коллекции или полями, не являющимися коллекциями свойств, так глубоко вложенными, как вам бы хотелось:
/* Create a repo, and decide what to index. */
RepoBuilder repoBuilder = RepoBuilder.getInstance();
/* Look at the nestedIndex. */
repoBuilder.primaryKey("id")
.searchIndex("firstName").searchIndex("lastName")
.searchIndex("salary").uniqueSearchIndex("empNum")
.nestedIndex("tags", "metas", "metas2", "name2");
Позже вы можете использовать nestedIndex для поиска.
List<Map<String, Object>> list = repo.query(
selects(select("tags", "metas", "metas2", "name2")),
eqNested("2tag1", "tags", "metas", "metas2", "name2"));
Безопасный способ использовать nestedIndex - это использовать eqNested. Вы можете использовать eq, gt, gte и т. Д., Если у вас есть такой индекс:
List<Map<String, Object>> list = repo.query(
selects(select("tags", "metas", "metas2", "name2")),
eq("tags.metas.metas2.name2", "2tag1"));
Вы также можете добавить поддержку подклассов
List<Employee> queryableList = $q(h_list, Employee.class, SalesEmployee.class,
HourlyEmployee.class);
List<Employee> results = sortedQuery(queryableList, "firstName", eq("commissionRate", 1));
assertEquals(1, results.size());
assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
results = sortedQuery(queryableList, "firstName", eq("weeklyHours", 40));
assertEquals(1, results.size());
assertEquals("HourlyEmployee", results.get(0).getClass().getSimpleName());
В хранилище данных есть аналогичная функция в методе DataRepoBuilder.build (...) для указания подклассов. Это позволяет без видимых полей запроса образовывать подклассы и классы в одном репо или поисковой коллекции.