أنظف طريقة لفهرسة مجموعة من خلال خاصية العنصر الذي يعد في حد ذاته مجموعة

StackOverflow https://stackoverflow.com//questions/23003754

سؤال

انا املك List<Foo> وأريد الجوافة Multimap<String, Foo> حيث قمنا بتجميع Fooبواسطة كل علامة خاصة بهم Collection<String> getTags() وظيفة.

أنا أستخدم Java 8، لذا فإن مراجع الطرق واللامدا جيدة/مُشجعة.

على سبيل المثال إذا كان لدي:

foo1, tags=a,b,c
foo2, tags=c,d
foo3, tags=a,c,e

سأحصل على Multimap<String, Foo> مع:

a -> foo1, foo3
b -> foo1
c -> foo1, foo2, foo3
d -> foo2
e -> foo3
هل كانت مفيدة؟

المحلول

يمكنك استخدام المجمع المخصص لهذا:

Multimap<String, Foo> map = list.stream().collect(
    ImmutableMultimap::builder,
    (builder, value) -> value.getTags().forEach(tag -> builder.put(tag, value)),
    (builder1, builder2) -> builder1.putAll(builder2.build())
).build();

هذا لا يسبب آثار جانبية إضافية (انظر هنا في هذا) متزامن وأكثر اصطلاحًا.

يمكنك أيضًا استخراج هذه اللامدا المخصصة في أداة تجميع كاملة، مثل هذا:

public static <T, K> Collector<T, ?, Multimap<K, T>> toMultimapByKey(Function<? super T, ? extends Iterable<? extends K>> keysMapper) {
    return new MultimapCollector<>(keysMapper);
}

private static class MultimapCollector<T, K> implements Collector<T, ImmutableMultimap.Builder<K, T>, Multimap<K, T>> {
    private final Function<? super T, ? extends Iterable<? extends K>> keysMapper;

    private MultimapCollector(Function<? super T, ? extends Iterable<? extends K>> keysMapper) {
        this.keysMapper = keysMapper;
    }

    @Override
    public Supplier<ImmutableMultimap.Builder<K, T>> supplier() {
        return ImmutableMultimap::builder;
    }

    @Override
    public BiConsumer<ImmutableMultimap.Builder<K, T>, T> accumulator() {
        return (builder, value) -> keysMapper.apply(value).forEach(k -> builder.put(k, value));
    }

    @Override
    public BinaryOperator<ImmutableMultimap.Builder<K, T>> combiner() {
        return (b1, b2) -> b1.putAll(b2.build());
    }

    @Override
    public Function<ImmutableMultimap.Builder<K, T>, Multimap<K, T>> finisher() {
        return ImmutableMultimap.Builder<K, T>::build;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.emptySet();
    }
}

ثم ستبدو المجموعة كما يلي:

Multimap<String, Foo> map = list.stream().collect(toMultimapByKey(Foo::getTags));

يمكنك أيضا العودة EnumSet.of(Characteristics.UNORDERED) من characteristics() الطريقة إذا كان الطلب غير مهم بالنسبة لك.وهذا يمكن أن يجعل آلات التجميع الداخلية تعمل بكفاءة أكبر، خاصة في حالة التخفيض الموازي.

نصائح أخرى

ImmutableMultimap.Builder<String, Foo> builder = ImmutableMultimap.builder();
list.forEach(foo -> foo.getTags().forEach(tag -> builder.put(tag, foo));
return builder.build();

يعد هذا أكثر اصطلاحيًا لتدفقات Java 8:

    Multimap<String, Foo> map = list.stream()
            //First build a stream of Pair<String, Foo>
            .flatMap(f -> f.tags.stream().map(s -> new AbstractMap.SimpleImmutableEntry<>(s, f)))
            //Then collect it up into a multimap.
            .collect(
                    Multimaps.toMultimap(
                            x -> x.getKey(),
                            x -> x.getValue(),
                            MultimapBuilder.hashKeys().arrayListValues()::build
                    )
            );

أعلم أن استخدام فئة الزوج أمر قبيح ولكنه قبيح، ولكن الشيء الأساسي الذي أردت إظهاره هو Multimaps.toMultmap جامع.هناك أيضا Multmaps.flatteningToMultimap لحالات الاستخدام الأخرى.

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