番石榴 - Iterables避免多次迭代

时间:2015-01-26 11:47:40

标签: java functional-programming guava

我有一个包含两个字符串属性的对象列表。

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中)?

4 个答案:

答案 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插入第一个SetN插入第二个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,其中包含三个单独的条目:

  1. 一个包含所有&#34; a-not-null&#34;的不可变列表键下的对象&#34; a&#34;。
  2. 包含所有&#34; b-not-null&#34;的不可变列表键下的对象&#34; b&#34;。
  3. 可能是一个带有对象的不可变列表,其中a和b在键&#34下为空;空&#34;。