Articles

filtrování kolekce Java seznamem

přehled

filtrování kolekce seznamem je běžný scénář obchodní logiky. Existuje spousta způsobů, jak toho dosáhnout. Některé však mohou vést k nedostatečným řešením, pokud nebudou provedeny správně.

v tomto tutoriálu porovnáme některé implementace filtrování a probereme jejich výhody a nevýhody.

pomocí pro-každou smyčku

začneme nejklasičtější syntaxí, pro-každou smyčku.

Pro tento a všechny ostatní příklady v tomto článku, budeme používat následující třídy:

public class Employee { private Integer employeeNumber; private String name; private Integer departmentId; //Standard constructor, getters and setters.}

Budeme také použít následující metody pro všechny příklady, pro jednoduchost:

private List<Employee> buildEmployeeList() { return Arrays.asList( new Employee(1, "Mike", 1), new Employee(2, "John", 1), new Employee(3, "Mary", 1), new Employee(4, "Joe", 2), new Employee(5, "Nicole", 2), new Employee(6, "Alice", 2), new Employee(7, "Bob", 3), new Employee(8, "Scarlett", 3));}private List<String> employeeNameFilter() { return Arrays.asList("Alice", "Mike", "Bob");}

Pro náš příklad, budeme filtrovat prvním seznamu Zaměstnanců na základě druhého seznamu jmen Zaměstnanců najít pouze Zaměstnanci s těmi konkrétními jmény.

nyní se podívejme na tradiční přístup-opakování obou seznamů hledajících shody:

@Testpublic void givenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingForEachLoop() { List<Employee> filteredList = new ArrayList<>(); List<Employee> originalList = buildEmployeeList(); List<String> nameFilter = employeeNameFilter(); for (Employee employee : originalList) { for (String name : nameFilter) { if (employee.getName().equals(name)) { filteredList.add(employee); // break; } } } assertThat(filteredList.size(), is(nameFilter.size()));}

Jedná se o jednoduchou syntaxi, ale je poměrně podrobná a ve skutečnosti docela neefektivní. Jednoduše řečeno, iteruje Kartézským součinem dvou sad, abychom získali naši odpověď.

i přidání přestávky k předčasnému ukončení bude stále iterovat ve stejném pořadí jako Kartézský produkt v průměrném případě.

Pokud zavoláme velikost seznamu zaměstnanců n, pak bude nameFilter na objednávce stejně velký, což nám dá klasifikaci O(n2).

pomocí streamů a seznamu#obsahuje

nyní refaktorujeme předchozí metodu pomocí lambdas pro zjednodušení syntaxe a zlepšení čitelnosti. Pojďme se také použít Seznam#obsahuje metody jako lambda filtr:

@Testpublic void givenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingLambda() { List<Employee> filteredList; List<Employee> originalList = buildEmployeeList(); List<String> nameFilter = employeeNameFilter(); filteredList = originalList.stream() .filter(employee -> nameFilter.contains(employee.getName())) .collect(Collectors.toList()); assertThat(filteredList.size(), is(nameFilter.size()));}

pomocí Stream API, čitelnost byla výrazně zlepšila, ale náš kód zůstává stejně neefektivní jako naše předchozí metoda, protože je to stále iterace přes Kartézský součin interně. Máme tedy stejnou klasifikaci O(n2).

pomocí streamů s HashSet

pro zlepšení výkonu musíme použít metodu HashSet # contains. Tato metoda se liší od Seznamu#obsahuje, protože to provádí hash kód vyhledávání, což nám dává konstantní úvazek počet operací:

@Testpublic void givenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingLambdaAndHashSet() { List<Employee> filteredList; List<Employee> originalList = buildEmployeeList(); Set<String> nameFilterSet = employeeNameFilter().stream().collect(Collectors.toSet()); filteredList = originalList.stream() .filter(employee -> nameFilterSet.contains(employee.getName())) .collect(Collectors.toList()); assertThat(filteredList.size(), is(nameFilterSet.size()));}

pomocí HashSet, náš kód účinnost výrazně zlepšila přitom nemá vliv na čitelnost. Protože HashSet#obsahuje běhy v konstantním čase, Vylepšili jsme klasifikaci na O (n).

závěr

v tomto rychlém tutoriálu jsme se naučili, jak filtrovat sbírku podle seznamu hodnot a nevýhod použití toho, co se může zdát jako nejjednodušší metoda.

musíme vždy zvážit efektivitu, protože náš kód by mohl skončit v obrovských datových sadách a problémy s výkonem by mohly mít v takových prostředích katastrofické důsledky.

veškerý kód uvedený v tomto článku je k dispozici na Githubu.

začněte s Jarní 5 a na Jaře Boot 2, a to prostřednictvím Naučit Jarní kurz:

>>