web-dev-qa-db-fra.com

Comment regrouper des éléments d'une liste <P> en une carte <K, Liste <V >> tout en conservant l'ordre?

J'ai une liste d'objets Google PlaceSummary tirés de l'API Google Adresses. Je souhaite les collecter et les regrouper en fonction de leur identifiant Google Place, mais également conserver l'ordre des éléments. Ce que je pensais travailler serait:

Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
            places.stream()
                  .collect(Collectors.groupingBy(
                          PlaceSummary::getPlaceId,
                          LinkedHashMap::new,
                          Collectors.mapping(PlaceSummary::getPlaceId, toList())
                  ));

Mais ça ne compilera même pas. Il semble que cela devrait être conforme à la documentation de l'API Java relative aux sur Collectors .

Auparavant j'avais ce code:

    Map<String, List<PlaceSummary>> placesGroupedByPlaceId = places.stream()
            .collect(Collectors.groupingBy(PlaceSummary::getPlaceId));

Cependant, la fonction .collect() standard de l'API Streams ne conserve pas l'ordre des éléments dans la version HashMap suivante (évidemment puisque HashMaps n'est pas ordonné). Je souhaite que la sortie soit une LinkedHashMap afin que la mappe soit triée selon l'ordre d'insertion de chaque compartiment.

Cependant, la solution que j'ai suggérée ne compile pas. Premièrement, il ne reconnaît pas le PlaceSummary::getPlaceId car il dit que ce n'est pas une fonction - même si je le sais. Deuxièmement, il est indiqué que je ne peux pas convertir LinkedHashMap<Object, Object> en M. M. étant censé être une collection générique, il devrait être accepté.

Comment convertir la liste en une LinkedHashMap à l'aide de l'API Java Stream? Y a-t-il une manière succincte de le faire? Si c'est trop difficile à comprendre, je peux simplement recourir aux méthodes de la vieille école, antérieures à Java 8.

J'ai remarqué qu'il y a une autre réponse à Stack Overflow lors de la conversion de List en LinkedHashMap , mais cela ne correspond pas à la solution que je souhaite, car je dois collecter "cet" objet que je parcourt spécifiquement.

18
James Murphy

Vous êtes vraiment proche de ce que vous voulez:

Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
            places.stream()
                  .collect(Collectors.groupingBy(
                          PlaceSummary::getPlaceId,
                          LinkedHashMap::new,
                          Collectors.mapping(Function.identity(), Collectors.toList())
                  ));

Dans la méthode Collectors.mapping, vous devez donner l'instance PlaceSummary et non l'ID de lieu. Dans le code ci-dessus, j'ai utilisé Function.identity(): ce collecteur est utilisé pour générer les valeurs. Nous devons donc accumuler les lieux eux-mêmes (et non leur ID).

Notez qu'il est possible d'écrire directement Collectors.toList() au lieu de Collectors.mapping(Function.identity(), Collectors.toList()).

Le code que vous avez jusqu'à présent ne compile pas car il crée en fait un Map<String, List<String>>: vous accumulez les identifiants pour chaque identifiant (ce qui est assez étrange).


Vous pouvez écrire ceci comme une méthode générique:

private static <K, V> Map<K, List<V>> groupByOrdered(List<V> list, Function<V, K> keyFunction) {
    return list.stream()
                .collect(Collectors.groupingBy(
                    keyFunction,
                    LinkedHashMap::new,
                    Collectors.toList()
                ));
}

et l'utiliser comme ça:

Map<String, List<PlaceSummary>> placesGroupedById = groupByOrdered(places, PlaceSummary::getPlaceId);
19
Tunaki

Je pense que vous avez un peu confus à propos du collecteur final. Il représente simplement ce qui doit être dans chaque valeur de carte. Il n’est pas nécessaire d’avoir un collecteur mapping secondaire, vous voulez simplement une liste des objets originaux.

    Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
          places.stream()
                .collect(Collectors.groupingBy(PlaceSummary::getPlaceId,
                                               LinkedHashMap::new,
                                               Collectors.toList()));
4
RealSkeptic

Si vous avez besoin d'un regroupement tout en maintenant l'ordre et appliquez une fonction (réduction), comptez peut-être que j'utilise quelque chose comme ça.

final Map<Integer,Long>map=stream.collect(Collectors.groupingBy(function
   ,LinkedHashMap::new
   ,Collectors.collectingAndThen(Collectors.counting(),Function.identity()))
 )
0
chiperortiz
/**
 * I have written this code more generic, if you want then you can group based on any * 
 * instance variable , id, name etc via passing method reference.
**/

class Student {
    private int id;
    private String name;
    public Student(int id, String name) {this.id = id;this.name = name;}
    /**
     * @return the id
     */
    public int getId() {return id;}
    /**
     * @param id
     *            the id to set
     */
    public void setId(int id) {this.id = id;}
    /**
     * @return the name
     */
    public String getName() {return name;}
    /**
     * @param name
     *            the name to set
     */
    public void setName(String name) {this.name = name;}
}

public class StudentMain {

    public static void main(String[] args) {

        List<Student> list = new ArrayList<>();
        list.add(new Student(1, "Amit"));
        list.add(new Student(2, "Sumit"));
        list.add(new Student(1, "Ram"));
        list.add(new Student(2, "Shyam"));
        list.add(new Student(3, "Amit"));
        list.add(new Student(4, "Pankaj"));

        Map<?, List<Student>> studentById = groupByStudentId(list,
                Student::getId);
        System.out.println(studentById);

       Map<?, List<Student>> studentByName = groupByStudentId(list,
                Student::getName);
        System.out.println(studentByName);

    }

    private static <K, V> Map<?, List<V>> groupByStudentId(List<V> list,
            Function<V, K> keyFunction) {
        return list.stream().collect(
                Collectors.groupingBy(keyFunction, HashMap::new,
                        Collectors.toList()));
    }
}
0
Ram Dular