أنظف طريقة لفهرسة مجموعة من خلال خاصية العنصر الذي يعد في حد ذاته مجموعة
-
20-12-2019 - |
سؤال
انا املك 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
لحالات الاستخدام الأخرى.