Список <object> на карту <String, Map<String,List<Object> >>

У меня есть List<Person> где Person выглядит следующим образом.

class Person {

    String personId;
    LocalDate date;
    String type;

    // getters & setters

}

Я пытаюсь преобразовать это в List<Person> в Map<String, Map<LocalDate,List<Person>>> где внешний ключ карты - это personId а внутренний ключ карты - date и я не мог понять, как достичь этого, До сих пор пробовали что-то вроде ниже. Открытые для Java 8 решения.

Map<String,Map<LocalDate,List<Person>>> outerMap = new HashMap<>();
Map<LocalDate,List<Person>> innerMap = new HashMap<>();

for(Person p : list) {
    List<Person> innerList = new ArrayList<>();
    innerList.add(p);
    innerMap.put(p.getDate(), innerList);
    outerMap.put(p.getPersonId(), innerMap);
}

Ответ 1

list.stream()
    .collect(Collectors.groupingBy(
        Person::getPersonId,
        Collectors.groupingBy(
            Person::getDate
)));

Ответ 2

Этот ответ показывает, как сделать это с потоками, а другой - как сделать это с помощью традиционного итеративного подхода. Вот еще один способ:

Map<String, Map<LocalDate, List<Person>>> outerMap = new HashMap<>();
list.forEach(p -> outerMap
        .computeIfAbsent(p.getPersonId(), k -> new HashMap<>()) // returns innerMap
        .computeIfAbsent(p.getDate(), k -> new ArrayList<>())   // returns innerList
    .add(p)); // adds Person to innerList

Это использует Map.computeIfAbsent чтобы создать новый innerMap и поместить его в outerMap если она отсутствует (ключ - personId), а также создать новый innerList и поместить его в innerMap если нет (ключ - date). Наконец, Person добавляется во innerList.

Ответ 3

Ваша for цикла организация должна быть изменена таким образом, что он пытается получать существующий List<Person> от вложенной карты, прежде чем приступить к созданию нового списка. Это двухэтапный процесс:

Map<String,Map<LocalDate,List<Person>>> outerMap = new HashMap<>();
for(Person p : list) {
    Map<LocalDate,List<Person>> innerMap = outerMap.get(p.getPersonId());
    if (innerMap == null) {
        innerMap = new HashMap<>();
        outerMap.put(p.getPersonId(), innerMap);
    }
    List<Person> innerList = innerMap.get(p.getDate());
    if (innerList == null) {
        innerList = new ArrayList<>();
        innerMap.put(p.getDate(), innerList);
    }
    innerList.add(p);
}