更好的 lambda 以获取对象层次结构上的不同值



我们有对象 A 类型,它包含一个对象数组 B 类型,而不是包含对象数组 C 类型,如示例中所示:

/* sample structure that ends with 4 instances of C with ids.100,200,100,200 */
private class A{
        int id;
        A(int id) {
            this.id = id;
        }
        B[] b = new B[]{new B(10), new B(20) };
    }
    private class B{
        int id;
        B(int id) {
            this.id = id;
        }
        C[] c = new C[]{new C(100), new C(200) };
    }
    private class C{
        int id;
        C(int id) {
            this.id = id;
        }
        public String toString(){
            return ""+id;
        }
    }

如果我们尝试获得所有不同的 c.id(100,200),第一种方法可能是

HashSet<Integer> distinctIdsOfC = new HashSet<Integer>();
for (B bbb: a.b){
    for (C ccc: bbb.c){
        distinctIdsOfC.add(ccc.id);
    }
}

(结果如预期为 100、200)

还有我的第一个 attepmt 与 lambda

List<C> a6 = Arrays.asList(a.b).stream().map(jaja -> Arrays.asList(jaja.c)).flatMap(List::stream).collect(Collectors.toList());
a6.stream().map(x->x.id).distinct().forEach(System.out::println);

(结果再次如预期的那样是 100、200)

最后是问题。有更好的替代λ吗?

/* Complete source sample to test */
package test;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        t.haz();
    }
    private void haz() {
        A a = new A(1);
        HashSet<Integer> distinctIdsOfC = new HashSet<Integer>();
        for (B bbb : a.b) {
            for (C ccc : bbb.c) {
                distinctIdsOfC.add(ccc.id);
            }
        }
        System.out.println("with for-loop" + distinctIdsOfC.toString());
        System.out.println("with lambda");
        List<C> a6 = Arrays.asList(a.b).stream().map(jaja -> Arrays.asList(jaja.c)).flatMap(List::stream)
                .collect(Collectors.toList());
        a6.stream().map(x -> x.id).distinct().forEach(System.out::println);
    }
    private class A {
        int id;
        A(int id) {
            this.id = id;
        }
        B[] b = new B[] { new B(10), new B(20) };
    }
    private class B {
        int id;
        B(int id) {
            this.id = id;
        }
        C[] c = new C[] { new C(100), new C(200) };
    }
    private class C {
        int id;
        C(int id) {
            this.id = id;
        }
        public String toString() {
            return "" + id;
        }
    }
}

好的,我认为我们有很多选择,但是:

1) 在 C 类中实现 equlas/hashCode,然后:

        Arrays.stream(a.b)
            .map(s -> s.c)
            .flatMap(Arrays::stream)
            .distinct()
            .forEach(System.out::println)

但是,如果您无法添加哈希代码/等于:

2) 创建过滤方法(按类字段区分):

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Map<Object,Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

然后像这样写 lambda:

        Arrays.asList(a.b).stream()
            .map(jaja -> Arrays.asList(jaja.c))
            .flatMap(List::stream)
            .filter(distinctByKey(s -> s.id))
            .forEach(System.out::println);

有了注释中的所有有价值的信息,我们正在寻找不同的 id 值,而不是分散的对象并试图让它更简单,以 lambda 结尾。

    /* proposed lambda */
    System.out.println("with lambda");
    Set<Integer> a7 = Arrays.stream(a.b).flatMap(jaja-> Arrays.stream(jaja.c).map(jeje->jeje.id)).collect(Collectors.toSet());
    System.out.println("with lambda set a7");
    a7.stream().forEach(System.out::println);
这避免了

中间不使用列表,避免了明显,并且更短更清晰。

最新更新