【问题标题】:java8 simplify transformations to mapjava8 简化映射的转换
【发布时间】:2019-03-10 20:41:51
【问题描述】:

我有这个类结构:

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());
}

有没有办法让这更简单???

【问题讨论】:

标签: java collections java-8 java-stream


【解决方案1】:

如果对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) -&gt; b

【讨论】:

  • 注意:上面的代码也没有使用getCsFromMap
【解决方案2】:

也许只是直接迭代 As ? (手头没有编译器,所以可能 sn-p 没有编译就绪)

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 是一个非常昂贵的调用,所以最好只调用一次。怎么能改变它以适应它?
【解决方案3】:

这样的东西不可行吗?我没有编译器,所以我无法真正测试它

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 进行一次调用,因为它相当昂贵?
  • 可以,但是为什么这么贵
  • 这是一个连接到数据库的服务调用
  • 就像我在生活中看到过很多过早的优化一样:它真的很贵吗?含义:将所有 ID 称为 1x 或将一小部分 ID 称为多次是否真的有区别(您是否测量过)?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-10
  • 1970-01-01
  • 1970-01-01
  • 2020-04-10
  • 1970-01-01
  • 2018-11-13
  • 1970-01-01
相关资源
最近更新 更多