我有一个包含两个字符串属性的对象列表。
public class A {
public String a;
public String b;
}
我想检索两个Sets
一个包含属性a
和一个b
。
幼稚的方法是这样长的:
List<A> list = ....
Set<String> listofa = new HashSet<>();
Set<String> listofb = new HashSet<>();
for (A item : list) {
if (item.a != null)
listofa.add(item.a);
if (item.b != null)
listofb.add(item.b);
}
试图在番石榴中以一种功能性的方式完成,我最终采用了这种方法:
Function<String,A> getAFromList = new Function<>() {
@Nullable
@Override
public String apply(@Nullable A input) {
return input.a;
}
};
Function<String,A> getBFromList = Function<>() {
@Nullable
@Override
public String apply(@Nullable A input) {
return input.b;
}
};
FluentIterable<A> iterables = FluentIterable.from(list);
Set<String> listofAs = ImmutableSet.copyOf(iterables.transform(getAFromList).filter(Predicates.notNull()));
Set<String> listofBs = ImmutableSet.copyOf(iterables.transform(getBFromList).filter(Predicates.notNull()));
然而,通过这种方式,我会在列表上迭代两次。
有什么方法可以避免迭代两次或多次吗?
一般来说,如何以功能性的方式解决这些用例(不仅在番石榴/java中)?
首先,你是在追求优化——但是如果性能是关键,使用常规的java方法而不是番石榴(即你的第一种方法)。看这里。
我认为因为你想要两个结果,在某些时候你需要迭代两次(除非你传入要填充的集合之一,但这绝对不是fp方法,因为它不是纯函数)。
但是,如果迭代成本高到需要优化,您可以将其迭代一次到中间结构:
a_b_pairs = transformToJustAB(input) //single expensive iteration
list_of_a = transformA(a_b_pairs) //multiple cheaper iterations
list_of_b = transformB(a_b_pairs)
所以简单的答案是你必须迭代两次。想想看。如果你的List
中有N
元素,你需要在第一个Set
中进行N
插入,在第二个Set
中进行N
插入。无论是功能上的还是其他方面的,你都必须迭代N
两次,无论是转换(提取)还是插入。
如果您要使用两个List
,则会有所不同,因为您可以创建视图并且仅根据需要进行迭代。
这可以使用Multimaps. index在一次迭代中解决:
Function<A, String> filterAB = new Function<A, String>() {
@Override
public String apply(A input) {
if (input.a != null) {
return "a";
}
if (input.b != null) {
return "b";
}
return "empty";
}
};
ImmutableListMultimap<String, A> partitionedMap = Multimaps.index(list, filterAB);
输出将是一个Guava Multimap,其中包含三个单独的条目: