【问题标题】:When not to use java8 streams? [closed]什么时候不使用 java8 流? [关闭]
【发布时间】:2020-04-11 14:32:37
【问题描述】:

我只是在尝试一些代码 sn-ps 并发现与使用 java 8 流相比,使用简单的 for 循环给了我更好的性能结果。现在,我可能在理解这些事情时遗漏了一些东西。我需要帮助理解差异。在下面添加我的代码。

//Following takes almost 3ms

public int[] testPerf(int[] nums, int[] index) {
    List<Integer> arrayL = new ArrayList<>();
    for(int i =0; i< index.length; i++){
        arrayL.add(index[i], nums[i]);
    }
    return arrayL.stream().mapToInt(i -> i).toArray();
}

//Following takes almost 1ms

public int[] testPerf(int[] nums, int[] index) {
    List<Integer> arrayL = new ArrayList<>();
    for(int i =0; i< index.length; i++){
        arrayL.add(index[i], nums[i]);
    }
    int [] result = new int[index.length];
    for(int i =0; i< index.length; i++){
        result[i] = arrayL.get(i);
    }
    return result;
}

编辑:开始

我想测试什么?在索引数组指定的索引处注入 nums 的元素,形成最终结果。

Input: nums = [0,1,2,3,4], index = [0,1,2,2,1]
Output: [0,4,1,3,2]
Explanation:
nums       index     target
0            0        [0]
1            1        [0,1]
2            2        [0,1,2]
3            2        [0,1,3,2]
4            1        [0,4,1,3,2]

编辑:结束

请注意,我已经用不同的输入测试了这些,并尝试了包含 2k+ 元素的数组,但带有 for 循环的代码仍然给了我更好的性能。

请指导是什么使其他代码花费更多时间? 还要指出我可以学习何时不使用 java 流的任何参考资料? (什么时候不要过于复杂?:))

【问题讨论】:

  • 第一种方法应该是一行return Arrays.stream(nums).toArray(); - 你的代码不是最优的。其次,众所周知,微基准测试很难正确执行(而且您还没有包含导致您得出结论哪个更快的测试工具)。
  • Stream 并不能保证更好的性能,但可以保证更好的代码可读性。你可以看看 mapToInt underhood 是如何实现的。
  • "与使用 java 8 流相比,简单的 for 循环给了我更好的性能结果"几乎总是会这样,因为有一个使用流的开销很大。只有当循环中的工作很繁重,并且您启用了并行处理时,流才有可能胜过旧式循环。
  • 你没有解释你是如何测试性能的。这就是为什么您的结果可能不正确的原因。看起来很奇怪:1)方法不仅包含您比较的功能,还包含附加代码。 2)你说的是 1 毫秒,3 毫秒。这是低于测试精度的范围。例如,进行 10 次测试,每次测试持续约 1 分钟。消除大偏差。然后取平均值。然后你有或多或少的现实比较。不过,for 可以更快。但是你会知道真实的画面。
  • @ElliottFrisch return Arrays.stream(nums).toArray(); 真的吗?为什么不return nums.clone();,因为这将是相同结果的更快版本? --- 这仍然完全忽略了index 参数,这可能是有原因的。

标签: java arrays java-8 java-stream


【解决方案1】:

如果可以的话,我会在循环上使用流(我在循环上使用任何东西),因为 流通常更容易编写而没有错误,即使在没有测试的情况下,因为它们的声明性统一语法。出于同样的原因,对于非作者来说,它们通常更容易阅读,即使没有彻底的评论。此外,流是惰性的(想想“拉”),for 循环是急切的(想想“推”)。而且懒惰通常更好。

不幸的是,流有(有时很大)开销,因此在编写性能关键代码时需要注意,尤其是收集器。

因此,类似于“尽可能使用流并在必须的地方循环”。

【讨论】:

    【解决方案2】:

    流的目标是使代码更紧凑、更易读、减少样板代码,从而简化开发人员的工作。流的目标不是提高性能。特定情况下的性能可能与 for 不同。流也可能占用更多内存。但结果 - 如何将流转换为字节码 - 可能取决于 JVM:Oracle JDK、Open JDK、IBM JDK 等。

    如果您对内存和性能有严格的要求,那么没有现成的答案。您应该比较两种变体中的生产代码(不是您在此处显示的示例) - 使用 forstreams,然后选择符合您严格要求的代码。

    但是...

    但在当今大多数应用程序中,循环的影响,无论是 for 还是 streams,对性能的影响非常低与访问数据库或通过网络调用某些 Web 服务等其他操作相比。即使 forstreams 快 10 倍,但数据库访问需要 2000 毫秒,您也不会看到真正的区别。

    代码风格的差异可能是必不可少的。开发人员可以生成可读性更好的代码。当您团队中的一些开发人员去其他项目并且一些新加入您的团队并且必须扩展现有代码时,他们将需要更少的时间来理解这些代码。理解 for 并不难 :) 但是与使用 for 的代码相比,阅读具有多个 streams 的代码对许多开发人员来说是自然而然的。

    在一些特殊情况下很难应用流,例如同时循环 2 或 3 个集合。在这种情况下,流没有多大意义,for 更可取。

    但一般来说,何时使用或不使用特定构造没有规则。您知道任何 whiledo - while 循环都可以替换为 for 循环。但是为什么 Java 有不同类型的循环呢?因为他们中的一些人可以比其他人更好地表达特定情况下的逻辑。与流相同。如果您和您的团队对流不满意,则不必使用它们。试一试,使用它们超过 3 个月,然后在团队内部讨论并决定项目中的每个开发人员是否应该尽可能多地使用它们,或者应该尽量避免使用它们。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-20
      • 2012-09-19
      • 1970-01-01
      • 2010-11-24
      • 2013-05-25
      • 2010-09-06
      相关资源
      最近更新 更多