Question

À partir d'un java.util.function.BiFunction qui cartes une paire de Enums dans une valeur, je veux construire une EnumMap qui reflète la cartographie.

Par exemple, je E1 et E2 être enum types et T tout type:

 BiFunction<E1,E2, T> theBiFunction = //...anything

 EnumMap<E1,EnumMap<E2,T>> theMap = 
    buildTheMap(                     // <--  this is where the magic happens
                E1.values(), 
                E2.values(),
                theBiFunction);

Compte tenu de toutes les paires de valeurs de type E1 et E2

E1 e1 = //any valid value...
E2 e2 = //any valid value....

les deux valeurs ci-dessous doivent être égaux:

T valueFromTheMaps = theMap.get(e1).get(e2);
T valueFromTheFunction = theBiFunction.apply(e1,e2);

boolean alwaysTrue = valueFromTheMaps.equals(valueFromTheFunction);

Quelle est la meilleure (plus élégant, efficace, etc...) de la mise en œuvre de la méthode où l' "la magie"a lieu?

Était-ce utile?

La solution

Vous obtenez une solution élégante, si vous allez à une solution générique et de le décomposer.Tout d'abord, de mettre en œuvre une fonction générique qui crée un EnumMap d'un Function, puis mettre en œuvre le imbriquée cartographie d'un BiFunction à l'aide de la première fonction combinée avec elle-même:

static <T,E extends Enum<E>>
  EnumMap<E,T> funcToMap(Function<E,T> f, Class<E> t, E... values) {
    return Stream.of(values)
      .collect(Collectors.toMap(Function.identity(), f, (x,y)->x, ()-> new EnumMap<>(t)));
}
static <T,E1 extends Enum<E1>,E2 extends Enum<E2>>
  EnumMap<E1,EnumMap<E2,T>> biFuncToMap(
  BiFunction<E1,E2,T> f, Class<E1> t1, Class<E2> t2, E1[] values1, E2[] values2){

  return funcToMap(e1->funcToMap(e2->f.apply(e1, e2), t2, values2), t1, values1);
}

Voici un petit cas de test:

enum Fruit {
    APPLE, PEAR
}
enum Color {
    RED, GREED, YELLOW
}

EnumMap<Fruit, EnumMap<Color, String>> result
  =biFuncToMap((a,b)->b+" "+a,
     Fruit.class, Color.class, Fruit.values(), Color.values());
System.out.println(result);

{APPLE={RED=RED APPLE, GREED=GREED APPLE, YELLOW=YELLOW APPLE}, PEAR={RED=RED PEAR, GREED=GREED PEAR, YELLOW=YELLOW PEAR}}

Bien sûr, à l'aide de la solution générique vous pouvez construite méthodes pour le béton enum les types qui ne nécessitent pas l' Class paramètre(s)...


Cela devrait fonctionner avec un courant parallèle si la (Bi)Function est thread-safe.

Autres conseils

Comme une base de référence pour comparaison, voici la version classique:

<T> EnumMap<E1,EnumMap<E2,T>> buildTheMap(E1[] e1values,
                                          E2[] e2values,
                                          BiFunction<E1,E2,T> f) {
    EnumMap<E1,EnumMap<E2,T>> outer = new EnumMap<>(E1.class);
    for (E1 e1 : e1values) {
        EnumMap<E2,T> inner = new EnumMap<>(E2.class);
        for (E2 e2 : e2values) {
            inner.put(e2, f.apply(e1, e2));
        }
        outer.put(e1, inner);
    }
    return outer;
}

Maintenant, voici une version qui utilise imbriqués trois-arg formes de la collect() flux d'exploitation du terminal:

<T> EnumMap<E1,EnumMap<E2,T>> buildTheMap(E1[] e1values,
                                          E2[] e2values,
                                          BiFunction<E1,E2,T> f) {
    return
        Stream.of(e1values)
              .collect(() -> new EnumMap<>(E1.class),
                       (map, e1) -> map.put(e1, Stream.of(e2values)
                                                      .collect(() -> new EnumMap<>(E2.class),
                                                               (m, e2) -> m.put(e2, f.apply(e1, e2)),
                                                               Map::putAll)),
                       Map::putAll);
}

Ce qui rend cette lourdeur est que l'accumulateur en fonction de l'extérieur du collecteur de a à gérer un flux avec son propre trois-arg collecteur pour produire de l'intérieur de la carte.C'est vraiment dur pour le retrait du bien.Au lieu de la norme d'espacement, j'ai aligné les trois arguments de chaque collect() appel.Cela le rend assez large, mais si je n'avais pas le faire, il serait difficile de voir ce qui se passe avec ce depuis la nidification est si profond.Comme beaucoup de fan de flux comme je suis, il est difficile pour moi de dire que c'est mieux que la version classique.

Vous pourriez dire, "Pourquoi ne pas l'utiliser toMap() au lieu de les trois-arg collect() la fonction?" Le problème est que nous avons besoin de créer EnumMap cas, et la surcharge de toMap() qui prend un fournisseur de carte a quatre arguments:

toMap(keyFunc, valueFunc, mergeFunc, mapSupplier)

Pire, la fonction de fusion (troisième arg) n'est pas utilisé, de sorte que nous aurions à fournir une fonction qui n'est jamais utilisé.Voici à quoi ça ressemble:

<T> EnumMap<E1,EnumMap<E2,T>> buildTheMap(E1[] e1values,
                                          E2[] e2values,
                                          BiFunction<E1,E2,T> f) {
    return
        Stream.of(e1values)
              .collect(toMap(e1 -> e1,
                             e1 -> Stream.of(e2values)
                                         .collect(toMap(e2 -> e2,
                                                        e2 -> f.apply(e1, e2),
                                                        (x, y) -> x,
                                                        () -> new EnumMap<>(E2.class))),
                             (x, y) -> x,
                             () -> new EnumMap<>(E1.class)));
}

Ne ressemble pas tout mieux pour moi.Mon argent est toujours sur la version classique.

Il y a un certain nombre d'approches alternatives, on pourrait essayer.Nous allons voir ce qu'une bonne nuit de sommeil apporte.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top