Подтвердить что ты не робот

Как вы группируете элементы в списке <p>на карту <K, List <V>> при сохранении порядка?

У меня есть список объектов Google PlaceSummary, взятых из API Google Адресов. Я хотел бы собрать и сгруппировать их по их идентификатору Google Place, но также сохранить порядок элементов. То, что я думал, будет работать:

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

Но он даже не будет компилироваться. Похоже, что в соответствии с документация Java API на коллекторах.

Раньше у меня был этот код:

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

Однако стандарт .collect() в API потоков не сохраняет порядок элементов в последующем HashMap (очевидно, поскольку HashMap неупорядочен). Я хочу, чтобы результат был LinkedHashMap, так что карта сортируется по порядку вставки каждого ведра.

Однако решение, которое я предложил, не компилируется. Во-первых, он не распознает PlaceSummary::getPlaceId, поскольку он говорит, что это не функция, хотя я знаю, что это так. Во-вторых, он говорит, что я не могу преобразовать LinkedHashMap<Object, Object> в M. M, как предполагается, является общей коллекцией, поэтому ее следует принять.

Как преобразовать List в LinkedHashMap с помощью Java Stream API? Есть ли лаконичный способ сделать это? Если это слишком сложно понять, я могу просто прибегнуть к старым школьным пред-Java 8 методам.

Я заметил, что есть другой ответ при конвертировании списка в LinkedHashMap, но это не имеет решение, которое требуется, поскольку мне нужно собрать 'this' объект, который я специально перебираю.

4b9b3361

Ответ 1

Вы действительно близки к тому, что хотите:

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

В методе Collectors.mapping вам нужно указать экземпляр PlaceSummary, а не идентификатор места. В приведенном выше коде я использовал Function.identity(): этот сборщик используется для построения значений, поэтому нам нужно накапливать сами места (а не их идентификатор).

Обратите внимание, что можно писать непосредственно Collectors.toList() вместо Collectors.mapping(Function.identity(), Collectors.toList()).

Код, который вы до сих пор не компилируете, потому что он фактически создает Map<String, List<String>>: вы накапливаете идентификаторы для каждого ID (что довольно странно).


Вы можете написать это как общий метод:

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

и используйте его следующим образом:

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

Ответ 2

Я думаю, вы немного смутили окончательный коллекционер. Он просто представляет собой то, что должно быть в каждой стоимости карты. Нет необходимости иметь вторичный сборщик mapping, поскольку вам просто нужен список исходных объектов.

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