新機能のjava8の一つ:(リスト、地図データ処理を含む)の処理データをストリーミングします。

I.はじめにストリーミング

私はjava8は、ストリーミングと接触すると、私の第一印象は、我々は通常の行で実現することができるストリーミングによる操作を完了するために、複数行のコードを必要とする一連の操作の多くはシンプルになるようにストリーミングされます。例えば、我々はすべての偶数スクリーニング備え整数の集合をしたい、と返された新しいリストにそれをカプセル化し、その後、java8前に、私たちは、次のコードによって達成される必要があります。

List<Integer> evens = new ArrayList<>();
for (final Integer num : nums) {
    if (num % 2 == 0) {
        evens.add(num);
    }
}

java8ストリーミング処理することにより、我々は、コードは単純化することができます。

List<Integer> evens = nums.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());

簡単に言えば、上記のステートメントは、この行の意味で説明し、stream()操作は、フローのセットに変換されますfilter()、我々は最終的に合格し、すべての偶数ラムダ式によって選択され、ここで、私たちのカスタムスクリーニングプロセスの実施collect()結果のカプセル化処理を、そしてによってCollectors.toList()指定さその一覧返されるコレクションにパッケージ化されています。

上記の例から分かるように、大幅に処理ストリーミングjava8は、等アレイ、ファイル、を含むコレクションは、限り、それがストリームに変換することができるように、我々は、ストリーミングを使用することができるだけでなく、実際には、コレクションのための操作を簡略化我々はそれを操作するようにSQL文を書くのが好き。反復プロセス内の対流によって達成java8は、プロセスストリームは、3つの部分に分けることができる:ストリームは、中間動作、端末の動作に変換されます。図は次のとおりです。

例のセットでは、呼び出すために動作し、私たち最初の必要性ストリーミングstream()ストリームに変換する機能をし、適切なを呼び出して中间操作、そのようなことで最終的には、変換をスクリーニングし、そしてなど、コレクションのために行う必要性を達成するために终端操作、上記の結果をカプセル化します私たちが必要とするフォームに戻ります。

二つの中間操作

例では、後者を実証するために私たちは、簡単なエンティティクラスの学生を定義します。

public class Student {

    /** 学号 */
    private long id;

    private String name;

    private int age;

    /** 年级 */
    private int grade;

    /** 专业 */
    private String major;

    /** 学校 */
    private String school;

    // 省略getter和setter
}
// 初始化
List<Student> students = new ArrayList<Student>() {
    {
        add(new Student(20160001, "孔明", 20, 1, "土木工程", "武汉大学"));
        add(new Student(20160002, "伯约", 21, 2, "信息安全", "武汉大学"));
        add(new Student(20160003, "玄德", 22, 3, "经济管理", "武汉大学"));
        add(new Student(20160004, "云长", 21, 2, "信息安全", "武汉大学"));
        add(new Student(20161001, "翼德", 21, 2, "机械与自动化", "华中科技大学"));
        add(new Student(20161002, "元直", 23, 4, "土木工程", "华中科技大学"));
        add(new Student(20161003, "奉孝", 23, 4, "计算机科学", "华中科技大学"));
        add(new Student(20162001, "仲谋", 22, 3, "土木工程", "浙江大学"));
        add(new Student(20162002, "鲁肃", 23, 4, "计算机科学", "浙江大学"));
        add(new Student(20163001, "丁奉", 24, 5, "土木工程", "南京大学"));
    }
};

2.1フィルタ

定義により、フィルタリングするフィルタ要素の所与のセットの要件に応じたとしてjava8条件スクリーニング手順満たすフィルタ、別個の、限界をスキップ。

フィルタ
我々のように定義されたフィルタを使用する方法を実証した前の例では:Stream<T> filter(Predicate<? super T> predicate)、フィルタ述語を取りPredicate、我々は導入ラムダ式を導入したとき、我々は、フィルタ条件を定義することができる述語はPredicate含ま機能インタフェースでありますtest(T t)メソッドが返しますboolean今、私たちは、コレクションから欲しいstudents我々はフィルタに渡されたパラメータとしてフィルタ、およびフィルタ操作で実現することができ、すべての武漢大学の学生からフィルタ:

List<Student> whuStudents = students.stream()
                                    .filter(student -> "武汉大学".equals(student.getSchool()))
                         
           .collect(Collectors.toList());

明確な
明確な操作は、追加我々はSQL文を書くときに似ているDISTINCT明確なに基づき、再処理のために行く、キーワードをObject.equals(Object)バック例の最初に、実現、我々は偶数のすべての非反復をフィルタリングしたいと、あなたは明確な操作を追加することができます:

List<Integer> evens = nums.stream()
                        .filter(num -> num % 2 == 0).distinct()
                        .collect(Collectors.toList());

制限
制限操作は、SQLステートメントに類似しているLIMITセットのサイズが少ないnよりも、実際の長さは、次の例に戻り最初の2つのプロのように、戻された場合には、最初のn個の要素を含むリターン・フローを制限する、キーワードが、機能は比較的弱いです以下のための土木工程専攻:

List<Student> civilStudents = students.stream()
                                    .filter(student -> "土木工程".equals(student.getMajor())).limit(2)
                                    .collect(Collectors.toList());

制限を言えば、私は別のストリームの動作について言及する必要がありますsorted対流ソートのための操作要素は、要素実装する必要があり、比較する要件をソートしComparable実現していない場合、それは問題で、我々は比較できないが、インターフェースはにパラメータとして渡されsorted(Comparator<? super T> comparator)、たとえば、私たちはプロの土木の学生うちフィルタ、およびプレスにしたいです、年齢、小規模から大規模まで、それは次のように実装することができ、2最年少の学生をスクリーニングしました。

List<Student> sortedCivilStudents = students.stream()
                                            .filter(student -> "土木工程".equals(student.getMajor())).sorted((s1, s2) -> s1.getAge() - s2.getAge())
                                            .limit(2)
                                            .collect(Collectors.toList());

スキップ
スキップ操作およびリミットの動作は逆に、その文字通りの意味として、例えば、我々は、それは次のように実装することができ、学生の後に土木工学2のソートを知りたい、最初のn個の要素をスキップすることです:

List<Student> civilStudents = students.stream()
                                    .filter(student -> "土木工程".equals(student.getMajor()))
                                    .skip(2)
                                    .collect(Collectors.toList());

nが設定された条件を満足するよりも大きい場合にスキップすることによって、最初の2つの要素をスキップし、バック構造の長さによって返されたストリームのすべての要素は、空のセットを返します。

2.2マッピング

SQLでは、とSELECTフィールド名を追加する必要が戻っキーワード、あなただけの私たちに必要なフィールドデータをエクスポートすることができ、およびマッピング操作は、この目的を達成するためにストリーミングされ、処理ストリーミングjava8で、主にマッピング操作の2種類が含まれています:マップとflatMap。

マップ
私たちは次のことを達成するために具体的には、学生の名前を文字列として学生の物理的なマップをマッピングすることで、上のフィルタでフィルタリングすることができるように、すべてのプロのコンピュータ科学の学生の名前をフィルタしたいという仮定に基づいてイラストを、:

List<String> names = students.stream()
                            .filter(student -> "计算机科学".equals(student.getMajor()))
                            .map(Student::getName).collect(Collectors.toList());

加えて、このようなベースマップの上、java8も提供しmapToDouble(ToDoubleFunction<? super T> mapper)mapToInt(ToIntFunction<? super T> mapper)mapToLong(ToLongFunction<? super T> mapper)私たちはすべての年齢やコンピュータサイエンスの学生の数をカウントしたいようにそれぞれ戻り流の種類に対応するこれらのマップは、java8は、プロとしてストリームにいくつかの特別なアクションを設定しました、我々は次のことを実現することができます。

int totalAge = students.stream()
                    .filter(student -> "计算机科学".equals(student.getMajor()))
                    .mapToInt(Student::getAge).sum();

学生が直接、年齢に応じてマッピングされIntStream、私たちが提供する直接呼び出すことができますsum()自分の目標を達成するための方法を、これらの値を使用する利点に加えて、パフォーマンスの消費によってもたらされる流れJVMボクシングの操作を回避することもあります。

flatMapの
差分マップとflatMapことを  flatMapストリームの各値は、ストリームを流入する平坦次いで、ストリームに変換されます  。例えば、我々は文字列の配列を持っていると仮定しString[] strs = {"java8", "is", "easy", "to", "use"};、我々はすべて我々が最初に次のことを達成するために思うかもしれない、配列の個別の文字を構成エクスポートします:

List<String[]> distinctStrs = Arrays.stream(strs)
                                .map(str -> str.split(""))  // 映射成为Stream<String[]>
                                .distinct()
                                .collect(Collectors.toList());

for(String[] strings : distinctStrs){ System.out.printLn(ArraystoString(strings)); }操作マップを実行した後、我々が到達したいので、異なる動作を行うが、文字列配列との比較に基づいている場合には、文字列(文字列を構成する文字列)を複数含む流れを得ます目的は、この時点で出力されます。

[j, a, v, a, 8]
[i, s]
[e, a, s, y]
[t, o]
[u, s, e]

唯一の明確な流れのためにすること、すなわち、私たちの目的を達成するために動作する複数の文字が含まれているStream<String>操作を行います。この時点で、flatMap私達は私達の目的を達成することができます:

List<String> distinctStrs = Arrays.stream(strs)
                                .map(str -> str.split(""))  // 映射成为Stream<String[]>
                                .flatMap(Arrays::stream)  // 扁平化为Stream<String>
                                .distinct()
                                .collect(Collectors.toList());

マッピングflatMapによって得られたマップStream<String[]>各流によって文字列の配列にマップに、Stream<String>、これらの小さなストリームとは、すべての文字列からなる群れに平坦化Steam<String>は、我々の目的を達成することが可能です。
マップと同様、flatMapは、特定のタイプのマッピング動作を提供します:flatMapToDouble(Function<? super T,? extends DoubleStream> mapper)flatMapToInt(Function<? super T,? extends IntStream> mapper)flatMapToLong(Function<? super T,? extends LongStream> mapper)

III。ターミナル操作

端末操作は、プロセスストリームの最後のステップは、我々は、端末、削減や他の操作で対流ルックアップ操作を実現することが可能です。

3.1検索

allMatch
:真すべて満たすリターンは、例えば、我々はすべての学生が、年齢の少なくとも18歳であるかどうかを確認したい、それはとして実装することができればallMatchは、すべて満たしているかどうか、指定されたパラメータの挙動を検出するために使用しました

boolean isAdult = students.stream().allMatch(student -> student.getAge() >= 18);

AnyMatch
AnyMatch満たしている場合はtrueを返し、指定された動作を満たすために、1つのまたは複数のパラメータの存在を検出することで、例えば、我々はそれのように実装することができ、武漢大学の学生があるかどうかを検出します:

boolean hasWhu = students.stream().anyMatch(student -> "武汉大学".equals(student.getSchool()));

noneMathch
真は、例えば、我々はコンピュータサイエンスの学生が存在しないために、あなたは以下を達成することができ、プロのかどうかを検出する場合の挙動を検出するためのnoneMatchは、指定された要素が存在しません満たし、その後、ノーリターンはありません。

boolean noneCs = students.stream().noneMatch(student -> "计算机科学".equals(student.getMajor()));

この関数は、FindFirst
FindFirstのは、私たちは、あなたが次のことを達成することができ、最初の行ではプロの土木学生を選出したいなど、条件を満たすために最初の要素を返すために使用しました:

Optional<Student> optStu = students.stream().filter(student -> "土木工程".equals(student.getMajor())).findFirst();

FindFirstのパラメータを運ばない、特定の検索基準は、我々がFindFirstのリターンを見つけることができるだけでなく、フィルタによってタイプオプションをある設定することができます。

findAny
findAny相対FindFirstの違いは、それは、findAnyは必ずしも最初の1に戻っているが、任意に戻るには、例えば、私たちは次のことを達成することができ、プロの土木工学の学生のいずれかを返すようにしたいです:

Optional<Student> optStu = students.stream().filter(student -> "土木工程".equals(student.getMajor())).findAny();

シーケンシャルストリーミングのため実際には、次の1我々が導入されているため、結果のFindFirst findAnyリターンは、なぜこの設計のように、同じである並列ストリーミングを我々はパラレルストリーミング表情を有効にすると、最初の要素は、常に多くの制約も、そうでない場合は特別なニーズ、十分に並列ストリーミングでのFindFirstよりfindAnyのパフォーマンスを使用します。

3.2削減

前の例では、主にすることによりcollect(Collectors.toList())、データなどの新しいセットを返さない私の目標として、パッケージを返されたが、更なる計算のために収集操作後パラメータに、我々は、コレクションの削減の実施形態を使用することができます操作。java8のストリーミング処理を提供しreduce、この目的を達成するための方法を。

私たちは、フロントmapToIntうStream<Student>にマッピングされIntStream、そしてを通じてIntStream和法すべての学生の年齢を決定して、実際には、我々は縮小操作を通過し、この目的を達成することができ、次のことを達成するために:

// 前面例子中的方法
int totalAge = students.stream()
                .filter(student -> "计算机科学".equals(student.getMajor()))
                .mapToInt(Student::getAge).sum();
// 归约操作
int totalAge = students.stream()
                .filter(student -> "计算机科学".equals(student.getMajor()))
                .map(Student::getAge)
                .reduce(0, (a, b) -> a + b);

// 进一步简化
int totalAge2 = students.stream()
                .filter(student -> "计算机科学".equals(student.getMajor()))
                .map(Student::getAge)
                .reduce(0, Integer::sum);

// 采用无初始值的重载版本,需要注意返回Optional
Optional<Integer> totalAge = students.stream()
                .filter(student -> "计算机科学".equals(student.getMajor()))
                .map(Student::getAge)
                .reduce(Integer::sum);  // 去掉初始值

3.3コレクション

フロント使ってcollect(Collectors.toList())簡単なコレクション操作を、パッケージには、同様に対応する、処理結果であるtoSettoMapために組織した結果について、当社のニーズを満たします。これらのメソッドからあるjava.util.stream.Collectors、私たちは、コレクタを呼び出すことができます。

3.3.1削減

コレクタはまた、対応する縮小操作を提供するが、内部に及び実装は容器にコレクタ可変低減操作に適し、異なる低減、これらは一般コレクタに基づいてCollectors.reducing()実装。

例1:学生の総数を探します

long count = students.stream().collect(Collectors.counting());

// 进一步简化
long count = students.stream().count();

例2:最大値と最小年齢を探します

// 求最大年龄
Optional<Student> olderStudent = students.stream().collect(Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge()));

// 进一步简化
Optional<Student> olderStudent2 = students.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge)));

// 求最小年龄
Optional<Student> olderStudent3 = students.stream().collect(Collectors.minBy(Comparator.comparing(Student::getAge)));

例3:総和を求めるための年齢

int totalAge4 = students.stream().collect(Collectors.summingInt(Student::getAge));

対応がありますsummingLongsummingDouble

例4:平均年齢

double avgAge = students.stream().collect(Collectors.averagingInt(Student::getAge));

対応がありますaveragingLongaveragingDouble

実施例5:要素の一回限りの数、合計、平均、最大値、最小値

IntSummaryStatistics statistics = students.stream().collect(Collectors.summarizingInt(Student::getAge));

出力:

IntSummaryStatistics{count=10, sum=220, min=20, average=22.000000, max=24}

対応がありますsummarizingLongsummarizingDouble

例6:文字列の連結

String names = students.stream().map(Student::getName).collect(Collectors.joining());
// 输出:孔明伯约玄德云长翼德元直奉孝仲谋鲁肃丁奉
String names = students.stream().map(Student::getName).collect(Collectors.joining(", "));
// 输出:孔明, 伯约, 玄德, 云长, 翼德, 元直, 奉孝, 仲谋, 鲁肃, 丁奉

3.3.2パケット

データベース操作では、我々はできるGROUP BYクエリキーワードにグループデータを、java8ストリーミングプロセスは、我々はまた、この機能を提供するCollectors.groupingBy操作のセットに。たとえば、私たちは生徒が、学校によってグループ化されているトップことができます。

Map<String, List<Student>> groups = students.stream().collect(Collectors.groupingBy(Student::getSchool));

groupingByクラシファイアを受けてFunction<? super T, ? extends K> classifier必要な分類結果を達成するために、我々はできるカスタム分類。

上に示した基であり、我々はまた、達成するために、複数の分類器を定義することができる  グループの複数のレベルを、我々は次のことを達成するためにグループの学校で専門的な根拠によってグループ化されることを望むなど、:

Map<String, Map<String, List<Student>>> groups2 = students.stream().collect(
                Collectors.groupingBy(Student::getSchool,  // 一级分组,按学校
                Collectors.groupingBy(Student::getMajor)));  // 二级分组,按专业

実際には、groupingBy第二引数は、groupingByを渡すこともあらゆる渡すことができますされていないだけCollectorの種類を、例えば、我々は渡すことができCollector.counting、各グループの数をカウントします。

Map<String, Long> groups = students.stream().collect(Collectors.groupingBy(Student::getSchool, Collectors.counting()));

私たちは二番目の引数を追加しない場合、コンパイラはデフォルトを追加するために私たちを助けますCollectors.toList()

3.3.3パーティション

パーティションは、パケットの特別なケースとして見ることができる、唯一の2つのパーティションの場合にはキー:trueまたはfalseは、オブジェクトは、ストリーミング使用java8設定された条件に応じて二つに分割することであるollectors.partitioningBy()分割の実現方法を、方法述語を受け、例えば、我々は学生が軍事と非軍事の大学生の大学生に分割されますことを願って、それは以下を達成することができます:

Map<Boolean, List<Student>> partition = students.stream().collect(Collectors.partitioningBy(student -> "武汉大学".equals(student.getSchool())));

パーティションの相対的なパケット利点は、我々は同時に結果の2種類を得ることができるということです、あなたは、このような配列が奇数と偶数に分かれているなど、いくつかのシナリオのステップに私たちが必要とするすべての結果を得ることができます。

すべてのコレクタは、インタフェースの上記の説明から達成してjava.util.stream.Collector次のように、インターフェースが定義されます。

public interface Collector<T, A, R> {
    /**
     * A function that creates and returns a new mutable result container.
     *
     * @return a function which returns a new, mutable result container
     */
    Supplier<A> supplier();

    /**
     * A function that folds a value into a mutable result container.
     *
     * @return a function which folds a value into a mutable result container
     */
    BiConsumer<A, T> accumulator();

    /**
     * A function that accepts two partial results and merges them.  The
     * combiner function may fold state from one argument into the other and
     * return that, or may return a new result container.
     *
     * @return a function which combines two partial results into a combined
     * result
     */
    BinaryOperator<A> combiner();

    /**
     * Perform the final transformation from the intermediate accumulation type
     * {@code A} to the final result type {@code R}.
     *
     * <p>If the characteristic {@code IDENTITY_TRANSFORM} is
     * set, this function may be presumed to be an identity transform with an
     * unchecked cast from {@code A} to {@code R}.
     *
     * @return a function which transforms the intermediate result to the final
     * result
     */
    Function<A, R> finisher();

    /**
     * Returns a {@code Set} of {@code Collector.Characteristics} indicating
     * the characteristics of this Collector.  This set should be immutable.
     *
     * @return an immutable set of collector characteristics
     */
    Set<Characteristics> characteristics();

}

また、ここでは拡大もはや、独自のトラップを定義するには、このインターフェイスを実装することはできません。

IV。並列処理のストリーミングデータ

ストリーミング多くは適している  分割統治  の考え、大幅コードのパフォーマンスを向上させる大規模なコレクションを扱うには、java8デザイナーはそう提供し、これを見てきました  パラレルストリーミングを上記の例では、呼び出しているstream()ストリーミングを開始するための方法を、java8も設けられparallelStream()、平行流の処理を開始するparallelStream()フォーク-結合java7本質的に達成されたフレームに基づいて、ホストのコアの数にデフォルトのスレッド数。

パラレルスタートストリーミングは、シンプルであるだけでする必要がありますstream()置き換えるparallelStream()缶を、それが平行であることから、マルチスレッドあなたはそれが価値が並列実行順序の効率よりも(必ずしも高くないかどうかを並列最初の確認を有効にする前に、セキュリティ上の問題を伴います)、他のスレッドの安全性を確保することです。この二つは保証できない、そして並列無意味では、すべての後に、結果はスピードよりも重要である、時間後に再度詳細に特定の実装とベストプラクティスパラレルストリームデータ処理を分析します。

出版元の記事 ウォンの賞賛1 ビュー73

おすすめ

転載: blog.csdn.net/f_u_c_k_le/article/details/105276953