Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java8-转换列表的最佳方法:map还是foreach?_Java_Java 8_Java Stream - Fatal编程技术网

Java8-转换列表的最佳方法:map还是foreach?

Java8-转换列表的最佳方法:map还是foreach?,java,java-8,java-stream,Java,Java 8,Java Stream,我有一个列表myListToParse,我想在其中过滤元素并对每个元素应用一个方法,然后将结果添加到另一个列表myFinalList 使用Java8,我注意到我可以用两种不同的方式来实现它。我想知道他们之间更有效的方法,并理解为什么一种方法比另一种更好 我愿意接受任何关于第三条道路的建议 方法1: myFinalList = new ArrayList<>(); myListToParse.stream() .filter(elt -> elt != null)

我有一个列表
myListToParse
,我想在其中过滤元素并对每个元素应用一个方法,然后将结果添加到另一个列表
myFinalList

使用Java8,我注意到我可以用两种不同的方式来实现它。我想知道他们之间更有效的方法,并理解为什么一种方法比另一种更好

我愿意接受任何关于第三条道路的建议

方法1:

myFinalList = new ArrayList<>();
myListToParse.stream()
        .filter(elt -> elt != null)
        .forEach(elt -> myFinalList.add(doSomething(elt)));

我喜欢第二条路

当您使用第一种方法时,如果您决定使用并行流来提高性能,您将无法控制通过
forEach
将元素添加到输出列表的顺序


当您使用
toList
时,即使您使用并行流,Streams API也会保持顺序。

不要担心任何性能差异,在这种情况下,它们通常都是最小的

方法2更可取,因为

  • 它不需要对lambda表达式外部存在的集合进行变异

  • 它更具可读性,因为在收集管道中执行的不同步骤是按顺序编写的:首先是筛选操作,然后是映射操作,然后是收集结果(有关收集管道好处的更多信息,请参阅Martin Fowler的。)

  • 通过更换使用的
    收集器
    ,您可以轻松更改收集值的方式。在某些情况下,您可能需要编写自己的
    收集器
    ,但这样做的好处是您可以轻松地重用它


  • 我同意现有的答案,即第二种形式更好,因为它没有任何副作用,更容易并行化(只需使用并行流)

    就性能而言,在您开始使用并行流之前,它们似乎是等效的。在这种情况下,map的性能会更好。请参见以下结果:

    您不能以相同的方式增强第一个示例,因为forEach是一个终端方法-它返回void-因此您必须使用有状态lambda。但是

    最后请注意,您的第二个代码段可以使用方法引用和静态导入以更加简洁的方式编写:

    myFinalList = myListToParse.stream()
        .filter(Objects::nonNull)
        .map(this::doSomething)
        .collect(toList()); 
    

    使用streams的主要好处之一是,它提供了以声明方式处理数据的能力,即使用函数式编程。它还免费提供多线程功能,这意味着不需要编写任何额外的多线程代码来使流并发

    假设您探索这种编程风格的原因是希望利用这些优势,那么您的第一个代码示例可能不起作用,因为
    foreach
    方法被归类为终端(这意味着它可能产生副作用)

    从函数编程的角度来看,第二种方法是首选的,因为map函数可以接受无状态lambda函数。更明确地说,传递给map函数的lambda应该是

  • 无干扰,意味着如果流是非并发的(例如,
    ArrayList
    ),则函数不应更改流的源
  • 无状态,以避免执行并行处理时出现意外结果(由线程调度差异引起)
  • 第二种方法的另一个好处是,如果流是并行的,而收集器是并发的且无序的,那么这些特征可以为简化操作提供有用的提示,以同时进行收集。

    如果使用,则可以使用
    collectIf()
    方法

    MutableList<Integer> source =
        Lists.mutable.with(1, null, 2, null, 3, null, 4, null, 5);
    
    MutableList<String> result = source.collectIf(Objects::nonNull, String::valueOf);
    
    Assert.assertEquals(Lists.immutable.with("1", "2", "3", "4", "5"), result);
    
    MutableList源代码=
    Lists.mutable.with(1,null,2,null,3,null,4,null,5);
    可变列表结果=source.collectIf(Objects::nonNull,String::valueOf);
    Assert.assertEquals(list.immutable.with(“1”、“2”、“3”、“4”、“5”),result);
    
    它的计算速度非常快,应该比使用流快一点


    注意:我是Eclipse集合的提交者。

    还有第三个选项-使用
    stream().toArray()
    -请参阅下面的注释。结果表明,它比forEach()或collect()更慢,表达能力也更低。它可能会在以后的JDK版本中得到优化,所以在这里添加它只是以防万一

    假设
    列表

    在doSomething()中,有一个微型基准测试、100万个条目、20%的空值和简单的转换

    顺序:

    toArray: LongSummaryStatistics{count=10, sum=5493, min=517, average=549,300000, max=569}
    forEach: LongSummaryStatistics{count=10, sum=5316, min=427, average=531,600000, max=571}
    collect: LongSummaryStatistics{count=10, sum=5380, min=444, average=538,000000, max=557}
    
    没有空值和筛选器的并行(因此流的大小为
    大小
    ): toArrays在这种情况下具有最佳性能,
    .forEach()
    在最近的ArrayList上以“indexOutOfBounds”失败,必须替换为
    .forEachOrdered()

    可能是方法3

    我总是喜欢把逻辑分开

    Predicate<Long> greaterThan100 = new Predicate<Long>() {
        @Override
        public boolean test(Long currentParameter) {
            return currentParameter > 100;
        }
    };
            
    List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
    List<Long> resultList = sourceLongList.parallelStream().filter(greaterThan100).collect(Collectors.toList());
    
    谓词greaterThan100=新谓词(){
    @凌驾
    公共布尔测试(长currentParameter){
    返回电流参数>100;
    }
    };
    List sourceLongList=Arrays.asList(1L、10L、50L、80L、100L、120L、133L、333L);
    List resultList=sourceLongList.parallelStream().filter(大于100.collect)(Collectors.toList());
    
    如果可以使用第三方库,则定义内置此功能的延迟扩展集合。例如,我们可以简单地写

    ListX myListToParse

    ListX myFinalList=myListToParse.filter(elt->elt!=null) .map(elt->doSomething(elt))

    直到第一次访问(在缓存和重用物化列表之后)才会对myFinalList进行评估


    [我是cyclops react的首席开发者]

    第二个。正确的函数应该没有副作用,在您的第一个实现中,您正在修改外部世界。只是风格问题,但是
    elt->elt!=null
    可以替换为
    对象::nonNull
    @8472最好首先确保集合中没有null值,
        myFinalList = Arrays.asList(
                myListToParse.stream()
                        .filter(Objects::nonNull)
                        .map(this::doSomething)
                        .toArray(String[]::new)
        );
    
    private LongSummaryStatistics benchmark(final String testName, final Runnable methodToTest, int samples) {
        long[] timing = new long[samples];
        for (int i = 0; i < samples; i++) {
            long start = System.currentTimeMillis();
            methodToTest.run();
            timing[i] = System.currentTimeMillis() - start;
        }
        final LongSummaryStatistics stats = Arrays.stream(timing).summaryStatistics();
        System.out.println(testName + ": " + stats);
        return stats;
    }
    
    toArray: LongSummaryStatistics{count=10, sum=3721, min=321, average=372,100000, max=535}
    forEach: LongSummaryStatistics{count=10, sum=3502, min=249, average=350,200000, max=389}
    collect: LongSummaryStatistics{count=10, sum=3325, min=265, average=332,500000, max=368}
    
    toArray: LongSummaryStatistics{count=10, sum=5493, min=517, average=549,300000, max=569}
    forEach: LongSummaryStatistics{count=10, sum=5316, min=427, average=531,600000, max=571}
    collect: LongSummaryStatistics{count=10, sum=5380, min=444, average=538,000000, max=557}
    
    toArray: LongSummaryStatistics{count=100, sum=75566, min=707, average=755,660000, max=1107}
    forEach: LongSummaryStatistics{count=100, sum=115802, min=992, average=1158,020000, max=1254}
    collect: LongSummaryStatistics{count=100, sum=88415, min=732, average=884,150000, max=1014}
    
    Predicate<Long> greaterThan100 = new Predicate<Long>() {
        @Override
        public boolean test(Long currentParameter) {
            return currentParameter > 100;
        }
    };
            
    List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
    List<Long> resultList = sourceLongList.parallelStream().filter(greaterThan100).collect(Collectors.toList());