Как сделать карту карты с помощью коллекционеров - PullRequest
2 голосов
/ 26 мая 2019

Я заблокирован при использовании Функциональное программирование Java для Построение карты .В моем случае использования, это только пример, я хотел бы построить Map<Integer, Map<Integer, List<Person>>>, начиная с List<Person>.

. Первый шаг очень прост: я создаю первый Map<Integer, List<person>>.

.

Затем я хочу добавить новый уровень группировки , чтобы он создал новую карту: Map<Integer, Map<Integer, List<person>>>.

Суть в том, как правильно использовать все возможности Collectors (groupingBy,отображение, может что-то еще) добавить новый уровень группировки?Все мои попытки не увенчались успехом: компилятор всегда говорит мне, что все предоставленные мной функции не соответствуют ожидаемым.

Вкратце приведем код:

Сначала класс Person, а затем метод длябыстро составить список:

public class Person {

  private long id;
  private String firstName;
  private String lastName;
  private Date birthDate;
  private int alea;
  ...

Этот метод (расположенный в классе DataBuilder) просто создает список Person для предоставления данных в основной класс:

public List<Person> getPersons() {

        List<Person> persons = new ArrayList<>();
        Supplier<Person> person = Person::new;

        for (int cpt = 0; cpt < 10; cpt++) {
            Person p = person.get();
            p.setId(cpt);
            p.setFirstName("fn" + cpt);
            p.setLastName("ln" + cpt);
            p.setBirthDate(new Date(119, cpt, 10 + cpt));
            p.setAlea(cpt % 2);
            persons.add(p);
        }

        return persons;

    }

в основном,Построение первого уровня карты не является проблемой:

public static void main(String[] args) {

    DataBuilder db = new DataBuilder();

    List<Person> persons = db.getPersons();

    Map<Integer, List<Person>> mapAleaPerson = persons.stream()
                .collect(Collectors.groupingBy(Person::getAlea,
                        Collectors.mapping(p -> p, Collectors.toList())));

        mapAleaPerson.forEach((k,v) -> System.out.printf("%n%s contains %s%n", k, v));

Результат в консоли в порядке:

0 contains [Person [id=0, firstName=fn0, lastName=ln0, alea=0], Person [id=2, firstName=fn2, lastName=ln2, alea=0], Person [id=4, firstName=fn4, lastName=ln4, alea=0], Person [id=6, firstName=fn6, lastName=ln6, alea=0], Person [id=8, firstName=fn8, lastName=ln8, alea=0]]

1 contains [Person [id=1, firstName=fn1, lastName=ln1, alea=1], Person [id=3, firstName=fn3, lastName=ln3, alea=1], Person [id=5, firstName=fn5, lastName=ln5, alea=1], Person [id=7, firstName=fn7, lastName=ln7, alea=1], Person [id=9, firstName=fn9, lastName=ln9, alea=1]]

Теперь я хотел бы добавить новый уровень группировки.Я выбрал год рождения, это другое свойство объекта Person.

По сути, я пытался что-то вроде этого:

Map<Integer,Map<Integer,List<Person>>> mapByYearThenAleaPerson = persons.stream()
                .collect(Collectors.groupingBy(p -> p.getBirthDate().getYear(),
                        Collectors.toMap(Collectors.groupingBy(Person::getAlea,
                        Collectors.mapping(p -> p, Collectors.toList())))));

Приведение не помогает:

@SuppressWarnings("unchecked")
        Map<Integer,Map<Integer,List<Person>>> mapByYearThenAleaPerson = persons.stream()
                .collect(Collectors.groupingBy(p -> p.getBirthDate().getYear(),
                        Collectors.toMap((Function<Integer, List<Person>>) Collectors.groupingBy(Person::getAlea,
                        Collectors.mapping(p -> p, Collectors.toList())))));

Я также пытался использовать Collectors.mapping вместо Collectors.toMap:

Map<Integer,Map<Integer,List<Person>>> mapByYearThenAleaPerson = persons.stream()
                .collect(Collectors.groupingBy(p -> p.getBirthDate().getYear(),
                        Collectors.mapping((Function<Integer, List<Person>>) Collectors.groupingBy(Person::getAlea,
                        Collectors.mapping(p -> p, Collectors.toList())))));

Каждый раз, когда компилятор возвращает ошибку из-за несовместимого типа функции.

Единственный способ, которым ядобавлено, что новый уровень группировки был добавлен с помощью старого кода Java и добавления внешнего цикла и группировки данных в Map of Map.

Нецелесообразно встраивать FP в старую Java !!

Есть кто-нибудьИдея сделать все это с чистым FP?


После ответа @eran (это прекрасно работает) вот что я сделал и его результат:

Map<Integer, Map<Integer, List<Person>>> mapByYearThenAleaPerson = persons.stream().collect(
                Collectors.groupingBy(p -> p.getBirthDate().getYear() + 1900, Collectors.groupingBy(Person::getAlea)));


{2018={0=[Person [id=0, firstName=fn0, lastName=ln0, alea=0], Person [id=2, firstName=fn2, lastName=ln2, alea=0], Person [id=4, firstName=fn4, lastName=ln4, alea=0]], 1=[Person [id=1, firstName=fn1, lastName=ln1, alea=1], Person [id=3, firstName=fn3, lastName=ln3, alea=1]]}, 2019={0=[Person [id=6, firstName=fn6, lastName=ln6, alea=0], Person [id=8, firstName=fn8, lastName=ln8, alea=0]], 1=[Person [id=5, firstName=fn5, lastName=ln5, alea=1], Person [id=7, firstName=fn7, lastName=ln7, alea=1], Person [id=9, firstName=fn9, lastName=ln9, alea=1]]}}

Что такоедействительно удивительно, простота вложенного оператора groupingBy по сравнению со способом построения самой первой карты.

Почему нам больше не нужно указывать mapping и Collectors.toList()?

1 Ответ

4 голосов
/ 26 мая 2019

Использовать вложенные groupingBy:

Map<Integer,Map<Integer,List<Person>>> mapByYearThenAleaPerson = 
    persons.stream()
           .collect(Collectors.groupingBy(p -> p.getBirthDate().getYear(),
                                          Collectors.groupingBy(Person::getAlea)));
...