Java Language Raccogli elementi di un flusso in una raccolta

Esempio

Raccogli con toList() e toSet()

Gli elementi di un Stream possono essere facilmente raccolti in un contenitore usando l'operazione Stream.collect :

System.out.println(Arrays
    .asList("apple", "banana", "pear", "kiwi", "orange")
    .stream()
    .filter(s -> s.contains("a"))
    .collect(Collectors.toList())
);
// prints: [apple, banana, pear, orange]

Altre istanze di raccolta, come un Set , possono essere create usando altri metodi built-in di Collectors . Ad esempio, Collectors.toSet() raccoglie gli elementi di un Stream in un Set .


Controllo esplicito sull'implementazione di List o Set

Secondo la documentazione di Collectors#toList() e Collectors#toSet() , non ci sono garanzie sul tipo, sulla mutevolezza, sulla serializzabilità o sulla sicurezza del thread List o del Set restituito.

Per il controllo esplicito dell'implementazione da restituire, è possibile utilizzare Collectors#toCollection(Supplier) , in cui il fornitore specificato restituisce una raccolta nuova e vuota.

// syntax with method reference
System.out.println(strings
        .stream()
        .filter(s -> s != null && s.length() <= 3)
        .collect(Collectors.toCollection(ArrayList::new))
);

// syntax with lambda
System.out.println(strings
        .stream()
        .filter(s -> s != null && s.length() <= 3)
        .collect(Collectors.toCollection(() -> new LinkedHashSet<>()))
);

Raccolta di elementi usando toMap

Il raccoglitore accumula elementi in una mappa, dove la chiave è l'id dello studente e il valore è il valore dello studente.

  List<Student> students = new ArrayList<Student>(); 
    students.add(new Student(1,"test1"));
    students.add(new Student(2,"test2"));
    students.add(new Student(3,"test3"));
    
    Map<Integer, String> IdToName = students.stream()
        .collect(Collectors.toMap(Student::getId, Student::getName));
    System.out.println(IdToName);

Produzione :

{1=test1, 2=test2, 3=test3}

Collectors.toMap ha un'altra implementazione Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction) . La funzione mergeFunction viene utilizzata principalmente per selezionare un nuovo valore o mantenere il vecchio valore se la chiave viene ripetuta quando si aggiunge un nuovo membro nella mappa da un elenco.

Spesso la mergeFunction assomiglia a: (s1, s2) -> s1 per mantenere il valore corrispondente alla chiave ripetuta, o (s1, s2) -> s2 per inserire un nuovo valore per la chiave ripetuta.

Raccolta di elementi per la mappa delle collezioni

Esempio: da ArrayList a Map <String, List <>>

Spesso è necessario creare una mappa dell'elenco da un elenco principale. Esempio: da uno studente della lista, abbiamo bisogno di fare una mappa dell'elenco delle materie per ogni studente.

    List<Student> list = new ArrayList<>();
    list.add(new Student("Davis", SUBJECT.MATH, 35.0));
    list.add(new Student("Davis", SUBJECT.SCIENCE, 12.9));
    list.add(new Student("Davis", SUBJECT.GEOGRAPHY, 37.0));

    list.add(new Student("Sascha", SUBJECT.ENGLISH, 85.0));
    list.add(new Student("Sascha", SUBJECT.MATH, 80.0));
    list.add(new Student("Sascha", SUBJECT.SCIENCE, 12.0));
    list.add(new Student("Sascha", SUBJECT.LITERATURE, 50.0));

    list.add(new Student("Robert", SUBJECT.LITERATURE, 12.0));

    Map<String, List<SUBJECT>> map = new HashMap<>();
    list.stream().forEach(s -> {
                map.computeIfAbsent(s.getName(), x -> new ArrayList<>()).add(s.getSubject());
            });
    System.out.println(map);

Produzione:

{ Robert=[LITERATURE], 
Sascha=[ENGLISH, MATH, SCIENCE, LITERATURE], 
Davis=[MATH, SCIENCE, GEOGRAPHY] }

Esempio: da ArrayList a Map <String, Map <>>

    List<Student> list = new ArrayList<>();
    list.add(new Student("Davis", SUBJECT.MATH, 1, 35.0));
    list.add(new Student("Davis", SUBJECT.SCIENCE, 2, 12.9));
    list.add(new Student("Davis", SUBJECT.MATH, 3, 37.0));
    list.add(new Student("Davis", SUBJECT.SCIENCE, 4, 37.0));

    list.add(new Student("Sascha", SUBJECT.ENGLISH, 5, 85.0));
    list.add(new Student("Sascha", SUBJECT.MATH, 1, 80.0));
    list.add(new Student("Sascha", SUBJECT.ENGLISH, 6, 12.0));
    list.add(new Student("Sascha", SUBJECT.MATH, 3, 50.0));

    list.add(new Student("Robert", SUBJECT.ENGLISH, 5, 12.0));

    Map<String, Map<SUBJECT, List<Double>>> map = new HashMap<>();

    list.stream().forEach(student -> {
        map.computeIfAbsent(student.getName(), s -> new HashMap<>())
                .computeIfAbsent(student.getSubject(), s -> new ArrayList<>())
                .add(student.getMarks());
    });

    System.out.println(map);

Produzione:

{ Robert={ENGLISH=[12.0]}, 
Sascha={MATH=[80.0, 50.0], ENGLISH=[85.0, 12.0]}, 
Davis={MATH=[35.0, 37.0], SCIENCE=[12.9, 37.0]} }

Cheat-sheet

Obbiettivo Codice
Raccogli a una List Collectors.toList()
Raccogli a un ArrayList con dimensioni pre-allocate Collectors.toCollection(() -> new ArrayList<>(size))
Raccogli per un Set Collectors.toSet()
Raccogli in un Set con prestazioni di iterazione migliori Collectors.toCollection(() -> new LinkedHashSet<>())
Raccogli in un Set<String> insensibile alle maiuscole / minuscole Set<String> Collectors.toCollection(() -> new TreeSet<>(String.CASE_INSENSITIVE_ORDER))
Raccogli a EnumSet<AnEnum> (miglior rendimento per enumerazioni) Collectors.toCollection(() -> EnumSet.noneOf(AnEnum.class))
Raccogli a una Map<K,V> con chiavi univoche Collectors.toMap(keyFunc,valFunc)
Mappare MyObject.getter () su MyObject univoco Collectors.toMap(MyObject::getter, Function.identity())
Mappare MyObject.getter () su più MyObjects Collectors.groupingBy(MyObject::getter)