2015-05-19 4 views
8

In un test di unità, voglio verificare che due elenchi contengano gli stessi elementi. L'elenco da testare è costituito da un elenco di oggetti Person, in cui viene estratto un campo di tipo String. L'altro elenco contiene letterali String.Java 8: un modo più efficiente di confrontare elenchi di tipi diversi?

Si trova spesso il seguente frammento di codice per eseguire questa operazione (vedi this answer):

List<Person> people = getPeopleFromDatabasePseudoMethod(); 
List<String> expectedValues = Arrays.asList("john", "joe", "bill"); 

assertTrue(people.stream().map(person -> person.getName()).collect(Collectors.toList()).containsAll(expectedValues)); 

La classe Person è defiend come:

public class Person { 

    private String name; 
    private int age; 

    public String getName() { 
     return name; 
    } 

    public void setName(final String name) { 
     this.name = name; 
    } 

    // other getters and setters 
} 

Nell'esempio di cui sopra, l'elenco delle persone (o persone) viene trasformato in un elenco di stringhe utilizzando le tecniche di Java 8 e la comparazione viene eseguita alla vecchia maniera.

Ora mi chiedo, se c'è un modo più diretto o più efficiente di fare il confronto usando altre dichiarazioni di Java 8, ad esempio allMatch() o qualche Predicate<T> o altro.

+0

Perché non utilizzare invece i matchers [Hamcrest] (http://hamcrest.org/)? – Makoto

+0

Vuoi veramente controllare 'containsAll'? Quindi l'ordine e la dimensione degli elenchi possono essere diversi? –

+0

@TagirValeev Voglio assicurarmi che la 'Lista persone' contenga tutte le persone che ho specificato nell'elenco statico confrontando i loro nomi univoci. Quindi l'ordine delle liste potrebbe essere diverso ma per affermazioni riuscite, la dimensione delle liste dovrebbe essere la stessa. –

risposta

13

Il codice della tua domanda non riflette ciò che descrivi nei commenti. Nei commenti dici che tutti i nomi dovrebbero essere presenti e le dimensioni devono corrispondere, in altre parole, solo l'ordine potrebbe essere diverso.

Il codice è

List<Person> people = getPeopleFromDatabasePseudoMethod(); 
List<String> expectedValues = Arrays.asList("john", "joe", "bill"); 

assertTrue(people.stream().map(person -> person.getName()) 
       .collect(Collectors.toList()).containsAll(expectedValues)); 

che manca di un test per la dimensione di people, in altre parole permette duplicati. Inoltre, utilizzando containsAll combinando due List s in molto inefficiente. E 'molto meglio se si utilizza un tipo di collezione che riflette te intenzione, cioè non ha duplicati, non si preoccupa di un ordine e ha una ricerca efficiente:

Set<String> expectedNames=new HashSet<>(expectedValues); 
assertTrue(people.stream().map(Person::getName) 
       .collect(Collectors.toSet()).equals(expectedNames)); 

con questa soluzione non è necessario per verificare la dimensione manuale, è già implicito che i set hanno la stessa dimensione se corrispondono, solo l'ordine può essere diverso.

c'è una soluzione che non richiede la raccolta i nomi di persons:

Set<String> expectedNames=new HashSet<>(expectedValues); 
assertTrue(people.stream().allMatch(p->expectedNames.remove(p.getName())) 
      && expectedNames.isEmpty()); 

ma funziona solo se expectedNames è un insieme temporaneo creato dalla collezione statica di nomi attesi. Non appena decidi di sostituire la tua raccolta statica con uno Set, la prima soluzione non richiede un set temporaneo e quest'ultimo non ha alcun vantaggio su di esso.

+0

Ammetto che ho anche un assegno per le dimensioni degli elenchi ma non l'ho scritto nel codice della domanda. E hai ragione a usare set anziché elenchi. La tua seconda soluzione è quello che mi aspettavo di ottenere. Ma come hai detto, non c'è alcun vantaggio sulla tua prima soluzione, io scelgo questa. Ma ora sto usando 'assertEquals' invece di' assertTrue'. –

4

Se il numero di elementi deve essere lo stesso, allora sarebbe meglio confrontare set:

List<Person> people = getPeopleFromDatabasePseudoMethod(); 
Set<String> expectedValues = new HashSet<>(Arrays.asList("john", "joe", "bill")); 
assertEquals(expectedValues, 
    people.stream().map(Person::getName).collect(Collectors.toSet())); 

Il metodo equals per i set correttamente attuate dovrebbe essere in grado di confrontare diversi tipi di set: esso diventa subito controlli se il contenuto è lo stesso (ignorando l'ordine, ovviamente).

L'utilizzo di assertEquals è più comodo in quanto in caso di errore un messaggio di errore conterrà la rappresentazione in stringa del set.

+0

Sì, l'utilizzo di set ha assolutamente senso. –