سؤال

من java.util.function.BiFunction الذي يعين زوجًا من Enums في القيمة، أريد بناء EnumMap الذي يعكس تلك الخريطة.

على سبيل المثال، اسمحوا E1 و E2 يكون enum أنواع و T أي نوع معين:

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

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

نظرا لأي زوج من القيم من النوع E1 و E2

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

يجب أن تكون كلا القيمتين أدناه متساويتين:

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

boolean alwaysTrue = valueFromTheMaps.equals(valueFromTheFunction);

ما هو أفضل تطبيق (أكثر أناقة وكفاءة، وما إلى ذلك...) للطريقة التي يتم فيها استخدام "سحر" يحدث ؟

هل كانت مفيدة؟

المحلول

يمكنك الحصول على حل أنيق إذا ذهبت إلى حل عام وقمت بتقسيمه.أولاً، قم بتنفيذ وظيفة عامة تقوم بإنشاء ملف EnumMap من أصل Function, ، ثم قم بتنفيذ التعيين المتداخل لـ a BiFunction باستخدام الوظيفة الأولى مجتمعة مع نفسها:

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

إليك حالة اختبار صغيرة:

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}}

بالطبع، باستخدام الحل العام يمكنك بناء طرق للخرسانة enum الأنواع التي لا تتطلب Class حدود)…


يجب أن يعمل هذا بسلاسة مع الدفق الموازي إذا تم توفيره (Bi)Function هو موضوع آمن.

نصائح أخرى

كأساس للمقارنة، إليك النسخة التقليدية:

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

إليك الآن إصدار يستخدم نماذج متداخلة ثلاثية الوسائط من collect() تشغيل محطة الدفق:

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

ما يجعل هذا الأمر مرهقًا هو أن وظيفة المجمع للمجمع الخارجي يجب أن تقوم بتشغيل دفق مع مجمع ثلاثي الوسائط خاص به لإنتاج الخريطة الداخلية.من الصعب حقًا وضع مسافة بادئة جيدة.بدلاً من التباعد القياسي، قمت بمحاذاة الوسيطات الثلاث لكل منها collect() يتصل.وهذا يجعلها واسعة جدًا، لكن إذا لم أفعل ذلك، سيكون من الصعب رؤية ما يتناسب مع كل شيء نظرًا لأن التعشيش عميق جدًا.بقدر ما أنا من محبي التدفقات، فإنه من الصعب بالنسبة لي أن أقول إن هذا أفضل من الإصدار التقليدي.

قد تقول: "لماذا لا تستخدم toMap() بدلاً من الثلاثة arg collect() وظيفة؟" المشكلة هي أننا بحاجة إلى إنشاء EnumMap الحالات، والحمل الزائد toMap() يأخذ مورد الخريطة لديه أربعة الحجج:

toMap(keyFunc, valueFunc, mergeFunc, mapSupplier)

والأسوأ من ذلك، أن وظيفة الدمج (الوسيط الثالث) لا تُستخدم، لذلك يتعين علينا توفير وظيفة لم يتم استخدامها مطلقًا.إليك ما يبدو عليه الأمر:

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

لا يبدو أفضل بالنسبة لي.أموالي لا تزال على النسخة التقليدية.

هناك عدد من الأساليب البديلة التي يمكن للمرء تجربتها.سنرى ما يجلبه النوم الجيد ليلاً.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top