Filtrovanie zbierky Java podľa zoznamu
1. Prehľad
Filtrovanie a Zbierka od a Zoznam je bežný scenár obchodnej logiky. Existuje veľa spôsobov, ako to dosiahnuť. Niektoré však môžu viesť k nedostatočnému riešeniu, ak nebudú vykonané správne.
V tomto návode porovnáme niektoré implementácie filtrovania a prediskutujeme ich výhody a nevýhody.
2. Pomocou a Pre každý Slučka
Začneme najklasickejšou syntaxou, slučkou pre každú.
Pre tento a všetky ďalšie príklady v tomto článku použijeme nasledujúcu triedu:
public class Employee {private Integer employeeNumber; súkromné meno reťazca; private Integer departmentId; // Štandardný konštruktor, getre a setre. }
Pre všetky príklady použijeme pre jednoduchosť nasledujúce metódy:
private List buildEmployeeList () {return Arrays.asList (new Employee (1, "Mike", 1), new Employee (2, "John", 1), new Employee (3, "Mary", 1), new Employee ( 4, „Joe“, 2), nový zamestnanec (5, „Nicole“, 2), nový zamestnanec (6, „Alice“, 2), nový zamestnanec (7, „Bob“, 3), nový zamestnanec (8, „Scarlett“, 3)); } private List employeeNameFilter () {return Arrays.asList ("Alice", "Mike", "Bob"); }
Pre náš príklad vyfiltrujeme prvý zoznam Zamestnanci na základe druhého zoznamu s Zamestnanec mená nájsť iba Zamestnanci s tými konkrétnymi menami.
Teraz sa pozrime na tradičný prístup - opakovanie oboch zoznamov hľadaním zhôd:
@Test public void givenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingForEachLoop () {Listfiltrovaný zoznam = nový ArrayList (); Zoznam originalList = buildEmployeeList (); Názov zoznamuFilter = employeeNameFilter (); pre (Zamestnanec zamestnanec: originalList) {pre (Názov reťazca: nameFilter) {if (employee.getName (). equals (name)) {filtrList.add (zamestnanec); // prestávka; }}} assertThat (filtrovanýList.size (), je (nameFilter.size ())); }
Toto je jednoduchá syntax, ale je dosť podrobná a v skutočnosti dosť neefektívna. Jednoducho povedané iteruje cez karteziánsky súčin týchto dvoch množín aby sme dostali našu odpoveď.
Dokonca aj pridanie a prestávka predčasný odchod bude v priemernom prípade stále iterovať v rovnakom poradí ako karteziánsky súčin.
Ak nazveme veľkosť zoznamu zamestnancov n, potom menoFilter bude na objednávku rovnako veľká, čo nám dá an O (n2) klasifikácia.
3. Používanie streamov a Zoznam obsahuje
Teraz zreferujeme predchádzajúcu metódu pomocou lambdas na zjednodušenie syntaxe a zlepšenie čitateľnosti. Použime tiež Zoznam obsahuje metóda ako lambda filter:
@Test public void givenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingLambda () {Zoznam filtrovaný zoznam; Zoznam originalList = buildEmployeeList (); Názov zoznamuFilter = employeeNameFilter (); filterList = originalList.stream () .filter (zamestnanec -> nameFilter.contains (employee.getName ())) .collect (Collectors.toList ()); assertThat (filterList.size (), je (nameFilter.size ())); }
Použitím Stream API, čitateľnosť sa výrazne zlepšila, ale náš kód zostáva rovnako neefektívny ako naša predchádzajúca metóda, pretože je stále interne iteruje prostredníctvom karteziánskeho súčinu. Máme teda rovnaké O (n2) klasifikácia.
4. Používanie streamov s HashSet
Na zlepšenie výkonu musíme použiť HashSet # obsahuje metóda. Táto metóda sa líši od Zoznam obsahuje pretože vykonáva a hash kód vyhľadávanie, čo nám dáva konštantný počet operácií:
@Test public void givenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingLambdaAndHashSet () {Listfiltrovaný zoznam; Zoznam originalList = buildEmployeeList (); Nastaviť nameFilterSet = employeeNameFilter (). Stream (). Collect (Collectors.toSet ()); filtrovanýZoznam = originalList.stream () .filter (zamestnanec -> nameFilterSet.contains (employee.getName ())) .collect (Collectors.toList ()); assertThat (filterList.size (), je (nameFilterSet.size ())); }
Používaním HashSet, účinnosť nášho kódu sa výrazne zlepšila, pričom to neovplyvnilo čitateľnosť. Odkedy HashSet # obsahuje beží v konštantnom čase, vylepšili sme našu klasifikáciu na O (n).
5. Záver
V tomto rýchlom výučbe sme sa naučili, ako filtrovať a Zbierka od a Zoznam hodnôt a nevýhod použitia toho, čo sa môže javiť ako najpriamejšia metóda.
Musíme vždy brať do úvahy efektívnosť, pretože náš kód by mohol skončiť v obrovských množinách údajov a problémy s výkonom by mohli mať v takýchto prostrediach katastrofické následky.
Celý kód uvedený v tomto článku je k dispozícii na GitHub.