public static void main(String args[]) {
  System.out.println("Creating list");
  List<string> strings = new ArrayList<>();
  for (int i = 0; i < 10000; i++) {
  strings.add("Item " + i);
  }
  strings.stream()
  .forEach(str -> System.out.println(str));
  }
  运行这段代码后,获得了一个我所预期的结果。在屏幕上输出的顺序与添加到列表中的顺序是一致的。
  .........
  Item 9982
  Item 9983
  Item 9984
  Item 9985
  Item 9986
  Item 9987
  Item 9988
  Item 9989
  Item 9990
  Item 9991
  Item 9992
  Item 9993
  Item 9994
  Item 9995
  Item 9996
  Item 9997
  Item 9998
  Item 9999
  现在,让我们看一下当转换成并行流后会发生什么。正如我之前所描述的,我即可以调用parallelStream方法,也可以在流上调用parallel方法。
  我将采用第二种方法。现在,我可以使用并行流了,该流可以根据负载分配到多个处理器来处理。
  strings.stream()
  .parallel()
  .forEach(str -> System.out.println(str));
  再次运行该段代码,然后观察会发生什么。注意,现在后打印的元素不是列表中后一个元素,后一个元素应该是9999。如果我滚动输出结果,能发现处理过程以某种方式在循环跳动。这是因为在运行时将数据划分成了多个块。
  .........
  Item 5292
  Item 5293
  Item 5294
  Item 5295
  Item 5296
  Item 5297
  Item 5298
  Item 5299
  Item 5300
  Item 5301
  Item 5302
  Item 5303
  Item 5304
  Item 5305
  Item 5306
  Item 5307
  Item 5308
  Item 5309
  Item 5310
  Item 5311
  然后,将数据块分配给合适的处理器去处理。只有当所有块都处理完成了,才会执行之后的代码。本质上讲,这是在调用 forEach() 方法时,将整个过程是根据需要来进行划分了。现在,这么做可能会提高性能,也可能不会。这依赖于数据集的大小以及你硬件的性能。通过这个例子,也可以看 出,如果需要按照添加的顺序挨个处理每一项,那么并行流可能不合适了。
  串行流能保证每次运行的顺序是一致的。但并行流,从定义上讲,是一种更有效率的方式。所以并行流在聚合操作的时候非常有效。很适合将集合作为一个整体考虑,然后在该集合上进行一些聚合操作的情况。我将会通过一个例子来演示集合元素的计数、求平均值及求和操作。
  我们在这个类的 main 方法中来计数,开始还是用相同的基础代码。创建10,000个字符串的列表。然后通过一个 for each 方法循环处理每一项。
  public static void main(String args[]) {
  System.out.println("Creating list");
  List<string> strings = new ArrayList<>();
  for (int i = 0; i < 10000; i++) {
  strings.add("Item " + i);
  }
  strings.stream()
  .forEach(str -> System.out.println(str));
  }
  在这个例子中,我想直接对集合元素进行计数,而不是挨个来处理。所以,我注释掉原来的代码,使用下面的代码。因为不能准确的知道该集合到底有多少个元素。所以我使用长整型变量来存储结果。
  我将这个变量命名为count,通过调用集合strings的.stream(), .count()方法,返回一个长整型的值。然后将这个值与“count:”拼接起来,再通过system的output来打印。
  //      strings.stream()
  //             .forEach(str -> System.out.println(str));
  long count = strings.stream().count();
  System.out.println("Count: " + count);
  保存并运行该段代码,下面是输出结果。集合中元素数量的统计几乎是瞬间完成。
  Creating list
  Count: 10000
  现在对上面的代码做一点小小的改动,增加两个0。现在,开始处理1000,000个字符串。我再次运行这段代码,也很快返回结果了。
  Creating list
  Count: 1000000
  现在,我使用并行流来处理,看会发生什么。我在下面增加 parallel 方法:
  //      strings.stream()
  //             .forEach(str -> System.out.println(str));
  long count = strings.stream().parallel().count();
  System.out.println("Count: " + count);
  然后我运行这段代码,发现花费的时间更长一点了。现在,我做一个基准测试,通过抓取操作前后的时间戳来观察发生了什么。然后做一点数学的事情。不同的系统 上,得到的结果可能不同。但是根据我的经验来说,这种包含简单类型的简单集合,使用并行流并没有太多的优势。不过,我还是鼓励你去自己做基准测试,虽然有 点麻烦。 不过这也要你是如何去做的。
  再让我们看一下求和及求均值。我将使用 SumAndAverage 类。这次,我有一个包含三个 person 对象的列表,每个 person 对象的有不同的年龄值。我的目的是求三个年龄的和及年龄的平均值。我在所有的 person 对象都加入到列表之后加入了一行新的代码。然后,我创建了一个名为sum的整型变量。
  首先,我通过 pepole.stream() 方法获取一个流。在这个流基础上,我可以调用 mapToInt() 方法。注意,还有两个类似的 Map Method:mapToDouble() 和 mapToLong()。这些方法的目的是,从复合类型中获取简单的基本类型数据,创建流对象。你可以用 lambda 表达式来完成这项工作。所以,我选择 mapToInt() 方法,因为每个人的年龄都是整数。
  关于 Lambda 表达式,开始是一个代表当前 person 的变量。然后,通过 Lambda 操作符和 Lambda 表达式(p.getAge())返回一个整数。这种返回值,我们有时也叫做int字符串。也可以返回double字符串或其它类型。现在,由于已经知道它 是一个数字类型的值,所以我可以调用 sum() 方法。现在,我已经将所有集合中 person 对象的年龄值全部加起来了。通过一条语句,我可以用 System Output 来输出结果了。我将求和的结果与“Total of ages”连接在一起输出。
  List<person> people = new ArrayList<>();
  people.add(new Person("Mohamed", 69));
  people.add(new Person("Doaa", 25));
  people.add(new Person("Malik", 6));
  int sum = people.stream()
  .mapToInt(p -> p.getAge())
  .sum();
  System.out.println("Total of ages " + sum);
  保存并运行上面的代码。三个年龄的总和是100。
  Total of ages 100
  求这些值的平均值非常类似。但是,求平均值需要做除法操作,所以需要考虑除数为0的问题,因此,当你求平均值的时候,可以返回一个Optional的变量。
  你可以使用多种数据类型。在计算平均值的时候,我想获得一个 doule 类型的值。所以,我创建了一个 OptionalDouble 类型的变量。注意,还存在 Optional Int 和 Optional Long。我将平均值命名为 avg,使用的代码与求和的代码也是一致的,开始用 people.stream()。在这个基础上,再次使用 mapToInt()。并且传递了相同的 lambda 表达式,后,调用 average 方法。
  现在,获得了一个OptionalDouble类型的变量。在处理这个变量前,你可以通过 isPresent() 来确保它确实是一个double值。所以,我使用了一段 if/else 的模板代码来处理。判定的条件是 avg.isPresent()。如果条件为真,使用 System Output 输出“Average”标签和平均值。在 else 子句中,我简单地打印“average wasn’t calculated”。
  OptionalDouble avg = people.stream()
  .mapToInt(p -> p.getAge())
  .average();
  if (avg.isPresent()) {
  System.out.println("Average: " + avg);
  } else {
  System.out.println("average wasn't calculated");
  }
  现在,在这个例子中,我知道能成功,因为我给三个人的年龄都赋值了。但是,情况不总是这样的。正如我前面说的,存在除0的情况,这时你不能获取到一个 double 类型返回值。我保存并运行这段代码,请注意 optional double 类,它是一个复合对象。
  Total of ages 100
  Average: OptionalDouble[33.333333333333336]
  所以,真实的值被包含在该类型中,回到这段代码,直接引用该对象,并调用 getAsDouble() 方法。
  if (avg.isPresent()) {
  System.out.println("Average: " + avg.getAsDouble());
  } else {
  System.out.println("average wasn't calculated");
  }
  现在,我可以获得 double 类型的值。我再次运行这段代码,输出结果如下:
  Total of ages 100
  Average: 33.333333333333336
  结论
  通过流和 lambda 表达式,你可以用非常非常少的代码可以完成集合的聚合计算。