Вложенный EnumMap с использованием потоков - PullRequest
1 голос
/ 09 апреля 2020

Я начинаю с Java потоков и во время лекции по книге эффективного программирования я нашел этот фрагмент кода:

    public enum Phase {
  SOLID, LIQUID, GAS;

  public enum Transition {
    MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),
    BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),
    SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID);

    private final Phase from;
    private final Phase to;

    Transition(Phase from, Phase to) {
        this.from = from;
        this.to = to;
    }
    private static final Map<Phase, Map<Phase, Transition>>
        m = Stream.of(values()).collect(groupingBy(t -> t.from,
            () -> new EnumMap<>(Phase.class),
            toMap(t -> t.to, t -> t,
                (x, y) -> y, () -> new EnumMap<>(Phase.class))));

    public static Transition from(Phase from, Phase to) {
        return m.get(from).get(to);
    }
  }
}

И я не могу понять, как создается эта EnumMap. Самое трудное для меня понять:

toMap(t -> t.to, t -> t,
                (x, y) -> y, () -> new EnumMap<>(Phase.class))));

Кто-нибудь может мне это объяснить?

Ответы [ 2 ]

1 голос
/ 09 апреля 2020

Что делает код: у вас есть поток элементов, и вы хотите сгруппировать их по начальной фазе. Здесь автор решил использовать сложную группу по операции, которая будет также выполнять пост-преобразование собранных данных. Именно здесь появляется функция toMap. Как только сборщик groupingBy начинает буферизацию элементов, он делегирует сборщику mapTo для создания пользовательских записей для каждой группы. Здесь автор использует toMap для индексации фаз по их target состоянию: до .

Итак, давайте разберем его:

Stream.of(values())
   // Queries a result
   .collect(
       // Elements must be grouped using a key, extracted from each streamed value
       groupingBy(
           // Specify how to extract the grouping key
           t -> t.from,
            // Here, the author specifies himself how to create the group map,
            // to be sure it will use a custom enum-optimized key management.
            () -> new EnumMap<>(Phase.class),
            // Nest another collector, to transform the list of values 
            // of each group into something else. Note that toMap does not 
            // buffer a collection of values for a key. It is designed to 
            // map a key to each value. But, it allows user to merge  
            // merge conflicting values if needed.
            toMap(
                // Same than groupby, we specify how to extract key from value. 
                t -> t.to, 
                // Here, specify that the value associated to the key is the 
                // object we've extracted key from. Here, wwe could have changed
                // it to something else (a text representation, for example).
                t -> t,
                // toMap will bind one key to one value. We have to solve 
                // conflict ourself. Here, author decided to drop arbitrarily 
                // one of the values.
                (x, y) -> y,
                // As for groupby operator, author forces a map implementation 
                // dedicated to enum keys.
                () -> new EnumMap<>(Phase.class))));

Наконец Официальная документация по сбору потоков дает достаточно объяснений.

Я не уверен, достаточно ли моего объяснения, вы можете высказать замечания в комментариях.

0 голосов
/ 09 апреля 2020

Здесь создается Карта, которая предоставляет метод Перехода при переходе из одного состояния в другое. Если бы это было сделано без lambdas или streams, это могло бы выглядеть следующим образом:

    public static Map<Phase, Map<Phase, Transition>> init() {
        Map<Phase, Map<Phase,Transition>> outerMap = new EnumMap<>(Phase.class);
        for (Transition v : Transition.values()) {
            Map<Phase, Transition> innerMap = outerMap.get(v.from);
            if (innerMap == null) {
                innerMap = new EnumMap<>(Phase.class);
                outerMap.put(v.from, innerMap);
            }
            innerMap.put(v.to, v);
        }
        return outerMap;
    }

Далее следует объяснение потока.

Начните с stream из Transition значения перечисления. Во всех случаях здесь t является типом Transition.

        public static Map<Phase, Map<Phase, Transition>> map = Stream
                .of(values())

Затем они собираются в EnumMap с использованием метода Collectors.groupingBy(). Все ключи здесь будут взяты из поля Transition.from. Значение для этой внешней карты будет EnumMap

                .collect(Collectors.groupingBy(t -> t.from,
                        () -> new EnumMap<>(Phase.class),

Далее, нижестоящий коллектор теперь заполняет эту вновь созданную карту другой картой. Внутренняя карта будет использовать следующее.

  • Поле Transition.to для ключей - t.to
  • Результирующий тип перехода для значений, который равен просто t
                        Collectors.toMap(t -> t.to, t -> t,

Этот следующий оператор является просто функцией слияния, и он не используется в данном конкретном приложении, поэтому просто выбрал значение.

                                (x, y) -> x,

Наконец, укажите Supplier для тип innerMap для создания.

                               () -> new EnumMap<>(Phase.class))));

Когда Transition.from() вызывается с использованием аргументов из перечисления Phase, на карту ссылаются для отображения процесса Transition.

...