【问题标题】:Java 8 stream API, how to collect List to Object with several List fieldsJava 8 流 API,如何使用多个 List 字段收集 List 到 Object
【发布时间】:2019-02-15 03:22:58
【问题描述】:

我想从没有 List 字段的对象列表中获取一个具有 List 字段的对象。

这是我的代码:

public class ObjectDTO {
    private List<String> ids;
    private List<Date> times;
    private List<String> names;
    private List<String> details;

public class ObjectDTOHelper {
        private String id;
        private Date time;
        private String name;
        private String detail;
}

我提供List&lt;ObjectDTOHelper&gt; 作为输入,并希望仅输出ObjectDTO 的一个对象。

我可以用多个独立的流函数来解决这个问题:

List<ObjectDTOHelper> helpers; //about 1000 elements

List<String> ids= helpers.stream().map(h -> h.id).collect(toList());
List<Date> times= helpers.stream().map(h -> h.time).collect(toList());
List<String> names= helpers.stream().map(h -> h.name).collect(toList());
List<String> details= helpers.stream().map(h -> h.detail).collect(toList());

new ObjectDTO(ids, times, name, detail)

我认为这不是最好的解决方案,因为我需要多做 100_000 次)

您能帮助更好地解决这个问题吗?

【问题讨论】:

  • 如果ObjectDTO 必须由所有 4 个列表构成(而不是逐步添加元素),那么这几乎是你能做的最好的事情了。
  • 问题中不清楚的是,从大约 1000 个元素,如何以 需要多做 100_000 次?当前解决方案的复杂性也是O(n),这是一种优化,可能完全是一次迭代。就最佳解决方案而言,效率不高的是将这么多 List&lt;T&gt; fields 存储在一个对象中。
  • 我同意你的观点,但这决定了“设计”,我只是在寻找更好的解决方案。也许我仍然可以改变结构)

标签: java lambda java-8 java-stream


【解决方案1】:

添加到您已经完成的一项可能的优化是预先分配所有列表所需的已知确切内存量:

List<ObjectDTOHelper> helpers = ...; //about 1000 elements
int size = helpers.size();

List<String> ids= helpers.stream().map(h -> h.id).collect(toCollection(()->new ArrayList<>(size)));
List<Date> times= helpers.stream().map(h -> h.time).collect(toCollection(()->new ArrayList<>(size)));
List<String> names= helpers.stream().map(h -> h.name).collect(toCollection(()->new ArrayList<>(size)));
List<String> details= helpers.stream().map(h -> h.detail).collect(toCollection(()->new ArrayList<>(size)));

new ObjectDTO(ids, times, name, detail)

如果你有太多这样的字段,你可以创建自己的 Collector 传递给 collect()。

【讨论】:

  • 恕我直言,这只是使现有解决方案复杂化
  • @nullpointer 我同意它更复杂,但 OP 专门要求比他们所拥有的更优化的东西。他们可以衡量性能差异(使用例如 JMH)并自行决定是否值得降低可读性。
  • 好吧,我很想知道这个解决方案对这个问题的效率如何?任何相关的文件。
  • @nullpointer 它只是避免调整 ArrayLists 内部使用的数组的大小。但正如我所说,应该测量开销。另见相关问题:stackoverflow.com/questions/41069206/…
  • 谢谢!根据我的测量,它确实改善了一点):有大小:1000 avgt 5 3245.702 ± 120.881 ms/op10000 avgt 5 29253.296 ± 646.855 ms/op没有大小1000 avgt 5 3323.091 ± 451.716 ms/op10000 avgt 5 31517.425 ± 3185.305 ms/op
【解决方案2】:

假设您来自 ObjectDTO 的字段被初始化为:

public class ObjectDTO {
    private List<String> ids = new LinkedList<>();
    ...
}

更新:When to use LinkedList over ArrayList in Java?

您可以使用IntStream 填写它

ObjectDTO objectDTO = new ObjectDTO();

IntStream.range(0, helpers.size())
    .forEach(i -> {
        objectDTO.getIds().add(helpers.get(i).getId());
        objectDTO.getTimes().add(helpers.get(i).getTime());
        objectDTO.getNames().add(helpers.get(i).getName());
        objectDTO.getDetails().add(helpers.get(i).getDetail());
     });

【讨论】:

  • 谢谢!这似乎是更好的解决方案,但经过几次测试,我得到了相同的性能(虽然我会测试它
  • 不需要 IntStream... 直接流式传输helpers
【解决方案3】:

假设一个 CPU/线程,您的解决方案在复杂性 (O(n)) 方面是一样高效的,但由于独立的 lambda,它可能具有不必要的开销。

进行一次向ObjectDTO添加帮助器的操作是否有意义:

class ObjectDTO {
  private List<String> ids;
  private List<Date> times;
  private List<String> names;
  private List<String> details;
  //...
  public void add(ObjectDTOHelper helper) {
      ids.add(helper.id)
      times.add(helper.time);
      names.add(helper.name);
      details.add(helper.detail);
  }
  //...
  public void addAll(Collection<? extends ObjectDTOHelper> helpers) {
     helpers.stream().forEach(this::add);
  }
  //...
}

如果ObjectDTO 是一个常量类(创建后不允许修改),您只需将这些方法更改为private 并在接受帮助器集合的公共构造函数中使用addAll

您还可以借此机会根据您要添加到每个帮助器位的预期和已知数量来调整列表的大小。

class ObjectDTO {
  // ...
  public ObjectDTO(Collection<? extends ObjectDTOHelper> helpers) {
     int size = helpers.size();

     ids = new ArrayList<>(size);
     names = new ArrayList<>(size);
     // ...
     addAll(helpers);
  }

也总是使用ArrayList 而从不使用LinkedList...我认为链接性能优于数组的唯一情况是,如果你要在一个大列表的中间进行重复插入,那不是你的情况.

【讨论】:

    猜你喜欢
    • 2016-08-19
    • 1970-01-01
    • 2019-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-26
    • 1970-01-01
    相关资源
    最近更新 更多