【问题标题】:Ways to map the list of objects to another list of objects将对象列表映射到另一个对象列表的方法
【发布时间】:2019-10-14 10:11:26
【问题描述】:

首先,这些类是模拟数据类。 我有电话列表,我必须创建一个仅包含电话类型的新列表。为此,我有两个选择,如果有人能以适当的理由解释哪个选项比另一个更好。

Class Phone{
      int id;
      String type;
      Company company;
     //Assume all getters setters and constructor is there

 }
Class PhoneType{
      int id;
      String type;
     //Assume all getters setters and constructor is there
 }

我有一个电话对象列表,如下所示。

List<Phone> phones = new ArrayList<>();
phones.add(new Phone(1,"J7",samsung));
phones.add(new Phone(2,"J5",samsung));
phones.add(new Phone(3,"I5",apple));

我必须从电话列表中获取电话类型列表。为此,我有以下两种方法 第一种方式

List<PhoneType> phoneTypes;
phoneTypes = phones.parallelStream().
map(phone ->new PhoneType(phone.getId(),phone.getType())).
collect(Collectors.toList());

第二种方式

List<PhoneType> phoneTypes = new ArrayList<>();
phones.parallelStream().forEach(
phone -> phoneTypes.add(new PhoneType(phone.getId(), phone.getType())));

如果你有比上述方法更好的方法,也可以参考一下

【问题讨论】:

  • 在您的第一个方法中,您使用的地图可能不是预期的方式。虽然您可以将各种副作用塞入其中,但无论如何您都拥有 forEach 的副作用。但请看下面的答案。
  • Phone 可以有一个 PhoneType 成员,而不是复制相同的字段。
  • @swpalmer 是模拟数据

标签: java


【解决方案1】:

我想说,这是规范的方法。无需单独创建,可以添加过滤器等进一步操作。我猜这仍然是一个品味问题。

List<PhoneType> phoneTypes = phones.stream()
  .map(p -> new PhoneType(p.getId(), p.getType())))
  .collect(Collectors.toList());

下面列出了多种可能性,在 8 核/8Gb 机器、4Gb JVM 堆大小上收集的经验测试结果。如果从中得出一定的结论,那就是,差异不在数量级上,collection.foreach 可能以某种方式领先于其他一切。找到另一个类似的讨论here,测量包括迭代器在内的进一步变化并得出类似的结论。

至于不同的方法:stream.forEach() 不保留输入的顺序,其他一切都保留,here's 详细讨论流与集合。

上面提到的第一种方式因为parallelStream而被取消资格,在简单的地图操作的情况下,它被证明一直比较慢。

第二个,因为 collection.forEach 做同样的工作,保持顺序,也更快。在这个场合,没有大力挖掘 挖掘意识形态领域,我想暗示讨论是否premature optimization is the root of all evil

中间版本是collection.forEach 而不是stream().forEach,但我觉得它不太吸引人 看一下stream().map,似乎有一些性能提升,虽然不是很大,但我们必须权衡 审美缺陷。也许是品味问题。

可以选择仅使用 for 循环和数组进行操作,完全绕过集合和流。缺点是, 它们只有固定长度,与光滑的功能版本相比显得凌乱。

我进行了一系列测试运行,在下面找到结果,其中令人惊讶的是 collection.foreach 排在前面,然后是 循环版本和流版本最后,parallelStream 运行最慢。测试结果因因素而模糊 我无法准确确定,垃圾收集是主要嫌疑人。

您可以找到 code and data 在 gist 中生成这些结果。

我进行了 10 次运行,每次运行 100000,1000000,10000000,20000000 和 30000000 的列表/数组大小的映射。

在每次运行之前,使用 30000000 进行热身运行,但未测量。 仍然有散布在各处的随机异常值(平均为 17 秒,平均为 1.5 秒),我将其归咎于垃圾收集器。 对于平均值的最终计算,我将异常值替换为列的平均值(包括异常值)

循环:数组→数组 1.8 循环:数组→列表 0.983 集合.foreach 0.8 流.foreach 1.3 映射序列 1.8 地图并行 2.1

【讨论】:

  • 我放了一些模拟数据,在实际情况下,我将使用大尺寸的数组。对于性能,我认为,我必须使用并行流。不是吗?
  • 我想说,对于像这样的简单映射操作,parallelStream 的开销将超过拥有更多线程处理列表部分的任何好处。如果对每个元素都进行大量计算、获取 http 请求等,情况会有所不同。如果您的列表填满了机器内存的一半,那么这种说法甚至可能成立。但我建议做一个实验。
  • 给PhoneType添加一个构造函数:这样你就可以将map操作简化为构造函数的方法引用:List phoneTypes =phones.stream() .map(PhoneType::new)) ) .collect(Collectors.toList());
  • 另外,我可能只是利用 PhoneType 传递给 Phone 本身的构造函数,而不是构造新的 PhoneType 对象。然后可以使用phones.stream().map(Phone::getType).collect(Collectors.toList()); 计算列表,同时还允许使用Stream#distinct,而不是为每个电话创建一堆新对象
猜你喜欢
  • 1970-01-01
  • 2014-04-02
  • 1970-01-01
  • 2014-03-01
  • 2013-03-11
  • 1970-01-01
  • 1970-01-01
  • 2022-11-16
  • 1970-01-01
相关资源
最近更新 更多