深さ分析:java 8の新しい特性lambdaとstreamストリーム、あなたはマスターしましたか?


1.lambda式
1.1 lambdaとは
Javaを例にとると、int a=1などのjava変数に値を割り当てることができますが、1つのメソッドに対して、1つのコードも1つの変数に与えられ、このコード、あるいは変数に与えられた関数に対してlambda式です.
//     
int a = 1;

//         
var = public void fun(int x){
    x+1;
}

//    
var = (x)->x+1;

1.2 javaはなぜlambdaを導入するのか
Lambdaは関数式プログラミングサービスのプログラミング言語の共通性の------関数式プログラミングとは何ですか?関数式プログラミングは、プログラムをどのように記述するかという方法論であり、演算過程をできるだけ一連のネストされた関数呼び出しに記述することを主な考え方とし、FPは「everything is lambda」を強調し、論理処理における不変性の重要性を強調する
OOPは「everything is object」とobject間のメッセージングを強調します.メッセージングによって各Objectの内部状態を変更しますが、コードの作成は実際にはオブジェクトを使用できない場合が多いです.例えば、データのセットを加工し、クエリーし、集約し、集約した後にソートし、joinし、ソートし、集約し、変換します(map)最終的な結果が得られます.この過程は、FPの関数で自然に
result = func1(func2(func3...funcN(x))))

JAvaは元oopの思想の上で関数式のプログラミングの使用を増加するためにjava 8の上でlambda関数の新しい特性を増加しました
それ以外にlambda式の導入はコードをより簡潔にし,汚染環境の不要な実装クラスを過剰に生成することを避けることができる(以下)
1.3 lambda式の使用方法
Lambda式の導入は、環境を汚染する実装クラスの生成を回避します.lambda式は変数に値を割り当てることができます.この変数のタイプは何ですか.javaでは、すべてのLambdaのタイプは1つのインタフェースですが、Lambda式自体は、このインタフェースの実装であれば、このインタフェースには3つの特徴が必要で、これらの特徴を備えたインタフェースが必要です関数インタフェースと呼ばれています
関数インタフェースは、デフォルトの実装として抽象メソッドdefaultメソッドが1つしかありません.抽象メソッドを計上しません.インタフェースがjava.lang.Objectを上書きするグローバルメソッドの1つを宣言した抽象メソッドがある場合、インタフェースの抽象メソッドの数には計上されません.インタフェースの実装はjava.lang.Objectまたは他の場所の実装がどのようにlambda式を使用するか、例えばComparatorインタフェースは関数インタフェースなので、lambda式を使用することができます.前にcomparatorを使用してlistをソートすると、次のようになります.
List list = new ArrayList<>();
Collections.sort(list, new Comparator() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1-o2;
    }
});

実際に役に立つのはreturn o 1-o 2で、上のコードはlambda式で次のように書かれています.
Collections.sort(list, ((o1, o2) -> o1-o2));

Lambda式の基礎構文:Lambdaオペレータ->Lambda式を2つの部分に分割する:左側:Lambda式のパラメータリスト;右側:Lambda式で実行する機能、すなわちLambda体;
     :   ,    
() -> System.out.println("Hello Lambda!");

     :     ,      
(x) -> System.out.println(x)

     :       ,         
x -> System.out.println(x)

     :        ,    ,   Lambda        
Comparator com = (x, y) -> {
System.out.println("     ");
return Integer.compare(x, y);
};

     :  Lambda         , return             
Comparator com = (x, y) -> Integer.compare(x, y);

1.4 lambda式メソッドリファレンス、コンストラクタリファレンス、配列リファレンス
メソッドリファレンスLambdaボディの機能の場合は、すでに実装されているメソッドがあり、メソッドリファレンスを使用することができます.
      ::      
   ::      
   ::      

①メソッドが参照するメソッドのパラメータリストと戻り値タイプは、関数インタフェースの抽象メソッドのパラメータリストと戻り値タイプと一致する必要があります!②Lambdaのパラメータリストの最初のパラメータがインスタンスメソッドの呼び出し者であり、2番目のパラメータ(または参照なし)がインスタンスメソッドのパラメータである場合、フォーマット:ClassName::MethodName::
//      ::      
@Test
public void test1(){
        //          
    Employee emp = new Employee(101, "  ", 18, 9999);
    Supplier sup = () -> emp.getName();
    System.out.println(sup.get());  

    System.out.println("----------------------------------");   
        //          
    Supplier sup2 = emp::getName;
    System.out.println(sup2.get());
}

//   ::      
@Test
public void test2(){
    Comparator com = (x, y) -> Integer.compare(x, y);  
    System.out.println("-------------------------------------");     
    Comparator com2 = Integer::compare;
}

//   ::      
@Test
public void test3(){
    BiPredicate bp = (x, y) -> x.equals(y);
    System.out.println(bp.test("abcde", "abcde"));

    System.out.println("-----------------------------------------");

    BiPredicate bp2 = String::equals;
    System.out.println(bp2.test("abc", "abc"));

}

コンストラクタリファレンス
personクラスには2つのコンストラクタがあります
class Person {
    String firstName;
    String lastName;

    Person() {}

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

現在、personクラスを生成するためのファクトリインタフェースがあります.
// Person   
interface PersonFactory

{ P create(String firstName, String lastName); }


このファクトリインタフェースを手動で実現する代わりに、Personクラスのコンストラクタをキーワードで参照することができます.
//      Person    
PersonFactory personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");

Person::newこのコードは、Personクラスのコンストラクタを直接参照できます.Javaコンパイラはコンテキストに基づいて正しいコンストラクタを選択してPersonFactory.createメソッドを実現できます.
2.1 Streamとは
Java 8は新しいStream APIを導入しており、ここでのStreamはI/Oストリームとは異なり、Java 8のStreamは対集合(Collection)であるオブジェクト機能の強化は、集合オブジェクトに対して様々な非常に便利で効率的な集約操作、または大量のデータ操作を行うことに集中しています.StreamAPIは、同じように新しく登場したLambda式を利用して、プログラミング効率とプログラムの可読性を大幅に向上させます.
Streamは反復器のようなもので、一方的で、往復できない.データは一度しか遍歴できない.一度遍歴した後、使い果たしてしまう.流水が目の前を流れているように、一度行っても戻らない.
List myList =
    Arrays.asList("a1", "a2", "b1", "c2", "c1");

myList
    .stream() //    
    .filter(s -> s.startsWith("c")) //     ,     c        
    .map(String::toUpperCase) //      
    .sorted() //   
    .forEach(System.out::println); // for     

①:            ,  ,            ,           。    filter   ,map     ,sorted   ,       。
②:                ,     void          。     forEach           

上はStreamの簡単な実用で、それも関数式のプログラミングであることを見ることができて、もっと多くの業務のロジックを表現しました
2.2常用api
Streamの作成
1. Arrays.stream()
日常的なプログラミングで配列に直面する場合、Arrays.stream()メソッドを使用してStreamを使用できます.
Integer[] array = new Integer[]{3,4,8,16,19,27,23,99,76,232,33,96};
long count = Arrays.stream(array).filter(i->i>20).count();

2. Stream.of()
配列に対してArrays.stream()メソッドを使用するほか、Streamを使用して必要な配列をStreamに変換することもできます.このメソッドでは、転送配列だけでなく、配列をStreamに変換したり、複数のパラメータを転送したりして、パラメータを最終的にStreamに変換することもできます.
Integer[] array = new Integer[]{3,4,8,16,19,27,23,99,76,232,33,96};
long count = Stream.of(array).filter(i->i>20).count();
long sum = Stream.of(12,77,59,3,654).filter(i->i>20).mapToInt(Integer::intValue).sum();
System.out.println("count:"+count+",sum:"+sum);

3. Collection.stream()
これが最も一般的なStreamsです.CollectionはJavaの集合インタフェースの親インタフェースであるため、Javaの集合はこのインタフェースを継承または実装します.Javaの集合はこの方法でStreamを作成できます.
List numbers = new ArrayList<>();
numbers.add(3);
numbers.add(4);
numbers.add(8);
numbers.add(16);   
numbers.stream().forEach(number->{
    System.out.println(number);
});

4.filter
これはStreamのフィルタ変換です.この方法では、特定の条件を満たすすべての要素を含む新しいストリームが生成されます.filterは、Lambda式で表される関数をパラメータとして受け入れます.
List integerList = Lists.newArrayList();
integerList.add(15);
integerList.add(32);
integerList.add(5);
integerList.add(232);
integerList.add(56);
List after = integerList.stream()
                    .filter(i->i>50)
                    .collect(Collectors.toList());
System.out.println(after);//232,56

5.map
mapメソッドとは、ストリーム内の値を何らかの形式で変換することを意味します.パラメータとして変換する関数を渡す必要があります.
List integerList = Lists.newArrayList();
integerList.add(15);
integerList.add(32);
integerList.add(5);
integerList.add(232);
integerList.add(56);
// Integer     String  
List afterString = integerList.stream()
                .map(i->String.valueOf(i)).collect(Collectors.toList());

6.flatMap
複数のStreamを1つのStreamに接続します.この場合、Streamの値を新しい値に置き換えるのではなく、mapとは異なり、Streamオブジェクトを再生成して置き換えます.
List words = new ArrayList();
words.add("your");
words.add("name");

public static Stream characterStream(String s){  
    List result = new ArrayList<>();  
    for (char c : s.toCharArray()) 
        result.add(c);
    return result.stream();  
}

Stream> result = words.map(w -> characterStream(w));  
//[['y', 'o', 'u', 'r'], ['n', 'a', 'm', 'e']]
Stream letters = words.flatMap(w -> characterStream(w));
//['y', 'o', 'u', 'r', 'n', 'a', 'm', 'e'] 

7.limitメソッドとskipメソッド
Limit(n)メソッドは、n個の要素を含む新しいストリームを返します(総長がn未満の場合は元のストリームを返します)skip(n)メソッドは正反対で、前のn個の要素を捨ててlimitメソッドとskipメソッドで一緒に使用すると、日常的なページング機能を実現できます.
List pageList = myList.stream()
                  .skip(pageNumber*pageSize)
                  .limit(pageSize).collect(Collectors.toList());

8.distinctメソッドとsortedメソッド
distinctメソッドは、元のストリームの要素に基づいて同じ順序で重複要素を除去したストリームを返します.この操作は、前に読み込んだ要素を記憶する必要があることが明らかです.
List myTestList = Lists.newArrayList();
myTestList.add(10);
myTestList.add(39);
myTestList.add(10);
myTestList.add(78);
myTestList.add(10);
List distinctList = myTestList.stream()
                        .distinct().collect(Collectors.toList());
System.out.println("distinctList:"+distinctList);
    :
distinctList:[10, 39, 78]

sortedメソッドは、ストリーム全体を巡回し、任意の要素を生成する前にソートする必要があります.ソート後のセットの最初の要素が、ソートされていないセットの最後の1つになる可能性があるためです.
List myTestList = Lists.newArrayList();
myTestList.add(39);
myTestList.add(78);
myTestList.add(10);
myTestList.add(22);
myTestList.add(56);
List sortList = myTestList.stream()
                .sorted(Integer::compareTo).collect(Collectors.toList());
System.out.println("sortList:"+sortList);
    :
sortList:[10, 22, 39, 56, 78]

9.Collect
collectはストリームにリスト,map,などの一般的なデータ構造を生成する
         List ,         。
List thereList = hereList.stream().collect(Collectors.toList());

   Set      
Set thereSet = hereList.stream().collect(Collectors.toSet());

   Set ,  Set   ,    。
TreeSet treeSet = hereList.stream()
                    .collect(Collectors.toCollection(TreeSet::new));

10.集約操作
集約とは、プログラムで使用するためにストリームを1つの値に集約することです.集約方法は、sum、count、max、minを含む終了操作です.
long sum = Stream.of(12,77,59,3,654).filter(i->i>20).mapToInt(Integer::intValue).sum();
findFirst              ,    filter        
Integer first = hearList.stream().filter(i->i>100).findFirst().get();

findAny                      ,   ,               
Integer anyItem = hearList.parallelStream().filter(i->i>100).findAny().get();

11.グループ化
同じ特性を持つ値をグループ化するのは一般的な機能です
   Room          。

List roomList = Lists.newArrayList(
new Room(11,23,56),
new Room(11,84,48),
new Room(22,46,112),
new Room(22,75,62),
new Room(22,56,75),
new Room(33,92,224));

Map> groupMap = roomList.stream().collect(Collectors.groupingBy(Room::getHigh));
System.out.println("groupMap:"+groupMap);

2.3 Streamストリームの処理手順
Streamストリームの中間動作には遅延性があり、端末動作が存在する場合にのみ中間動作が実行される
Stream.of("d2", "a2", "b1", "b3", "c")
    .filter(s -> {
        System.out.println("filter: " + s);
        return true;
    });

このコードセグメントを実行すると、何も印刷されません.上のコードにforEach端末を追加すると、印刷内容が表示されます.
Stream.of("d2", "a2", "b1", "b3", "c")
    .filter(s -> {
        System.out.println("filter: " + s);
        return true;
    })
    .forEach(s -> System.out.println("forEach: " + s));
filter:  d2
forEach: d2
filter:  a2
forEach: a2
filter:  b1
forEach: b1
filter:  b3
forEach: b3
filter:  c
forEach: c

しかし、出力結果は、すべてのfilter操作の印刷文を先に印刷するものではないことがわかります.実際には、出力結果はチェーンが垂直に移動するにつれて、例えば、Streamがd 2要素の処理を開始すると、実際にはfilter操作を実行した後、forEach操作を実行し、次に2番目の要素を処理します.
なぜなら、パフォーマンスの観点からです.このように設計すると、次のような各要素に対する実際の操作数を減らすことができます.
Stream.of("d2", "a2", "b1", "b3", "c")
    .map(s -> {
        System.out.println("map: " + s);
        return s.toUpperCase(); //    
    })
    .anyMatch(s -> {
        System.out.println("anyMatch: " + s);
        return s.startsWith("A"); //      A       
    });

// map:      d2
// anyMatch: D2
// map:      a2
// anyMatch: A2

端末操作anyMatch()は、いずれかの要素がAを接頭辞としてtrueを返すとループが停止することを示す.したがって、d 2からマッチングを開始し、a 2にループするとtrueを返し、ループを停止する.
データストリームのチェーン呼び出しは垂直に実行されるため、mapはここで2回しか実行できません.水平実行に比べて、mapはすべての要素をmapに変換するのではなく、できるだけ少ない回数実行されます.
stream --> filter --> map --> sorted --> collect

2.4パラレルフロー
反復器とは異なり、Streamは並列化操作が可能であり、反復器は命令式、直列化操作しかできない.その名の通り、シリアル方式で遍歴すると、各itemが読み終わった後に次のitemを読む.Streamは平行処理能力を持ち、処理の過程は分けて治す.つまり、一つの大きなタスクを複数の小さなタスクに切り分ける.これは各タスクがすべて1つのアクション
//parallel                   
Stream.of(roomList).parallel();
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.parallelStream()
       .forEach(out::println);  
//         1、2、3、4、5、6、7、8、9,         

最後に
皆さんは何か分からないことがあったら、下に伝言を残して討論してください.