Java 番石榴酱避免多次重复

Java 番石榴酱避免多次重复,java,functional-programming,guava,Java,Functional Programming,Guava,我有一个包含两个字符串属性的对象列表 public class A { public String a; public String b; } 我想检索两个集一个包含属性a和一个b 天真的方法是这样的: List<A> list = .... Set<String> listofa = new HashSet<>(); Set<String> listofb = new HashSet<>(); for (A item

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

public class A {
    public String a;
    public String b;
}
我想检索两个
一个包含属性
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);

}
列表=。。。。
Set listofa=新HashSet();
Set listofb=新的HashSet();
对于(项目:列表){
if(item.a!=null)
增加(项目a);
如果(b项!=null)
b.增补清单(b项);
}
在番石榴I中尝试以功能性的方式完成,最终采用了以下方法:

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()));
函数getAFromList=新函数(){ @可空 @凌驾 公共字符串应用(@Nullable A input){ 返回输入; } }; 函数getBFromList=函数(){ @可空 @凌驾 公共字符串应用(@Nullable A input){ 返回输入.b; } }; FluentIterable iterables=FluentIterable.from(列表); Set listofAs=ImmutableSet.copyOf(iterables.transform(getAFromList.filter)(谓词.notNull()); Set listofBs=ImmutableSet.copyOf(iterables.transform(getBFromList.filter)(谓词.notNull()); 然而,这样我将在列表上迭代两次

有没有办法避免重复两次或多次


一般来说,如何以功能性的方式解决这些用例(不仅仅是在guava/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)

所以简单的答案是你必须迭代两次。想想看。如果您的
列表中有
N
元素
,则需要将
N
插入第一个
集合
,并将
N
插入第二个
集合
。无论是函数式还是其他方式,无论是在转换(提取)还是插入时,都必须迭代
N
两次


如果您要创建两个
列表
,则会有所不同,因为您可以创建视图,并且只能根据需要进行迭代

您试图实现的是使用谓词对集合进行分区或拆分


有番石榴,你可以用。请参阅相关问题和答案。

这可以通过以下方法在一次迭代中解决:

函数有三个单独的条目:

  • 键“a”下包含所有“a-not-null”对象的不可变列表
  • 键“b”下包含所有“b-not-null”对象的不可变列表
  • 还有可能是一个不可变的列表,其中的对象a和b在键“empty”下都为null

  • 嗯,我不确定我能跟上。当我使用
    列表时,这会有什么不同?我仍然需要在初始列表上迭代两次。即使我只使用了lazy-consumered视图,当我访问这两个列表中的项目时,它也必须以与set相同的方式在列表上迭代两次。还是我遗漏了什么?不,你是对的。通过视图初始化将是O(1),但一旦您迭代访问元素,您将处于O(N)。只有在不总是迭代一个或两个集合的情况下,此策略才能很好地工作。
    
        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);