我有一个包含两个字符串属性的对象列表。
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()));
但是这样我会在列表上迭代两次。
有什么办法可以避免两次或多次迭代?
一般来说,如何以功能的方式解决这些用例(不仅仅是在guava / java中)?
答案 0 :(得分:1)
首先,您在优化之后 - 但如果性能是关键,请使用常规java方法而不是番石榴(即您的第一种方法)。请参阅here。
我认为因为你想要两个结果,在某些时候你需要迭代两次(除非你传入其中一个要填充的集合,但那肯定不是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)
答案 1 :(得分:1)
所以简单的答案是你必须迭代两次。想一想。如果N
中有List
个元素,则需要N
插入第一个Set
和N
插入第二个Set
。功能性或其他方面,无论是转换(提取)还是插入,您都必须迭代N
两次。
如果您要使用两个Lists
,那么它会有所不同,因为您可以创建视图并仅根据需要进行迭代。
答案 2 :(得分:1)
您要实现的是使用谓词对集合进行分区或拆分。
使用Guava,您可以使用Multimap.index。请参阅相关问答here。
答案 3 :(得分:1)
这可以通过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,其中包含三个单独的条目: