#ストリームAPI最良のガイドクール

第JavaStorm国民の関心は、よりエキサイティングな学習します

流れとラムダ、非常に簡単かつガスなっサンパウロコードと組み合わせて使用​​する場合のJava 8は、素晴らしい新機能ラムダ式の流れ(ストリーム)をもたらします。

スーパー動きは、コードを公開します

需要があれば、請求書情報データベースクエリを処理する必要があります。

  1. 請求書の金額を削除すると、10,000未満です。
  2. アウトフィルタリングされたデータをソートします。
  3. 請求書側のピン名取得注文後。

請求書モデル

@Builder
@Data
public class Invoice implements Serializable {
    /**
     * 销方名称
     */
    private String saleName;
    /**
     * 是否作废
     */
    private Boolean cancelFlag;
    /**
     * 开票金额
     */
    private BigDecimal amount;
    /**
     * 发票类型
     */
    private Integer type;
    /**
     * 明细条数
     */
    private Integer detailSize;
}

复制代码

私たちは、伝統的な方法を使用して、我々は前にテストデータを初期化します

public class StreamTest {

    private List<Invoice> invoiceList;

    @Before
    public void initData() {
        Invoice invoice = Invoice.builder().amount(BigDecimal.valueOf(100.02)).cancelFlag(false).detailSize(10)
                .saleName("广西制药").type(1).build();
        Invoice invoice2 = Invoice.builder().amount(BigDecimal.valueOf(89032478.9)).cancelFlag(false).detailSize(2)
                .saleName("深圳电子科技").type(1).build();
        Invoice invoice3 = Invoice.builder().amount(BigDecimal.valueOf(2077777889)).cancelFlag(true).detailSize(6)
                .saleName("宇宙心空").type(1).build();
        Invoice invoice4 = Invoice.builder().amount(BigDecimal.valueOf(356.8)).cancelFlag(false).detailSize(10)
                .saleName("孟达餐厅").type(2).build();
        Invoice invoice5 = Invoice.builder().amount(BigDecimal.valueOf(998.88)).cancelFlag(false).detailSize(0)
                .saleName("网红餐厅").type(2).build();
        Invoice invoice6 = Invoice.builder().amount(BigDecimal.valueOf(9009884.09)).cancelFlag(false).detailSize(1)
                .saleName("机动车").type(3).build();
        invoiceList = Stream.of(invoice, invoice2, invoice3, invoice4, invoice5, invoice6).collect(Collectors.toList());
        System.out.println("原始数据:" + invoiceList.toString());
    }
复制代码

Java8の実装に先立ち

/**
     * 筛选出金额小于 10000 的发票,根据金额排序,获取排序后的销方名称列表
     */
    @Test
    public void testJava7() {
        ArrayList<Invoice> lowInvoiceList = new ArrayList<>();
        //筛选出 金额小于 10000 的发票
        for (Invoice invoice: invoiceList) {
            if (invoice.getAmount().compareTo(BigDecimal.valueOf(10000)) < 0) {
                lowInvoiceList.add(invoice);
            }
        }
        // 对筛选出的发票排序
        lowInvoiceList.sort(new Comparator<Invoice>() {
            @Override
            public int compare(Invoice o1, Invoice o2) {
                return o1.getAmount().compareTo(o2.getAmount());
            }
        });
        // 获取排序后的销方名字
        ArrayList<String> nameList = new ArrayList<>();
        for (Invoice invoice : lowInvoiceList) {
            nameList.add(invoice.getSaleName());
        }

    }
复制代码

Java8、一度後のサンパウロガス操作。もはや、長いコードの臭い余分な時間を働かなければなりません

@Test
public void testJava8() {
  List<String> nameList = invoiceList.stream()
    .filter(item -> item.getAmount().compareTo(BigDecimal.valueOf(10000)) < 0)// 过滤数据
    .sorted(Comparator.comparing(Invoice::getAmount))// 对金额升序排序
    .map(Invoice::getSaleName)//提取名称
    .collect(Collectors.toList());//转换成list

}
复制代码

竜のサービスを感じ、1内のすべての空に連れて行きます。大幅にコードの量を減らします。

Niubi

そして今、再び需要

請求書データの分類をチェックアウトするには、それは地図<整数、一覧>データを返します。

Java7下の文言を想起し、私はこする何かがある、これはあまりにも面倒です。私はガールフレンドを保持するために、できるだけ早く仕事に戻ることはできません。

@Test
public void testGroupByTypeJava7() {
  HashMap<Integer, List<Invoice>> groupMap = new HashMap<>();
  for (Invoice invoice : invoiceList) {
    //存在则追加
    if (groupMap.containsKey(invoice.getType())) {
      groupMap.get(invoice.getType()).add(invoice);
    } else {
      // 不存在则初始化添加
      ArrayList<Invoice> invoices = new ArrayList<>();
      invoices.add(invoice);
      groupMap.put(invoice.getType(), invoices);
    }
  }
  System.out.println(groupMap.toString());
}
复制代码

その後、我々は、上記の要件を達成するためにサンパウロの操作コードのストリームを使用します

groupingByのグループ化

@Test
public void testGroupByTypeJava8() {
  Map<Integer, List<Invoice>> groupByTypeMap = invoiceList.stream().collect(Collectors.groupingBy(Invoice::getType));
}
复制代码

これは、単純な、粗、コードの黄龍ラインを破壊することです。

負荷力

ストリームとは何ですか?

ストリーム(ストリーム)データソースと支持重合操作からキュー要素であり、それはデータのデータ構造ではありませんが格納されていない場合、主な目的は、計算することです。

要素は、キューが形成され、オブジェクトの特定のタイプです。Javaの流れの中や要素を格納しませんが、オンデマンド・コンピューティング。データソースの起源。セット、配列、I / Oチャネル、発電機の発電機などであってもよいです。このようなフィルタ、マップ、削減見つけ、マッチ、ソートなどのSQL文の動作と同様の重合操作。コレクション異なる前の操作、ストリーム操作二つの基本的な特徴があります。

  • パイプライン:中間の操作は、ストリームオブジェクト自体を返します。このような動作は、フロースタイル(流動様式)として、パイプの複数の直列に接続されていてもよいです。これは、遅延実行(怠惰)、短絡(ショート)など、動作を最適化します。
  • 内部の反復:コレクションはイテレータの方法によって、または各ため、横断する前には、明示的に外部の反復と呼ばれる反復、外に設定してください。ストリームは、反復モードは、ビジター(訪問者)によって達成される内部方法を提供します。

ストリームを生成する方法

5つの主要な方法があります。

1.セット生成

Collection<String> collection = Arrays.asList("a", "b", "c");
Stream<String> streamOfCollection = collection.stream();
复制代码

アレイを生成することにより、2

int[] intArr = new int[]{1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(intArr);
复制代码

代わりに[即ちIntStream]フロー法によって生成され、処理フローが生成される数値をArrays.stream Stream<Integer>パフォーマンスを向上させるために、梱包を開梱の数値フロー計算処理を使用しないようにそれを追加します。

ストリームAPIはmapToInt、mapToDouble、mapToLongを提供する三つの方法、すなわち、ストリームオブジェクトストリーム[STREAM]は、対応する数値に変換され、この方法は、オブジェクトストリームにボックス化値の流れを提供

値生成3.

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
复制代码

方法のストリームによってストリームを生成、空の空のメソッドストリームstreamによって生成することができます

4.ファイルによって生成されます

Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
复制代码

Files.line法により得られたストリーム、及び結果として生じたストリームは、ファイルの各行に与えられています

生成された関数、反復及び機能から生成された2つの静的メソッドを生成するストリーム

反復子:反復子フローストリームが生成されるような反復法もわずか5発生、無限トランケーション限界対流法により行われる、第2の動作の関数として、2つのパラメータ最初の初期値を取り

Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
复制代码

ジェネレータ:ストリームのための値パラメータ型サプライヤーによってパラメータを受け付けます。生成された生成する流れは無限大であるため、切り捨てが対流限界によって行わ

Stream<Double> stream = Stream.generate(Math::random).limit(5);
复制代码

操作タイプの流れ

2つの主なタイプ

1.中間操作

ストリームは、ゼロ以上の中間の操作が続いてもよいです。その主な目的は、データマッピング/フィルタリングのいくつかの学位を作り、流れを開くことですし、次の操作を使用して、新しいストリームを返します。

そのような操作は不活性である、のみなど、地図、中間動作は直ちに以下に示す一般的なフィルタを有する端末トラバース動作まで待機する実際の必要性、そのようなメソッドの呼び出し、およびストリームの実際の開始を通過しません

2.端末操作

ストリームは、一つを有し、操作が行われた場合、ストリームが閉じられた唯一の端末操作は、もはや流れは、従ってもう一度生成されたソース・データ・ストリームをトラバースする必要性を横断することができ、操作することができません。行っターミナルの操作は、ストリームの本当の始まりを横断します。すぐ下に説明するように、その上で収集し、カウントされます。

中級操作API

フィルタースクリーニング

Stream<Invoice> invoiceStream = invoiceList.stream().filter(invoice -> invoice.getDetailSize() < 10);
复制代码

個別の削除重複要素

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().distinct();
复制代码

指定された制限ストリームの数を返します。

Stream<Invoice> invoiceStream = invoiceList.stream().limit(3);
复制代码

多くの方法で指定された流量限界を返す、パラメータ値> = 0を制限する必要があり、そうでない場合、例外がスローされます

スキップストリーム要素をスキップ

 List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
 Stream<Integer> stream = integerList.stream().skip(2);
复制代码

上記の例の前に2つの要素をスキップし、エレメンタリストリームでスキップ方法によってスキップされ、印刷結果は、そうでない場合、例外がスローされ、> = 0でなければならないパラメータの値をスキップし、2,3,4,5です。

地図フローマッピング

いわゆるフローマッピングは、他の受光素子に要素をマッピングすることです

List<String> stringList = Arrays.asList("Java 8", "Lambdas",  "In", "Action");
Stream<Integer> stream = stringList.stream().map(String::length);
复制代码

この例では、マップ・メソッドをマッピングすることによって達成することができる、完全な文字列 - >整数マッピング、マップ方法書によって上記の例の完了前 - >文字列のマッピング

flatMapストリーム変換

ストリームの各値は、別のストリームに変換されます。

List<String> wordList = Arrays.asList("Hello", "World");
        List<String> strList = wordList.stream()
                .map(w -> w.split(""))// 将元素根据 空格分隔字符的Stream<String[]>
                .flatMap(Arrays::stream)// 将Stream<String[]> 转换成 Stream<String>
                .distinct() //去重
                .collect(Collectors.toList());
        System.out.println(strList.toString());
复制代码

マップ(wは- > w.split( " 「)) 戻り値はStream<String[]>、我々が入手したいStream<String>>ストリーム変換- flatMapメソッドストリームによって行うことができます。だから、最終的な結果が印刷され[H, e, l, o, W, r, d]

要素が一致します

  1. allMatchすべての一致
if (invoiceList.stream().allMatch(Invoice::getCancelFlag)) {
  System.out.println("发票全是作废");
}
复制代码
  1. anyMatch 1試合

無効にされた請求書の印刷があります

if (invoiceList.stream().anyMatch(Invoice::getCancelFlag)) {
  System.out.println("存在作废发票");
}
复制代码

に相当

for (Invoice invoice : invoiceList) {
  if (invoice.getCancelFlag()) {
    System.out.println("存在作废发票");
    break;
  }
}
复制代码
  1. noneMatchすべての不一致
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().noneMatch(i -> i > 3)) {
    System.out.println("值都小于3");
}
复制代码

ターミナル事業

統計フロー内の要素の数

  1. 使用回数
long count = invoiceList.stream()
  .filter(item -> item.getAmount().compareTo(BigDecimal.valueOf(10000)) < 0)
  .count();
复制代码
  1. カウントを使用します
long count = invoiceList.stream()
  .filter(item -> item.getAmount().compareTo(BigDecimal.valueOf(10000)) < 0)
  .collect(Collectors.counting());
复制代码

収集と併せて使用される場合、最終的に、統計的方法の要素の数は、特に有用です

求めます

  1. まず、検索関数は、FindFirst
Optional<Invoice> first = invoiceList.stream()
  .filter(item -> item.getAmount().compareTo(BigDecimal.valueOf(10000)) < 0)
  .findFirst();
复制代码

FindFirstのことで10,000未満の量の最初の要素を見つけます

  1. ランダムfindAnyを探します
Optional<Invoice> any = invoiceList.stream()
  .filter(item -> item.getAmount().compareTo(BigDecimal.valueOf(10000)) < 0)
  .findAny();
复制代码

要素が10,000未満のプリントであり、なぜなら内部最適化、およびそれが3より大きくを満たすために最初の要素は、方法及び結果FindFirstの方法の結果として、終了したと認めるときは、前記方法によりfindAnyを見つけます。FindAny方法は、より良い並列ストリームを利用するため、FindFirstのより限定的方法で提供される[この記事は、並列に並列ストリームを導入しないであろう]

結合ストリーム要素を減らします

私たちは一連の値を合計したものと

jdk8前

int sum = 0;
for (int i : integerList) {
	sum += i;
}
复制代码

削減jdk8によって処理された後、

int sum = integerList.stream().reduce(0, (a, b) -> (a + b));
//还可以用方法引用写
int sum = integerList.stream().reduce(0, Integer::sum);
复制代码

このような統計的加算請求金額など

BigDecimal reduce = invoiceList.stream().map(Invoice::getAmount).reduce(BigDecimal.ZERO, (a, b) -> (a.add(b)));
复制代码

簡便法の参照を継続して使用します

BigDecimal reduce = invoiceList.stream().map(Invoice::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
复制代码

低減は、初期値が0である2つのパラメータを、受け付けBinaryOperator<T> accumulator、二つの要素には、新しい値を生成するために結合します

この方法は、さらに値の初期化メソッドをオーバーライドしない減らします

最小および最大流量を求めます

最小の最小値/最大値の最大値を取得することにより

Optional<BigDecimal> min = invoiceList.stream().map(Invoice::getAmount).min(BigDecimal::compareTo);
Optional<BigDecimal> max = invoiceList.stream().map(Invoice::getAmount).max(BigDecimal::compareTo);
复制代码

それはまたのように記述することができます

OptionalInt min1 = invoiceList.stream().mapToInt(Invoice::getDetailSize).min();
OptionalInt max1 = invoiceList.stream().mapToInt(Invoice::getDetailSize).max();
复制代码

フロー最小値min、maxの最大値取得したフロー処理パラメータを求めますComparator<? super T> comparator

最小minBy / maxByの最大値を取得することにより

invoiceList.stream().map(Invoice::getAmount).collect(Collectors.minBy(BigDecimal::compareTo)).get();
复制代码

最小値と最大取得することにより削減

Optional<BigDecimal> max = invoiceList.stream().map(Invoice::getAmount).reduce(BigDecimal::max);
复制代码

加算

summingIntにより、

Integer sum = invoiceList.stream().collect(Collectors.summingInt(Invoice::getDetailSize));
复制代码

データ型は二重である場合、長い、summingDouble、summingLong法を加算することにより行われます

によって削減

Integer sum = invoiceList.stream().map(Invoice::getDetailSize).reduce(0, Integer::sum);
复制代码

合計で、最高の言葉遣い

//推荐写成
Integer sum = invoiceList.stream().mapToInt(Invoice::getDetailSize).sum();
复制代码

上記加算において、最大値を選択し、同じ動作のための最小時間は、異なる実行方法を選択することができています。、削減収集し選択することができ、最小/最大/合計方法は、MIN、MAX、和方法をお勧めします。ボクシングとアンボクシングの動作を回避するために、値のストリームにmapToIntオブジェクトストリームを介して、読み取ることが最も簡単ですので

averagingIntを平均化することにより

Double avg = invoiceList.stream().collect(Collectors.averagingInt(Invoice::getDetailSize));
复制代码

データ型は二重である場合、長い平均化averagingDouble、a​​veragingLong法により行われます

BigDecimalのあなたは、作品の総数で割った第1の和に必要

List<BigDecimal> sumList = invoiceList.stream().map(Invoice::getAmount).collect(Collectors.toList());
        BigDecimal average = average(sumList, RoundingMode.HALF_UP);
// 求平均值
public BigDecimal average(List<BigDecimal> bigDecimals, RoundingMode roundingMode) {
  BigDecimal sum = bigDecimals.stream()
    .map(Objects::requireNonNull)
    .reduce(BigDecimal.ZERO, BigDecimal::add);
  return sum.divide(new BigDecimal(bigDecimals.size()), roundingMode);
}
复制代码

同時にsummarizingInt合計、平均、最大値、最小値を取得することにより

IntSummaryStatistics statistics = invoiceList.stream().collect(Collectors.summarizingInt(Invoice::getDetailSize));
double average1 = statistics.getAverage();
int max1 = statistics.getMax();
int min1 = statistics.getMin();
long sum = statistics.getSum();
复制代码

エレメンタルのforeachによって横断

invoiceList.forEach(item -> {
  System.out.println(item.getAmount());
});
复制代码

要素を結合することによりスプライシングされたストリーム

String result = invoiceList.stream().map(Invoice::getSaleName).collect(Collectors.joining(", "));
复制代码

groupingByによってグループ化されました

 Map<Integer, List<Invoice>> groupByTypeMap = invoiceList.stream().collect(Collectors.groupingBy(Invoice::getType));
复制代码

方法groupingBy分類機能のためのパラメータの着信グループ化方法を収集GroupingBy。また、マルチレベルのネストされたgroupingByによって分類することができます

Map<String, Map<String, List<RzInvoice>>> = invoiceList.stream().collect(Collectors.groupingBy(Invoice::getType, Collectors.groupingBy(invoice -> {
    if (invoice.getAmount().compareTo(BigDecimal.valueOf(10000)) <= 0) {
        return "low";
    } else if (invoice.getAmount().compareTo(BigDecimal.valueOf(80000)) <= 0) {
        return "mi";
    } else {
        return "high";
    }
})));
复制代码

まず、パケットのサイズに基づいて、その後の課金額、インボイス方式のパケットに基づいて、返されるデータ型は、Map <文字列、地図<文字列、一覧>>です

partitioningByによって高度なパーティション

分類は真と偽に基づいて、その結果を2つのグループにアップ返される特殊なパケットを、

Map<Boolean, List<Dish>> = invoiceList.stream().collect(Collectors.partitioningBy(RzInvoice::getCancelFlag));
复制代码

に相当

Map<Boolean, List<Dish>> = invoiceList.stream().collect(Collectors.groupingBy(RzInvoice::getCancelFlag));
复制代码

この例では、別の1時明白な例を分割する必要はありませんでしたでも、ゾーニングと分類、との違いが表示されないことがあります。

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Map<Boolean, List<Integer>> result = integerList.stream().collect(partitioningBy(i -> i < 3));
复制代码

戻り値は、結合のブールタイプが残っているが、その分類は、範囲に従って分類され、範囲に応じて、ゾーニングの分類を処理するためのより適切な

彼は仕事で遭遇したサンプルへ

// 过滤T-1至T-12 近12月数据,根据省份分组求和开票金额,使用金额进行倒序,产生LinkedHashMap
        LinkedHashMap<String, BigDecimal> areaSortByAmountMaps =
                invoiceStatisticsList.stream().filter(FilterSaleInvoiceUtil.filterSaleInvoiceWithRange(1, 12, analysisDate)) //根据时间过滤数据
                        .collect(Collectors.groupingBy(FkSalesInvoiceStatisticsDO::getBuyerAdministrativeAreaCode
                                , Collectors.reducing(BigDecimal.ZERO, FkSalesInvoiceStatisticsDO::getInvoiceAmount, BigDecimal::add)))// 根据开票地区分组,并同时将每个分组数据的开票金额求和
                        .entrySet().stream().sorted(Map.Entry.<String, BigDecimal>comparingByValue().reversed()) // 根据金额大小倒序
                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); //收集数据生成LinkedHashMap
复制代码

概要

ストリームAPIは、コードを使用することによって簡素化され、コードの可読性を向上させ、かつ迅速にプロジェクトにまで使用することができます。ストリームAPIを学ばなかった理由に先立ち、アプリケーション内で私にラムダをたくさん書いた誰もが、ストリームAPIは、彼の足を飛ぶことを望んでいました。

私は彼を愛するかもしれないと思う[喜喜]。同時に、慎重な時間宣言型と命令型プログラミングのミックスを使用しません。

おすすめ

転載: juejin.im/post/5d906754e51d4578014332df