Een Java-verzameling filteren op een lijst

1. Overzicht

Filteren van een Verzameling door a Lijst is een veelvoorkomend scenario voor bedrijfslogica. Er zijn tal van manieren om dit te bereiken. Sommige kunnen echter leiden tot slecht presterende oplossingen als ze niet correct worden uitgevoerd.

In deze tutorial we zullen enkele filterimplementaties vergelijken en hun voor- en nadelen bespreken.

2. Met behulp van een Voor elk Lus

We beginnen met de meest klassieke syntaxis, een for-each-lus.

Voor deze en alle andere voorbeelden in dit artikel gebruiken we de volgende klasse:

openbare klasse Employee {private Integer employeeNumber; private String naam; private Integer departmentId; // Standaard constructeur, getters en setters. }

Voor alle voorbeelden zullen we voor de eenvoud ook de volgende methoden gebruiken:

private List buildEmployeeList () {return Arrays.asList (nieuwe werknemer (1, "Mike", 1), nieuwe werknemer (2, "John", 1), nieuwe werknemer (3, "Mary", 1), nieuwe werknemer ( 4, "Joe", 2), nieuwe werknemer (5, "Nicole", 2), nieuwe werknemer (6, "Alice", 2), nieuwe werknemer (7, "Bob", 3), nieuwe werknemer (8, "Scarlett", 3)); } privélijst employeeNameFilter () {return Arrays.asList ("Alice", "Mike", "Bob"); }

Voor ons voorbeeld filteren we de eerste lijst met Werknemers op basis van de tweede lijst met Werknemer namen om alleen de Werknemers met die specifieke namen.

Laten we nu eens kijken naar de traditionele benadering - door beide lijsten bladeren op zoek naar overeenkomsten:

@Test openbare ongeldig gegevenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingForEachLoop () {Lijst gefilterdeList = nieuwe ArrayList (); Lijst originalList = buildEmployeeList (); Lijst nameFilter = employeeNameFilter (); voor (Werknemer werknemer: origineleLijst) {voor (Tekenreeksnaam: naamFilter) {if (werknemer.getNaam (). gelijk aan (naam)) {filterList.add (werknemer); // breken; }}} assertThat (filterList.size (), is (nameFilter.size ())); }

Dit is een eenvoudige syntaxis, maar het is nogal uitgebreid en eigenlijk vrij inefficiënt. Simpel gezegd, het itereert door het cartesiaanse product van de twee sets om ons antwoord te krijgen.

Zelfs het toevoegen van een breken vroegtijdig afsluiten zal in het gemiddelde geval nog steeds dezelfde volgorde herhalen als een Cartesiaans product.

Als we de omvang van de werknemerslijst noemen n, dan naamFilter zal op de bestelling net zo groot zijn, waardoor we een O (n2) classificatie.

3. Gebruik van streams en Lijst # bevat

We zullen nu de vorige methode refactoren door lambdas te gebruiken om de syntaxis te vereenvoudigen en de leesbaarheid te verbeteren. Laten we ook de Lijst # bevat methode als de lambda filter:

@Test openbare leegte gegevenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingLambda () {Lijst gefilterdeList; Lijst originalList = buildEmployeeList (); Lijst nameFilter = employeeNameFilter (); filterList = originalList.stream () .filter (werknemer -> naamFilter.contains (werknemer.getNaam ())) .collect (Collectors.toList ()); assertThat (filterList.size (), is (nameFilter.size ())); }

Door de Stream APIis de leesbaarheid aanzienlijk verbeterd, maar onze code blijft net zo inefficiënt als onze vorige methode omdat het intern itererend door het Cartesiaanse product. We hebben dus hetzelfde O (n2) classificatie.

4. Streams gebruiken met HashSet

Om de prestaties te verbeteren, moeten we de HashSet # bevat methode. Deze methode verschilt van Lijst # bevat omdat het een hash-code lookup, wat ons een constant aantal bewerkingen geeft:

@Test openbare leegte gegevenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingLambdaAndHashSet () {Lijst gefilterdeList; Lijst originalList = buildEmployeeList (); Stel nameFilterSet = employeeNameFilter (). Stream (). Collect (Collectors.toSet ()); filterList = originalList.stream () .filter (werknemer -> naamFilterSet.contains (werknemer.getNaam ())) .collect (Collectors.toList ()); assertThat (filterList.size (), is (nameFilterSet.size ())); }

Door het gebruiken van HashSet, onze code-efficiëntie is enorm verbeterd zonder dat dit ten koste gaat van de leesbaarheid. Sinds HashSet # bevat loopt in constante tijd, hebben we onze classificatie verbeterd naar Aan).

5. Conclusie

In deze korte handleiding hebben we geleerd hoe u een Verzameling door a Lijst waarden en de nadelen van het gebruik van wat misschien de meest eenvoudige methode lijkt.

We moeten altijd rekening houden met efficiëntie, want onze code kan in enorme gegevenssets terechtkomen en prestatieproblemen kunnen in dergelijke omgevingen catastrofale gevolgen hebben.

Alle code die in dit artikel wordt gepresenteerd, is beschikbaar op GitHub.