Articles

filtrarea unei colecții Java după o listă

Prezentare generală

filtrarea unei colecții după o listă este un scenariu logic de afaceri comun. Există o mulțime de modalități de a realiza acest lucru. Cu toate acestea, unele pot duce la soluții slab performante dacă nu sunt realizate corect.

în acest tutorial, vom compara unele implementări de filtrare și vom discuta despre avantajele și dezavantajele acestora.

folosind o buclă pentru fiecare

vom începe cu cea mai clasică sintaxă, o buclă pentru fiecare.

pentru aceasta și pentru toate celelalte exemple din acest articol, vom folosi următoarea clasă:

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

vom folosi, de asemenea, următoarele metode pentru toate exemplele, de dragul simplității:

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

pentru exemplul nostru, vom filtra prima listă de angajați pe baza celei de-a doua liste cu numele angajaților pentru a găsi nume.

acum, să vedem abordarea tradițională-looping prin ambele liste în căutarea pentru meciuri:

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

aceasta este o sintaxă simplă, dar este destul de detaliată și de fapt destul de ineficientă. Pur și simplu, iterează prin produsul cartezian al celor două seturi pentru a obține răspunsul nostru.

chiar și adăugarea unei pauze pentru a ieși devreme va repeta în continuare în aceeași ordine ca un produs cartezian în cazul mediu.

dacă numim dimensiunea listei angajaților n, atunci nameFilter va fi la comandă la fel de mare, oferindu-ne o clasificare O(n2).

folosind fluxuri și lista#conține

vom refactor acum metoda anterioară prin utilizarea lambdas pentru a simplifica sintaxa și de a îmbunătăți lizibilitatea. Să folosim și metoda List # contains ca filtru lambda:

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

prin utilizarea API-ului Stream, lizibilitatea a fost mult îmbunătățită, dar codul nostru rămâne la fel de ineficient ca metoda noastră anterioară, deoarece încă iterează prin produsul cartezian intern. Astfel, avem aceeași clasificare O (n2).

utilizarea fluxurilor cu HashSet

pentru a îmbunătăți performanța, trebuie să folosim metoda HashSet#contains. Această metodă diferă de lista # conține deoarece efectuează o căutare a codului hash, oferindu-ne un număr constant de operații:

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

folosind HashSet, eficiența codului nostru s-a îmbunătățit foarte mult, fără a afecta lizibilitatea. Deoarece HashSet # conține rulează în timp constant, ne-am îmbunătățit clasificarea noastră la O(n).

concluzie

În acest tutorial rapid, am învățat cum să filtrăm o colecție după o listă de valori și dezavantajele utilizării a ceea ce poate părea cea mai simplă metodă.trebuie să luăm întotdeauna în considerare eficiența, deoarece codul nostru ar putea ajunge să ruleze în seturi uriașe de date, iar problemele de performanță ar putea avea consecințe catastrofale în astfel de medii.

tot codul prezentat în acest articol este disponibil peste pe GitHub.

începeți cu Spring 5 și Spring Boot 2, prin cursul Learn Spring:

>> verificați cursul