Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/cmake/2.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
Java 8 为什么过滤流比简单返回子列表的迭代代码要快?_Java 8_Java Stream - Fatal编程技术网

Java 8 为什么过滤流比简单返回子列表的迭代代码要快?

Java 8 为什么过滤流比简单返回子列表的迭代代码要快?,java-8,java-stream,Java 8,Java Stream,有人能通过返回主列表的子列表,向我解释为什么流版本中的以下代码比只使用过滤器的代码快 public static final int N = 50000000; static List<Integer> sourceList = new ArrayList<>(); static { for (int i = 0; i < N; i++) { sourceList.add(i); } } @Benchmark public List<Pa

有人能通过返回主列表的子列表,向我解释为什么流版本中的以下代码比只使用过滤器的代码快

public static final int N = 50000000;

static List<Integer> sourceList = new ArrayList<>();

static {
  for (int i = 0; i < N; i++) {
    sourceList.add(i);
  }
}

@Benchmark
public List<Pair<Integer, Integer>> vanilla() {
   List<Pair<Integer, Integer>> collect1 = 
   sourceList.stream()
     .map(integer -> Pair.of(integer, integer))
     .collect(Collectors.toList());
   return collect1.subList(1000, 100000);
}

@Benchmark
public List<Pair<Integer, Integer>> stream() {
  return sourceList.stream()
    .map(value -> Pair.of(value, value))
    .filter(value -> value.getLeft() > 1000 && value.getLeft() < 100000)
    .collect(Collectors.toList());
}

Benchmark     Mode  Cnt    Score   Error  Units
Test.stream   avgt   20    9.867 ± 0.218  ns/op
Test.vanilla  avgt   20  183.304 ± 8.550  ns/op
公共静态最终整数N=50000000;
静态列表sourceList=newarraylist();
静止的{
对于(int i=0;i成对(整数,整数))
.collect(Collectors.toList());
返回集合1.子列表(1000100000);
}
@基准
公共列表流(){
返回sourceList.stream()
.map(值->成对(值,值))
.filter(value->value.getLeft()>1000&&value.getLeft()<100000)
.collect(Collectors.toList());
}
基准模式Cnt分数误差单位
试验流平均燃气轮机20 9.867±0.218纳秒/升
试验结果:avgt 20 183.304±8.550纳秒/升
我使用JMH运行测试,但我不理解结果。我认为,通过添加将整数值封装在一对中的映射函数,将迫使流创建所有新对象,并将它们传递给filter方法,然后提取对的左侧部分进行比较。对我来说,这听起来比另一种方法更为密集,在这种方法中没有过滤,结果只是原始列表的一个子列表,因此没有遍历整个列表


我在这里遗漏了什么吗?

很可能是因为第一个元素必须填充一个包含50000000个元素的完整列表,这涉及到分配更多内存,每次达到容量时都要制作列表使用的内部数组的多个副本

第二种方法只需创建一个包含99000个元素的列表,从而分配更少的内存,并生成更少的内部数组副本


一个更快的解决方案是在映射之前进行过滤,从而避免无用的装箱和成对创建。限制到100000当然也会更快。

性能问题不是
子列表的问题,实际上它只是简单地包装了主
列表

ArrayList
将重置容量,并在需要时重复将元素复制到新数组中,以便添加更多元素
vanilla
使用更大的内存大小来添加
50000000
元素。因此它比
慢,因为
只添加
[1000..100000]
元素

以下部分是
ArrayList
类以扩展需要的容量,在
ArrayList
中添加更多元素将导致更多
ensureCapacityInternal
方法调用:

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}
可能以下版本的
vanilla
stream
更快,但仍然使用更大的内存大小:

@Benchmark
public List<Pair<Integer, Integer>> vanilla() {
   List<Pair<Integer, Integer>> main = 
   sourceList.stream()
             .map(integer -> Pair.of(integer, integer))
             .collect(Collectors.toCollection(()->new ArrayList<>(N)));
   return main.subList(1000, 100000);
}
@基准测试
公开名单(){
主列表=
sourceList.stream()
.map(整数->成对(整数,整数))
.collect(collector.toCollection(()->newarraylist(N));
返回主目录子目录(1000100000);
}

stream()方法创建的成对对象与另一个对象一样多。但它不会将它们全部添加到列表中。@JBNizet对不起,只是输入错误。好的,这很有意义。我知道先过滤会更快,但我尝试简化这个示例的生产代码,它也会先进行一些映射。生产代码与此类似,它必须按照每个对象的出现顺序为它们分配一个int值,然后只保留给定下限和上限的对象。问题是我需要以某种方式保留一个计数器,并将其传递给过滤器,因为对象不是像这里这样的int。在反转映射和过滤函数的同时,有没有办法重构这个示例代码以保持语义?在您发布的示例中,先过滤是很简单的,对吗?对于您的实际生产代码,我不能不看它。