《水浒》凭什么成了经典

   
“四大名著”之一的“水浒传”是本身异常热衷的一本书。当然,喜欢读《水浒》的人是成千上万的,自古至今,他平昔倍受欢迎,早已成了经典。

Streams

  • 原文作者:
    shekhargulati
  • 译者: leege100
  • 状态: 完成

在其次章中,我们学习到了lambda表达式允许我们在不创制新类的图景下传递行为,从而援救大家写出到底简洁的代码。lambda表明式是一种简易的语法结构,它经过应用函数式接口来协助开发者简单明了的传递意图。当使用lambda表明式的统筹思想来设计API时,lambda表达式的雄强就会赢得呈现,比如大家在其次节商讨的运用函数式接口编程的APIlambdas
chapter

Stream是java8引入的一个重度使用lambda表明式的API。Stream使用一种恍若用SQL语句从数据库查询数据的直观模式来提供一种对Java集合运算和发挥的高阶抽象。直观意味着开发者在写代码时只需关注他们想要的结果是咋样而无需关注实现结果的求实办法。这一章节中,大家将介绍为啥我们需要一种新的多寡处理API、Collection和Stream的不同之处以及咋样将StreamAPI应用到大家的编码中。

本节的代码见 ch03
package
.

可是,他究竟有何魅力竟能如此吸引一代代的人?

为何大家需要一种新的数据处理抽象概念?

在我看来,重要有两点:

  1. Collection API
    不可能提供更高阶的布局来询问数据,因此开发者不得不为贯彻多数零碎的职责而写一大堆样板代码。

2、对聚集数据的并行处理有自然的限定,怎么着运用Java语言的面世结构、如何快速的拍卖数量以及咋样飞快的产出都亟待由程序员自己来研商和促成。

语言 1

Java 8往日的数据处理

翻阅下边这一段代码,猜猜看它是拿来做如何的。

public class Example1_Java7 {

    public static void main(String[] args) {
        List<Task> tasks = getTasks();

        List<Task> readingTasks = new ArrayList<>();
        for (Task task : tasks) {
            if (task.getType() == TaskType.READING) {
                readingTasks.add(task);
            }
        }
        Collections.sort(readingTasks, new Comparator<Task>() {
            @Override
            public int compare(Task t1, Task t2) {
                return t1.getTitle().length() - t2.getTitle().length();
            }
        });
        for (Task readingTask : readingTasks) {
            System.out.println(readingTask.getTitle());
        }
    }
}

下边这段代码是用来依照字符串长度的排序打印所有READING类型的task的title。所有Java开发者每一天都会写这样的代码,为了写出那般一个简约的次第,我们不得不写下15行Java代码。然则下边这段代码最大的题材不在于其代码长度,而在于不可以清晰传达开发者的用意:过滤出装有READING的task、依照字符串的长短排序然后生成一个String类型的List。

这一题材的确不佳应对,更何况我是一个无名小辈,能力有限,不可以瞎说,唯恐误评了经典。

Java8中的数据处理

可以像下边这段代码那样,使用java8中的Stream
API来实现与地点代码同等的功能。

public class Example1_Stream {

    public static void main(String[] args) {
        List<Task> tasks = getTasks();

        List<String> readingTasks = tasks.stream()
                .filter(task -> task.getType() == TaskType.READING)
                .sorted((t1, t2) -> t1.getTitle().length() - t2.getTitle().length())
                .map(Task::getTitle)
                .collect(Collectors.toList());

        readingTasks.forEach(System.out::println);
    }
}

下边这段代码中,形成了一个由五个stream操作结合的管道。

  • stream() – 通过在近似下边tasks List<Task>的集合源上调用
    stream()形式来创建一个stream的管道。

  • filter(Predicate<T>)
    这一个操功能来领取stream中匹配predicate定义规则的元素。假若您有一个stream,你可以在它上边调用零次依然频繁戛不过止的操作。lambda表明式task -> task.getType() == TaskType.READING概念了一个用来过滤出具有READING的task的平整。

  • sorted(Comparator<T>): This operation returns a stream
    consisting of all the stream elements sorted by the Comparator
    defined by lambda expression i.e. in the example shown
    above.此操作重临一个stream,此stream由具有遵照lambda表明式定义的Comparator来排序后的stream元素组成,在下面代码中排序的表明式是(t1,
    t2) -> t1.getTitle().length() – t2.getTitle().length().

  • map(Function<T,R>):
    此操作再次来到一个stream,该stream的各样元平昔自原stream的各样元素通过Function<T,R>处理后得到的结果。

  • collect(toList())
    -此操作把下面对stream举办各样操作后的结果装进一个list中。

   
现在,即使提笔了,但也是竭尽谨小慎微地谈论自己爱好他的因由。虽然这多少个原因是成百上千人所共同所以为然的,这他或可以成为《水浒》成为经典的案由之一二了。

何以说Java8更好

In my opinion Java 8 code is better because of following reasons:
在我看来,Java8的代码更好根本有以下几点原因:

  1. Java8代码可以清楚地表明开发者对数码过滤、排序等操作的打算。

  2. 透过利用Stream
    API格式的更高抽象,开发者表明他们所想要的是何等而不是怎么去取得这个结果。

  3. Stream
    API为数量处理提供一种统一的语言,使得开发者在议论数据处理时有共同的词汇。当五个开发者研讨filter函数时,你都会领会他们都是在拓展一个数码过滤操作。

  4. 开发者不再需要为贯彻数据处理而写的各类规范代码,也不再需要为loop代码或者暂时集结来囤积数据的冗余代码,Stream
    API会处理这一切。

  5. Stream不会修改潜在的聚集,它是非换换的。

   
首先,我认为生动曲折的故事情节是最要紧的因由。整本书重点介绍的英雄人物一百单八,另外知名有姓者577人,有姓无名者99人,有名无姓者9人,无名无姓,但对故事情节发展有肯定效能的人选40,还有书中涉及但未出台的人物102人,共827人。这几人选中,有男有女,有官有吏,有士有兵,有颇具贫,有僧有妓,有主有仆,有汉有胡,还有不同职业的底部百姓。不言而喻,人物众多,身份多样事关更为错综复杂。不过,读《水浒》是,却一点也觉得不到任何累赘,不必刻意记住人名,也不会因人物关系复杂而感到懊恼,因为作者巧妙地把全路安排得如此周全。正是因为这样多有声有色的人物形象,复杂连贯的人员关系,小说内容也不停提升,曲折生动,令人敬佩。而且,作者的高超文笔水平越来越让故事情节形象生动地表现在读者面前,有将近的痛感。例如,武松打虎那一节,对老虎的形容从面貌到动作都分外实事求是,让洋洋人揣度是不是施耐庵先生爬到虎山上的一棵树上观看老虎。

Stream是什么

Stream是一个在少数数据上的虚幻视图。比如,Stream可以是一个list或者文件中的几行依然此外随意的一个要素体系的视图。Stream
API提供可以顺序表现依旧并行表现的操作总和。开发者需要精晓某些,Stream是一种更高阶的抽象概念,而不是一种数据结构。Stream不会储存数据Stream天生就很懒,只有在被应用到时才会举办总结。它同意我们发出无限的数据流(stream
of
data)。在Java8中,你可以像下面这样,异常轻松的写出一个十分制生成特定标识符的代码:

public static void main(String[] args) {
    Stream<String> uuidStream = Stream.generate(() -> UUID.randomUUID().toString());
}

在Stream接口中有诸如ofgenerate语言,、iterate等多种静态工厂方法可以用来创设stream实例。下边提到的generate格局包含一个SupplierSupplier是一个方可用来描述一个不需要任何输入且会发出一个值的函数的函数式接口,大家向generate形式中传送一个supplier,当它被调用时会生成一个特定标识符。

Supplier<String> uuids = () -> UUID.randomUUID().toString()

运转方面这段代码,什么都不会时有暴发,因为Stream是懒加载的,直到被拔取时才会履行。假设我们改成如下这段代码,大家就会在控制台看到打印出来的UUID。这段程序会一向进行下去。

public static void main(String[] args) {
    Stream<String> uuidStream = Stream.generate(() -> UUID.randomUUID().toString());
    uuidStream.forEach(System.out::println);
}

Java8运作开发者通过在一个Collection上调用stream办法来成立Stream。Stream协理数据处理操作,从而开发者可以动用更高阶的多少处理社团来表达运算。

语言 2

Collection vs Stream

下边这张表解说了Collection和Stream的不同之处

语言 3

Collection vs Stream

下边我们来商讨内迭代(internal iteration)和外迭代(external
iteration)的区分,以及懒赋值的概念。

 
其次,在内容上,贴近民生的历史主旨更加关键。“官逼民反”“啥地方有压迫,啥地方就有对抗”是本书彰显的主旨。中国的封建史很遥远,而笔者正是在这长时间的历史中,时代变化,朝代更替中,提炼出了这么一个一定的真谛。这一主旨到了现代社会,也仍然不算过时。因为在我们如此一个社会主义国家,人民是国家的主人,人民的力量是战无不胜的。由此,人民在昏天黑地的时日读之,就好比找到了发展的大方向;在通达的一时读之,又如与一个智者成为朋友。

外迭代(External iteration) vs (内迭代)internal iterationvs

地方谈到的Java8 Stream API代码和Collection
API代码的区别在于由何人来控制迭代,是迭代器本身如故开发者。Stream
API仅仅提供他们想要实现的操作,然后迭代器把这一个操作使用到秘密Collection的各样元素中去。当对神秘的Collection举行的迭代操作是由迭代器本身决定时,就叫着内迭代;反之,当迭代操作是由开发者控制时,就叫着外迭代。Collection
API中for-each结构的行使就是一个外迭代的例子。

有人会说,在Collection
API中我们也不需要对神秘的迭代器举行操作,因为for-each布局早已替我们处理得很好了,不过for-each布局其实只是是一种iterator
API的语法糖罢了。for-each尽管很简单,但是它有局部瑕疵 —
1)唯有固有各种 2)容易写出生硬的命令式代码(imperative code)
3)难以并行。

   
还有,作为一部小说,关键还得有可以唤起读者兴趣的地方。《水浒》是一部章回体随笔,“预知后事如何,且听下回分解”便让读者对下一章内容充满希望,又增长《水浒》情节往往出人意料,不像现在众多网络小说,好像都是一个模型里出来的均等,没什么稀奇的,看一页,就猜到了结果。我们反复调侃高丽国电视机剧都是一个人导演,就因为其内容大半是两男争一女,一贫一富,无聊非凡。但梁山好汉的故事,你是猜不到的。何人会想到鲁令尹三拳会打死镇关西,林冲一个规规矩矩人会火并王伦?

Lazy evaluation懒加载

stream表明式在被终极操作方法调用以前不会被赋值总结。Stream
API中的大多数操作会再次回到一个Stream。这一个操作不会做其他的执行操作,它们只会构建这一个管道。看着下边这段代码,预测一下它的输出会是什么。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream().map(n -> n / 0).filter(n -> n % 2 == 0);

地点这段代码中,大家将stream元素中的数字除以0,大家可能会以为那段代码在运行时会抛出ArithmeticExceptin老大,而实质上不会。因为stream表明式唯有在有终点操作被调用时才会被执行运算。假如大家为地点的stream加上终极操作,stream就会被实践并抛出特别。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream().map(n -> n / 0).filter(n -> n % 2 == 0);
stream.collect(toList());

我们会取得如下的stack trace:

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at org._7dayswithx.java8.day2.EagerEvaluationExample.lambda$main$0(EagerEvaluationExample.java:13)
    at org._7dayswithx.java8.day2.EagerEvaluationExample$$Lambda$1/1915318863.apply(Unknown Source)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)

   
以上原则满足了相应可以变成经典,但其抢眼的文艺美,使之更上一个水平,不仅是大手笔,更是“四大名著”之一,名著中的名著。随笔毕竟也是一种历史学随笔,语言,写法也很重大。《水浒》中的文字繁简适当,字字句句显得魅力十足,动作形象生动,正如金圣叹所评,有些字句直触人心。其它,虽为元初之作,但也远非有领会困难,可见语言通俗易懂,朴实。

使用Stream API

Stream
API提供了一大堆开发者可以用来从集合中询问数据的操作,这么些操作分为二种–过渡操作和终极操作。

连着操作从已存在的stream上爆发另一个新的stream的函数,比如filter,map,
sorted,等。

终端操作从stream上暴发一个非stream结果的函数,如collect(toList())
, forEach, count等。

连着操作允许开发者构建在调用终极操作时才实施的管道。下边是Stream
API的一些函数列表:

<a
href=”https://whyjava.files.wordpress.com/2015/07/stream-api.png"&gt;

语言 4

stream-api

</a>

 
综上说述,《水浒》是当之无愧的绝唱。倘使以上述口径去评价当今社会上的管农学小说,不知能否找到名副其实的一两部经典?

示例类

在本教程中,我们将会用Task管理类来解释这多少个概念。例子中,有一个叫Task的类,它是一个由用户来表现的类,其定义如下:

import java.time.LocalDate;
import java.util.*;

public class Task {
    private final String id;
    private final String title;
    private final TaskType type;
    private final LocalDate createdOn;
    private boolean done = false;
    private Set<String> tags = new HashSet<>();
    private LocalDate dueOn;

    // removed constructor, getter, and setter for brevity
}

事例中的数据集如下,在一切Stream API例子中我们都会用到它。

Task task1 = new Task("Read Version Control with Git book", TaskType.READING, LocalDate.of(2015, Month.JULY, 1)).addTag("git").addTag("reading").addTag("books");

Task task2 = new Task("Read Java 8 Lambdas book", TaskType.READING, LocalDate.of(2015, Month.JULY, 2)).addTag("java8").addTag("reading").addTag("books");

Task task3 = new Task("Write a mobile application to store my tasks", TaskType.CODING, LocalDate.of(2015, Month.JULY, 3)).addTag("coding").addTag("mobile");

Task task4 = new Task("Write a blog on Java 8 Streams", TaskType.WRITING, LocalDate.of(2015, Month.JULY, 4)).addTag("blogging").addTag("writing").addTag("streams");

Task task5 = new Task("Read Domain Driven Design book", TaskType.READING, LocalDate.of(2015, Month.JULY, 5)).addTag("ddd").addTag("books").addTag("reading");

List<Task> tasks = Arrays.asList(task1, task2, task3, task4, task5);

本章节暂不啄磨Java8的Data 提姆e
API,这里大家就把它当着一个家常的日期的API。

Example 1: 找出所有READING Task的题目,并遵照它们的创登时间排序。

首先个例子我们就要实现的是,从Task列表中找出富有正在翻阅的任务的标题,并遵照它们的创立时间排序。我们要做的操作如下:

  1. 过滤出富有TaskType为READING的Task。
  2. 遵照创制时间对task举行排序。
  3. 赢得每个task的title。
  4. 将收获的这多少个title装进一个List中。

地方的五个操作步骤可以相当简单的翻译成下边这段代码:

private static List<String> allReadingTasks(List<Task> tasks) {
        List<String> readingTaskTitles = tasks.stream().
                filter(task -> task.getType() == TaskType.READING).
                sorted((t1, t2) -> t1.getCreatedOn().compareTo(t2.getCreatedOn())).
                map(task -> task.getTitle()).
                collect(Collectors.toList());
        return readingTaskTitles;
}

在上头的代码中,我们运用了Stream API中如下的有些格局:

  • filter:允许开发者定义一个判断规则来从神秘的stream中领到符合此规则的一部分因素。规则task
    -> task.getType() ==
    TaskType.READING
    意为从stream中精选所有TaskType 为READING的元素。

  • sorted:
    允许开发者定义一个相比器来排序stream。上例中,我们依照创立时间来排序,其中的lambda表明式(t1,
    t2) ->
    t1.getCreatedOn().compareTo(t2.getCreatedOn())
    就对函数式接口Comparator中的compare函数举办了贯彻。

  • map:
    需要一个兑现了可以将一个stream转换成另一个stream的Function<? super T, ? extends R>的lambda表明式作为参数,Function<?
    super T, ? extends
    R>接口可以将一个stream转换为另一个stream。lambda表达式task
    -> task.getTitle()
    将一个task转化为标题。

  • collect(toList())
    这是一个极限操作,它将有着READING的Task的题目标卷入一个list中。

咱们得以经过动用Comparator接口的comparing情势和方法引用来将方面的代码简化成如下代码:

public List<String> allReadingTasks(List<Task> tasks) {
    return tasks.stream().
            filter(task -> task.getType() == TaskType.READING).
            sorted(Comparator.comparing(Task::getCreatedOn)).
            map(Task::getTitle).
            collect(Collectors.toList());

}

从Java8起初,接口可以蕴涵通过静态和默认方法来实现模式,在ch01业已介绍过了。
办法引用Task::getCreatedOn是由Function<Task,LocalDate>而来的。

上边代码中,大家接纳了Comparator接口中的静态帮助方法comparing,此方法需要收取一个用来领取ComparableFunction用作参数,再次来到一个透过key举办比较的Comparator。方法引用Task::getCreatedOn
是由 Function<Task, LocalDate>而来的.

大家得以像如下代码这样,使用函数组合,通过在Comparator上调用reversed()主意,来充足轻松的颠倒排序。

public List<String> allReadingTasksSortedByCreatedOnDesc(List<Task> tasks) {
    return tasks.stream().
            filter(task -> task.getType() == TaskType.READING).
            sorted(Comparator.comparing(Task::getCreatedOn).reversed()).
            map(Task::getTitle).
            collect(Collectors.toList());
}

Example 2: 去除重复的tasks

假设我们有一个有过多再一次task的数据集,可以像如下代码这样经过调用distinct措施来轻松的删除stream中的重复的要素:

public List<Task> allDistinctTasks(List<Task> tasks) {
    return tasks.stream().distinct().collect(Collectors.toList());
}

distinct()情势把一个stream转换成一个不含重复元素的stream,它通过对象的equals措施来判定目的是否等于。依照目的相等方法的论断,假设六个目的相等就意味着有重新,它就会从结果stream中移除。

Example 3: 遵照创造时间排序,找出前5个处于reading状态的task

limit方法能够用来把结果集限定在一个加以的数字。limit是一个封堵操作,意味着它不会为了取得结果而去运算所有因素。

public List<String> topN(List<Task> tasks, int n){
    return tasks.stream().
            filter(task -> task.getType() == TaskType.READING).
            sorted(comparing(Task::getCreatedOn)).
            map(Task::getTitle).
            limit(n).
            collect(toList());
}

可以像如下代码这样,同时利用skip方法和limit措施来创制某一页。

// page starts from 0. So to view a second page `page` will be 1 and n will be 5.
//page从0开始,所以要查看第二页的话,`page`应该为1,n应该为5
List<String> readingTaskTitles = tasks.stream().
                filter(task -> task.getType() == TaskType.READING).
                sorted(comparing(Task::getCreatedOn).reversed()).
                map(Task::getTitle).
                skip(page * n).
                limit(n).
                collect(toList());

Example 4:总计意况为reading的task的数量

要收获所有正处在reading的task的数额,我们得以在stream中应用count格局来收获,这么些点子是一个极端方法。

public long countAllReadingTasks(List<Task> tasks) {
    return tasks.stream().
            filter(task -> task.getType() == TaskType.READING).
            count();
}

Example 5: 非重复的列出富有task中的全部标签

要找出不重复的竹签,我们需要下边几个步骤

  1. 得到每个task中的标签。
  2. 把所有的标签放到一个stream中。
  3. 剔除重复的价签。
  4. 把最后结果装进一个列表中。

率先步和第二步可以透过在stream上调用flatMap来得到。flatMap操作把经过调用task.getTags().stream收获的一一stream合成到一个stream。一旦大家把具备的tag放到一个stream中,大家就足以因而调用distinct措施来取得非重复的tag。

private static List<String> allDistinctTags(List<Task> tasks) {
        return tasks.stream().flatMap(task -> task.getTags().stream()).distinct().collect(toList());
}

Example 6: 检查是否享有reading的task都有book标签

Stream
API有局部得以用来检测数据汇总是否含有某个给定属性的方法,allMatch,anyMatch,noneMatch,findFirst,findAny。要看清是否拥有意况为reading的task的title中都带有books标签,能够用如下代码来贯彻:

public boolean isAllReadingTasksWithTagBooks(List<Task> tasks) {
    return tasks.stream().
            filter(task -> task.getType() == TaskType.READING).
            allMatch(task -> task.getTags().contains("books"));
}

要咬定所有reading的task中是否留存一个task包含java8标签,可以由此anyMatch来贯彻,代码如下:

public boolean isAnyReadingTasksWithTagJava8(List<Task> tasks) {
    return tasks.stream().
            filter(task -> task.getType() == TaskType.READING).
            anyMatch(task -> task.getTags().contains("java8"));
}

Example 7: 创设一个有所title的总览

当您想要创造一个装有title的总览时就可以运用reduce操作,reduce可以把stream变成成一个值。reduce函数接受一个可以用来连接stream中具有因素的lambda表明式。

public String joinAllTaskTitles(List<Task> tasks) {
    return tasks.stream().
            map(Task::getTitle).
            reduce((first, second) -> first + " *** " + second).
            get();
}

Example 8: 基本类型stream的操作

除了周边的依据对象的stream,Java8对诸如int,long,double等主导类型也提供了一定的stream。下面一起来看有的主导类型的stream的例子。

要开创一个值区间,可以调用range方法。range主意创立一个值为0到9的stream,不带有10。

IntStream.range(0, 10).forEach(System.out::println);

rangeClosed模式允许我们创制一个涵盖上限值的stream。因而,下边的代码会暴发一个从1到10的stream。

IntStream.rangeClosed(1, 10).forEach(System.out::println);

还可以够像下边这样,通过在着力项目标stream上应用iterate格局来成立无限的stream:

LongStream infiniteStream = LongStream.iterate(1, el -> el + 1);

要从一个极致的stream中过滤出所有偶数,可以用如下代码来实现:

infiniteStream.filter(el -> el % 2 == 0).forEach(System.out::println);

能够透过利用limit操作来现在结果stream的个数,代码如下:
We can limit the resulting stream by using the limit operation as
shown below.

infiniteStream.filter(el -> el % 2 == 0).limit(100).forEach(System.out::println);

Example 9: 为数组创制stream

可以像如下代码这样,通过调用Arrays类的静态方法stream来把为数组建立stream:

String[] tags = {"java", "git", "lambdas", "machine-learning"};
Arrays.stream(tags).map(String::toUpperCase).forEach(System.out::println);

仍是可以够像如下这样,依照数组中一定初始下标和竣工下标来制造stream。这里的原初下标包括在内,而得了下标不分包在内。

Arrays.stream(tags, 1, 3).map(String::toUpperCase).forEach(System.out::println);

Parallel Streams并发的stream

利用Stream有一个优势在于,由于stream选取其中迭代,所以java库可以使得的管住处理并发。可以在一个stream上调用parallel方法来使一个stream处于并行。parallel主意的底部实现基于JDK7中引入的fork-joinAPI。默认意况下,它会发出与机具CPU数量相等的线程。上面的代码中,我们依据拍卖它们的线程来对将数字分组。在第4节上校学习collectgroupingBy函数,现在暂时通晓为它可以按照一个key来对元素举行分组。

public class ParallelStreamExample {

    public static void main(String[] args) {
        Map<String, List<Integer>> numbersPerThread = IntStream.rangeClosed(1, 160)
                .parallel()
                .boxed()
                .collect(groupingBy(i -> Thread.currentThread().getName()));

        numbersPerThread.forEach((k, v) -> System.out.println(String.format("%s >> %s", k, v)));
    }
}

在自家的机器上,打印的结果如下:

ForkJoinPool.commonPool-worker-7 >> [46, 47, 48, 49, 50]
ForkJoinPool.commonPool-worker-1 >> [41, 42, 43, 44, 45, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130]
ForkJoinPool.commonPool-worker-2 >> [146, 147, 148, 149, 150]
main >> [106, 107, 108, 109, 110]
ForkJoinPool.commonPool-worker-5 >> [71, 72, 73, 74, 75]
ForkJoinPool.commonPool-worker-6 >> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160]
ForkJoinPool.commonPool-worker-3 >> [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 76, 77, 78, 79, 80]
ForkJoinPool.commonPool-worker-4 >> [91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145]

并不是每个工作的线程都处理相等数量的数字,可以通过改变系统特性来决定fork-join线程池的多少System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "2")

除此以外一个会用到parallel操作的例子是,当你像下边这样要处理一个URL的列表时:

String[] urls = {"https://www.google.co.in/", "https://twitter.com/", "http://www.facebook.com/"};
Arrays.stream(urls).parallel().map(url -> getUrlContent(url)).forEach(System.out::println);

若果你想更好的精晓如何时候理应利用并发的stream,推荐您读书由Doug
Lea和任何几位Java大牛写的稿子http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图