【问题标题】:Arrays.asList vs. Arrays.stream to use forEach()Arrays.asList 与 Arrays.stream 使用 forEach()
【发布时间】:2014-12-09 23:50:15
【问题描述】:

如果你有一个 Array 并且你想使用 Java8 的 forEach() 方法,哪种方法更好或更高效:

Arrays.asList(new String[]{"hallo","hi"}).forEach(System.out::println);

Arrays.stream(new String[]{"hallo","hi"}).forEach(System.out::println);

差异是否显着或有更好的解决方案来解决这个问题?

【问题讨论】:

  • 可能是asList,似乎涉及的对象较少。但在程序执行过程中您仍然不会注意到任何事情。
  • 你可以写Arrays.asList("hello","hi")如果你还没有数组,你不需要创建一个数组

标签: java lambda java-8 java-stream


【解决方案1】:

两者都没有。如果你已经有一个数组,

String[] array;

我会使用:

Arrays.stream(array).forEach(System.out::println);

因为您将数组到流的转换留给 JDK - 让它负责效率等。

但是,由于您没有数组,我将使用Stream.of() 的可变参数来创建值流:

Stream.of("hallo","hi").forEach(System.out::println);

这再次让 JDK 负责在它认为合适的时候有效地流式传输值。

【讨论】:

  • 我不明白你的如果你已经有一个数组。一种或另一种方式,他们需要一个数组。 stream 接受一个数组,asList 也这样做。
  • @SotiriosDelimanolis “已经有一个数组”的意思是“不在行内编码文字元素” - 即该数组是在相关代码的外部声明和填充的
  • @SotiriosDelimanolis 是的,可读性(在设计雷达上总是很高,我相信“代码越少越好”)性能 - 它更多地交给 JDK (我们倾向于相信“写得很好”)。
  • 为了更难做出决定,我补充说,如果你没有现有的数组,你也可以说Stream.of("hallo","hi").forEach(System.out::println);。这在长度为1 的特殊情况下会更有效,因为在这种情况下,不会创建任何数组。
【解决方案2】:

这似乎几乎完全没有区别。我为此创建了一个测试类。在五次运行过程中,我的输出是这样的:

Run 1:
Arrays.asList() method................: 3231 ms
Arrays.stream() method................: 3111 ms
Stream.of() method....................: 3031 ms
Arrays.asList() (premade array) method: 3086 ms
Arrays.stream() (premade array) method: 3231 ms
Stream.of() (premade array) method....: 3191 ms

Run 2:
Arrays.asList() method................: 3270 ms
Arrays.stream() method................: 3072 ms
Stream.of() method....................: 3086 ms
Arrays.asList() (premade array) method: 3002 ms
Arrays.stream() (premade array) method: 3251 ms
Stream.of() (premade array) method....: 3271 ms

Run 3:
Arrays.asList() method................: 3307 ms
Arrays.stream() method................: 3092 ms
Stream.of() method....................: 2911 ms
Arrays.asList() (premade array) method: 3035 ms
Arrays.stream() (premade array) method: 3241 ms
Stream.of() (premade array) method....: 3241 ms

Run 4:
Arrays.asList() method................: 3630 ms
Arrays.stream() method................: 2981 ms
Stream.of() method....................: 2821 ms
Arrays.asList() (premade array) method: 3058 ms
Arrays.stream() (premade array) method: 3221 ms
Stream.of() (premade array) method....: 3214 ms

Run 5:
Arrays.asList() method................: 3338 ms
Arrays.stream() method................: 3174 ms
Stream.of() method....................: 3262 ms
Arrays.asList() (premade array) method: 3064 ms
Arrays.stream() (premade array) method: 3269 ms
Stream.of() (premade array) method....: 3275 ms

从输出来看,Stream.of() 方法似乎非常有效(但始终如一)最有效,并且

Stream.of("hallo","hi").forEach(System.out::println);

是非常易读的代码。 Stream.of 的优势在于它不必将数组转换为列表,或者创建数组然后创建流,而是可以直接从元素创建流。让我有点惊讶的是,由于我进行测试的方式,每次使用 Stream.of() 实例化一个新的数组流比传入一个预制数组要快,可能是因为“捕获” lambdas - 那些引用外部变量的 - 效率稍低。

这是我的测试类的代码:

import java.util.Arrays;
import java.util.function.Consumer;
import java.util.stream.Stream;


public class StreamArrayTest {

    public static void main(String[] args){
        System.out.println("Arrays.asList() method................: " + arraysAsListMethod() + " ms");
        System.out.println("Arrays.stream() method................: " + arraysStreamMethod() + " ms");
        System.out.println("Stream.of() method....................: " + streamOfMethod() + " ms");
        System.out.println("Arrays.asList() (premade array) method: " + presetArraysAsListMethod() + " ms");
        System.out.println("Arrays.stream() (premade array) method: " + presetArraysStreamMethod() + " ms");
        System.out.println("Stream.of() (premade array) method....: " + presetStreamsOfMethod() + " ms");
    }

    private static Long timeOneMillion(Runnable runner){
        MilliTimer mt = MilliTimer.start();
        for (int i = 0; i < 1000000; i++){
            runner.run();
        }
        return mt.end();
    }

    private static Long timeOneMillion(String[] strings, Consumer<String[]> consumer){
        MilliTimer mt = MilliTimer.start();
        for (int i = 0; i < 1000000; i++){
            consumer.accept(strings);
        }
        return mt.end();        
    }

    public static Long arraysAsListMethod(){
        return timeOneMillion(()->Arrays.asList(new String[]{"hallo","hi","test","test2","test3","test4","test5","test6","test7","test8"}).forEach(StreamArrayTest::doSomething));
    }

    public static Long arraysStreamMethod(){
        return timeOneMillion(()->Arrays.stream(new String[]{"hallo","hi","test","test2","test3","test4","test5","test6","test7","test8"}).forEach(StreamArrayTest::doSomething));
    }

    public static Long streamOfMethod(){
        return timeOneMillion(()->Stream.of("hallo","hi","test","test2","test3","test4","test5","test6","test7","test8").forEach(StreamArrayTest::doSomething));        
    }   

    public static Long presetArraysAsListMethod(){
        String[] strings = new String[]{"hallo","hi","test","test2","test3","test4","test5","test6","test7","test8"};
        return timeOneMillion(strings, (s)->Arrays.asList(s).forEach(StreamArrayTest::doSomething));    
    }

    public static Long presetArraysStreamMethod(){
        String[] strings = new String[]{"hallo","hi","test","test2","test3","test4","test5","test6","test7","test8"};
        return timeOneMillion(strings, (s)->Arrays.stream(s).forEach(StreamArrayTest::doSomething));    
    }

    public static Long presetStreamsOfMethod(){
        String[] strings = new String[]{"hallo","hi","test","test2","test3","test4","test5","test6","test7","test8"};
        return timeOneMillion(strings, (s)->Stream.of(s).forEach(StreamArrayTest::doSomething));    
    }

    public static void doSomething(String s){
        String result = s;
        for (int i = 0; i < 10; i++){
            result = result.concat(s);
        }
    }
}

还有我使用的 MilliTimer 类:

public class MilliTimer {
    private long startTime = 0L;

    private MilliTimer(long startTime){
        this.startTime = startTime;
    }

    public static MilliTimer start(){
        return new MilliTimer(System.currentTimeMillis());
    }

    public long end() throws IllegalArgumentException {
        return System.currentTimeMillis() - startTime;
    }
}

【讨论】:

  • 优化器更喜欢本地值。因此,使用临时数组的代码确实可能与使用准备好的数组一样快甚至更快,但是,尚不清楚这是否适用于您的基准测试,因为它存在缺陷。您的方法doSomething 可能看起来很复杂,但实际上什么也没做,因为它里面的所有东西都没有真正的效果。现代 JVM 能够识别这一点。除此之外,您应该使用System.nanoTime() 而不是System.currentTimeMillis(),因为后者返回的值不能保证不断向前移动。
  • doSomething 方法实际上做了一些事情......它实例化了许多 String 对象并将它们连接在一起。 .如果你不相信,那就把这四行删掉,看看执行时间如何从大约 3200 毫秒变为大约 30 毫秒。
  • 而且如果你在doSomething方法内部调整循环中的迭代次数,也会影响执行时间。
【解决方案3】:
Arrays.asList() method................: 22 ms
Arrays.stream() method................: 26 ms
Stream.of() method....................: 26 ms
Arrays.asList() (premade array) method: 8 ms
Arrays.stream() (premade array) method: 30 ms
Stream.of() (premade array) method....: 17 ms

当您将 doSomething 更改为实际上什么都不做时:

public static void doSomething(String s){
}

那么你测量的是这些操作的实际速度,而不是操作 String = String + String;这就是 doSomething 正在做的事情,当然它始终保持相同的速度。但是实际速度不一样,带有预制数组的 asList 要快得多。

这里的真实结果已经被其他人注意到了,你应该注意流,因为它通常比普通的旧 java(非 lambda)方法慢 4 倍。

【讨论】:

  • 如果你删除了 doSomething 的主体,你就没有考虑到各种处理方法的输入/输出。是的,可以使用Arrays.asList()Stream.of()Arrays.stream() 更快地实例化一个列表,但是如果您实际上不对数组中的值执行任何操作,则没有处理开销,因为优化器将识别noop 方法并剪切出来。 doSomething() 的时间应该是一致的,因为它们是完全相同的操作,剩下的大部分应该是调用各种处理方法的开销。
猜你喜欢
  • 2016-09-12
  • 2021-11-18
  • 2014-10-18
  • 1970-01-01
  • 2012-01-13
  • 2016-06-24
  • 2022-07-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多