Jak odpytywać zbiory obiektów w Javie (Criteria/SQL-like)?

Załóżmy, że masz kolekcję kilkuset obiektów w pamięci i musisz odpytywać tę listę, aby zwrócić obiekty pasujące do niektórych SQL lub kryteriów, takich jak query. Na przykład możesz mieć listę obiektów samochodowych i chcesz zwrócić wszystkie samochody wyprodukowane w latach 60., z tablicą rejestracyjną zaczynającą się od AZ, uporządkowaną według nazwy modelu samochodu.

Wiem o JoSQL , Czy ktoś z tego korzystał lub ma jakieś doświadczenie z innymi/domowymi rozwiązaniami?

Author: stian, 2008-09-18

7 answers

Użyłem Apache Commons JXPath w aplikacji produkcyjnej. Pozwala na stosowanie wyrażeń XPath do Wykresów obiektów w Javie.

 12
Author: Eric Weilnau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 15:29:50

Filtrowanie jest jednym ze sposobów, jak to opisano w innych odpowiedziach.

Filtrowanie nie jest jednak skalowalne. Na powierzchni złożoność czasu wydaje się być O (N ) (tzn. nie jest skalowalna, jeśli liczba obiektów w kolekcji będzie rosnąć), ale w rzeczywistości ponieważ jeden lub więcej testów należy zastosować do każdego obiektu w zależności od zapytania, złożoność czasu dokładniej to o (n T) gdzie t jest liczbą testów, które należy zastosować do każdego obiektu. obiekt.

Tak więc wydajność będzie spadać wraz z dodaniem dodatkowych obiektów do kolekcji, i / lub {[11] } wraz ze wzrostem liczby testów w zapytaniu.

Jest inny sposób, aby to zrobić, używając indeksowania i teorii mnogości.

Jednym z podejść jest budowanie indeksów na polach wewnątrz obiektów przechowywanych w Twojej kolekcji i które następnie przetestujesz w zapytaniu.

Powiedzmy, że masz zbiór Car obiektów i każdy Car obiekt posiada pole color. Powiedz, że Twoje zapytanie jest odpowiednikiem " SELECT * FROM cars WHERE Car.color = 'blue'". Możesz zbudować indeks na Car.color, który w zasadzie wyglądałby tak:

'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'}}

Następnie, biorąc pod uwagę zapytanie WHERE Car.color = 'blue', zestaw niebieskich samochodów można pobrać w O(1) złożoność czasowa. Jeśli w Twoim zapytaniu były dodatkowe testy, możesz przetestować każdy samochód w tym zestawie kandydatów , aby sprawdzić, czy pasował do pozostałych testów w Twoim zapytaniu. Ponieważ zestaw kandydatów prawdopodobnie będzie znacznie złożoność czasu mniejsza niż cała kolekcja jest mniejsza niż O (n ) (w sensie inżynierskim, patrz komentarze poniżej). Wydajność nie ulega degradacji tak bardzo, gdy do kolekcji dodawane są dodatkowe obiekty. Ale to nadal nie jest idealne, Czytaj dalej.

Innym podejściem jest to, co nazwałbym stały indeks zapytań. Aby wyjaśnić: w konwencjonalnej iteracji i filtrowaniu, kolekcja jest iteracją i każdy obiekt jest testowany aby sprawdzić, czy pasuje do zapytania. Filtrowanie jest więc jak uruchamianie zapytania nad kolekcją. Stały indeks zapytań byłby odwrotnie, gdzie kolekcja jest uruchamiana nad zapytaniem, ale tylko raz dla każdego obiektu w kolekcji, nawet jeśli kolekcja może być zapytana dowolną liczbę razy.

A standing query index {[11] } byłoby jak zarejestrowanie zapytania za pomocą pewnego rodzaju intelligent collection, takiego, że obiekty as są dodawane i usuwane z kolekcja, kolekcja automatycznie testuje każdy obiekt w stosunku do wszystkich stałych zapytań, które zostały w nim zarejestrowane. Jeśli obiekt pasuje do stałego zapytania, kolekcja może dodać/usunąć go do / z zestawu dedykowanego do przechowywania obiektów pasujących do tego zapytania. Następnie obiekty pasujące do dowolnego z zarejestrowanych zapytań mogą być pobierane w O(1) złożoność czasowa.

Powyższe informacje pochodzą z CQEngine (Collection Query Engine) . To zasadniczo jest to silnik zapytań NoSQL do pobierania obiektów ze zbiorów Javy za pomocą zapytań podobnych do SQL, bez konieczności iteracji przez kolekcję. Jest zbudowany wokół powyższych pomysłów, a także kilka innych. Zastrzeżenie: jestem autorem. Jest open source i w Maven central. Jeśli uważasz, że to pomocne proszę o upvote tej odpowiedzi!

 26
Author: npgall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-06 23:38:51

Tak, Wiem, że to stary post, ale technologie pojawiają się codziennie i odpowiedź zmieni się z czasem.

Myślę, że jest to dobry problem, aby rozwiązać go z LambdaJ. Znajdziesz go tutaj: http://code.google.com/p/lambdaj/

Oto przykład:

Szukaj aktywnych klientów / / (Wersja Iterable)

List<Customer> activeCustomers = new ArrayList<Customer>();  
for (Customer customer : customers) {  
  if (customer.isActive()) {  
    activeCusomers.add(customer);  
  }  
}  

Wersja LambdaJ

List<Customer> activeCustomers = select(customers, 
                                        having(on(Customer.class).isActive()));  

Oczywiście, posiadanie tego rodzaju piękna wpływa na występ (trochę... średnio 2 razy), ale czy można znaleźć bardziej czytelny kod?

Posiada wiele wielu funkcji, innym przykładem może być sortowanie:

Sort Iterative

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());
        }
}); 

Sortuj z lambda

List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge()); 

Update: po Javie 8 można używać gotowych wyrażeń lambda, takich jak:

List<Customer> activeCustomers = customers.stream()
                                          .filter(Customer::isActive)
                                          .collect(Collectors.toList());                                      
 6
Author: Federico Piazza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-18 22:25:51

Kontynuując temat Comparator, Możesz również rzucić okiem na API Google Collections . W szczególności mają interfejs o nazwie predykat, który pełni podobną rolę do Comparator, ponieważ jest to prosty interfejs, który może być używany przez metodę filtrowania, jak zestawy .filtr . Obejmują one całą masę złożonych implementacji predykatów, do zrobienia IDs, or, itp.

W zależności od wielkości zestawu danych, bardziej sensowne może być użycie tego podejście niż podejście SQL lub zewnętrzne relacyjne bazy danych.

 3
Author: joev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 16:10:54

Jeśli potrzebujesz jednego konkretnego dopasowania, możesz użyć klasy implementation Comparator, a następnie utworzyć samodzielny obiekt ze wszystkimi zahaszowanymi polami i użyć go do zwrócenia indeksu dopasowania. Jeśli chcesz znaleźć więcej niż jeden (potencjalnie) obiekt w kolekcji, musisz zwrócić się do biblioteki takiej jak JoSQL(która sprawdziła się dobrze w banalnych przypadkach, do których go używałem).

Ogólnie rzecz biorąc, mam tendencję do osadzania Derby w nawet moich małych aplikacjach, używam Hibernate adnotacje do zdefiniuj moje klasy modelu i pozwól Hibernate zająć się schematami buforowania, aby wszystko było szybkie.

 2
Author: Steve Moyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 15:18:30

Użyłbym komparatora, który zajmuje szereg lat i wzór tablic rejestracyjnych jako parametry wejściowe. Następnie po prostu przejrzyj swoją kolekcję i skopiuj obiekty, które pasują. Prawdopodobnie skończysz tworząc cały pakiet niestandardowych komparatorów z takim podejściem.

 1
Author: Bill the Lizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 15:17:33

Opcja Comparator nie jest zła, zwłaszcza jeśli używasz anonimowych klas( aby nie tworzyć nadmiarowych klas w projekcie), ale w końcu, gdy spojrzysz na przepływ porównań, to prawie tak jak zapętlenie całej kolekcji samodzielnie, określając dokładnie warunki dopasowania elementów:

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

Potem jest sortowanie... to może być ból w tyłku, ale na szczęście jest klasa Collections i jej metody sort, z których jedna otrzymuje Comparator...

 0
Author: Yuval,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 15:45:33