Java8简化转换到Map的操作

3

我有这样的类结构:

public class A {
    private List<B> bs;
...//getters
}

public class C {
    private Long id;
...//getters
}

public class B {

    private Long idOfC;
...//more stuff
}

B::getIdOfC匹配C::getId。

在更好的设计中,B应该只包含对C的引用,而不是其id(我无法更改这一点),因此现在我需要创建一个映射,因此我的方法签名如下所示:

public Map<A, List<C>> convert(Collection<A> collection)

在这个转换方法中,有一个


List<C> getCsByIds(List<Long> id) 

这段代码将会被用来与B.idOfC进行匹配,但是由于该方法的执行代价较高,因此应该仅调用一次。

如果我以以下方式进行:

 List<B> bs = Arrays.asList(new B(10L), new B(11L)); //10L and 11L are the values of idOfC
   List<A> as = Arrays.asList(bs);
   //And assuming getCsByIds returns Arrays.asList(new C(10L), new C(11L), new C(12L));

那么
    Map<A, List<C>> map = convert(as);
    map.values().get(0) 

返回类似于Arrays.asList(new C(10L), new C(11L))的东西。

我认为负责这个方法的代码相当庞大:

    public Map<A, List<C>> convert(Collection<A> as) {
    List<Long> cIds = as.stream()
            .flatMap(a -> a.getBs().stream())
            .map(B::getCId)
            .collect(Collectors.toList());

    //single call to gsCsByIds
    Map<Long, C> csMap = getCsByIds(cIds)
            .stream()
            .collect(Collectors.toMap(C::getId, Function.identity()));

    //a whole new map is created by iterating over the list called "as"
    Map<A, List<C>> csByAs = new HashMap<>();
    if (!csMap.isEmpty()) {
        for (A a : as) {
            Set<C> cs = getCsFromMap(csMap, a.getBs());
            if (!cs.isEmpty()) {
                csByAs.put(a, new ArrayList<>(cs));
            }
        }
    }

    return csByAs;
}

private Set<B> getCsFromMap(Map<Long, C> cMap, List<B> bs) {
    return bs.stream()
            .map(b -> cMap.get(b.getIdOfc()))
            .collect(Collectors.toSet());
}

有没有简化这个的方法?

猜测 multimap https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/Multimap.html 可以帮助使代码更清洁。 - Oleks
3个回答

2
如果调用getCsByIds的成本很高,那么你最初的想法是执行它本身,这个想法还可以进一步简化为:

最初的回答: 如果getCsByIds的调用成本很高,那么你最初的想法是不错的,可以直接执行它。甚至可以再简化为:
public Map<A, List<C>> convert(Collection<A> as) {
    List<Long> cIds = as.stream()
            .flatMap(a -> a.getBs().stream())
            .map(B::getIdOfC)
            .collect(Collectors.toList());
    Map<Long, C> csMap = getCsByIds(cIds).stream()
            .collect(Collectors.toMap(C::getId, Function.identity()));

    return as.stream()
            .collect(Collectors.toMap(Function.identity(),
                    a -> a.getBs().stream().map(b -> csMap.get(b.getIdOfC()))
                            .collect(Collectors.toList()), (a, b) -> b));
}

您可以根据需要选择合并函数(a,b) -> b

最初的回答:

您可以选择自己的合并函数(a,b) -> b


注意:上面的代码也没有使用 getCsFromMap - Naman

1
也许直接遍历A会更好?(手头没有编译器,所以这段代码可能不能直接编译)
public Map<A, List<C>> convert(Collection<A> as) {
  Map<A, List<C>> result = new HashMap<>();
  for(A a: as){
     List<Long> cIds = a.getBs().stream()
                         .map(B::getIdOfC)
                         .collect(Collectors.toList());
     result.put(a, getCsByIds(cIds));
  }
  return result;
}

之前我没有提到,getCsByIds是一个非常昂贵的调用,因此最好只调用一次。如何更改以适应这一点? - CCC

0

这样的代码应该可以吧?我没有编译器,所以不能真正测试它。

public Map<A, List<C>> convert(Collection<A> as) {
    return as.stream()
             .collect(Collectors.toMap(Function::identity,
                                       a -> a.getBs().stream()
                                                     .map(B::getIdOfC)
                                                     .flatMap(id -> getCsByIds(asList(id))
                                                                   .values()
                                                                   .stream())
                                                     .collect(Collectors.toList())
                                      )
                     );
}

能否通过一次调用getCsByIds来获取数据,因为这样会比较耗费资源? - CCC
当然,但为什么这么贵呢? - Yassin Hajaj
1
这是一个连接到数据库的服务调用。 - CCC
就像我在生活中看到了很多过早的优化一样:它真的很昂贵吗?也就是说,对于所有ID调用1次还是针对一小部分ID多次调用,这是否真的有所不同(您已经测量过了)? - Locked

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接