コレクション、コレクション、コレクション、コレクター、コレクタ
コレクションは、Javaコレクションの祖先インターフェイスです。
コレクションは、java.utilパッケージの下のツールクラスであり、コレクションの処理のためのさまざまな静的方法を意味します。
java.util.stream.stream#collect(java.util.stream.collector <?super t、a、r>)は、ストリームの収集を担当するストリームの関数です。
java.util.stream.collectorは、コレクターの関数を宣言する関数を収集するためのインターフェイスです。
java.util.comparatorsは、一連のコレクターの実装が組み込まれたコレクターツールクラスです。
コレクターの機能
Java8ストリームは、派手で怠zyなデータセットの反復剤と考えることができます。 2種類の操作をサポートしています:中間操作(フィルター、マップなど)と端子操作(Count、FindFirst、Foreach、Reduceなど)。中間操作を接続して、あるストリームを別のストリームに変換できます。これらの操作はストリームを消費せず、目的はパイプラインを作成することです。対照的に、ターミナル操作はクラスを消費し、最終的な結果をもたらします。収集は削減操作であり、削減と同様に、さまざまな方法をパラメーターとして受け入れ、ストリーム内の要素を要約結果に蓄積することができます。特定のアプローチは、新しいコレクターインターフェイスを定義することによって定義されます。
事前に定義されたコレクター
以下は、基本的な内蔵コレクターの簡単なデモンストレーションです。シミュレートされたデータソースは次のとおりです。
final arraylist <dish> dishes = lists.newarraylist(new Dish( "Pork"、false、800、type.meat)、new Dish( "Beef"、false、false、700、type.meat)、new Dish( "Chicken"、false、false、type.meat)、new dish( "French Fries"、Themethers.the other oth、new seas othe oth oth oth oth oth oth oth、othe oth、othe other atherフルーツ」、True、120、Type.Other)、新しい料理(「ピザ」、True、550、Type.Other)、新しいディッシュ(「エビ」、偽、300、タイプ。
最大値、最小値、平均値
//オプションを返すのはなぜですか?ストリームがnullの場合はどうすればよいですか? Optinalはこの時点でオプションの<dish> mostCaloriedish = dishes.stream()。max(comparator.comparingint(dish :: getcalories)); optional <dish> mincaloriedish.stream()。 dishes.stream()。collect(collectors.avagingint(dish :: getcalories)); intsummarystatistics summarystatistics = dishes.stream()。collect(collectors.summarizingint(dish :: getcalories)); double average = summarystatistics.getaverage(); long count = summarystatistics.getCount(); int max = summarystatistics.getMax(); int min = summarystatistics.getMin(); long sum = summarystatistics.getSum();
これらの単純な統計インジケーターには、特に数値タイプのアンボクシング機能のためにコレクターが組み込まれたコレクター関数があり、パッケージングタイプを直接操作するよりもはるかに安価になります。
コレクターを接続します
ストリームの要素をまとめたいですか?
// string join1 = dishes.stream()。map(dish :: getname).collect(collectors.jaining()); // comma string join2 = dishes.stream()。map(dish :: getname).collect(collectors.jaining( "、"));
トーリスト
リスト<文字列> names = dishes.stream()。マップ(dish :: getname).collect(tolist());
元のストリームを単一の要素ストリームにマップし、リストとして収集します。
トセット
set <type> types = dishes.stream()。map(dish :: getType).collect(collectors.toset());
タイプをセットとして収集すると、繰り返すことができます。
トマップ
map <type、dish> bytype = dishes.stream()。collect(tomap(dish :: getType、d-> d));
アレイをキャッシュ用のマップに変換する必要がある場合があります。これにより、複数の計算と取得が容易になります。 TomapはメソッドKとvの生成関数を提供します(上記のデモはピットであることに注意してください。このように使用できません!!! Tomap(機能、機能、バイナリオーパレーターを使用してください))
上記はほぼ最も一般的に使用されるコレクターであり、基本的に十分です。しかし、初心者として、理解には時間がかかります。これが収集に使用できる理由を本当に理解するには、内部実装を確認する必要があります。これらのコレクターは、最初に言及されたコレクターの実装クラスであるjava.util.stream.collectors.collectorimplに基づいていることがわかります。カスタムコレクターは、後で特定の使用法を学習します。
カスタム削減
前の少数は、削減工場法によって定義された還元プロセスの特別なケースです。実際、コレクターの削減を使用してコレクターを作成できます。たとえば、合計を求めます
Integer TotalCalories = dishes.stream()。collect(reducing(0、dish :: getcalories、(i、j) - > i + j)); //矢印関数の代わりにビルトイン関数を使用します。
もちろん、Reduceを直接使用することもできます
オプション<integer> totalcalories3 = dishes.stream()。map(dish :: getcalories).reduce(integer :: sum);
大丈夫ですが、効率を検討する場合は、以下を選択する必要があります
int sum = dishes.stream()。maptoint(dish :: getcalories).sum();
状況に応じて最適なソリューションを選択してください
上記のように、機能プログラミングは通常、同じ操作を実行するための複数の方法を提供します。コレクターコレクションの使用は、ストリームAPIを使用するよりも複雑です。利点は、収集がより高いレベルの抽象化と一般化を提供し、再利用とカスタマイズが簡単になることです。
私たちのアドバイスは、手元の問題に対するさまざまなソリューションを可能な限り探索し、常に最も専門的なソリューションを選択することです。これは一般に、読みやすさとパフォーマンスの点で最良の決定です。
初期値を受信することに加えて、削減することは最初の項目を初期値として使用することもできます
optional <dish> mostcaloriedish = dishes.stream().collect(reducing((d1、d2) - > d1.getcalories()> d2.getcalories()?d1:d2));
削減
削減の使用法は非常に複雑であり、目標は2つの値を1つの値に統合することです。
public static <t、u> collector <t、?、u>還元(uアイデンティティ、function <?super t、?
最初に、3つのジェネリックを見ました。
uは返品値のタイプです。たとえば、上記のデモで計算された熱、uは整数です。
Tに関しては、Tはストリーム内の要素タイプです。関数関数から、マッパーの関数はパラメーターtを受信し、結果Uを返すことであることがわかります。デモの皿に対応しています。
?return Value Collectorを使用した汎用リストの中央では、これはコンテナタイプを表します。コレクターはもちろん、データを保存するためのコンテナが必要です。ここ?これは、コンテナタイプが不確かであることを意味します。実際、ここのコンテナはu []です。
パラメーターについて:
アイデンティティは、アキュムレータの開始点として理解できるリターン値タイプの初期値です。
マッパーはマップの機能であり、その重要性は、ストリームを必要なタイプストリームに変換することにあります。
OPはコア関数であり、その機能は2つの変数をどのように処理するかです。その中で、最初の変数は累積値であり、これは合計として理解できます。2番目の変数は計算される次の要素です。したがって、蓄積が達成されます。
また、最初のパラメーターを省略するための過負荷の方法もあります。つまり、ストリーム内の最初のパラメーターが初期値として使用されます。
public static <t> collector <t、?、optional <t >>削減(binaryoperator <t> op)
返品値の違いを見てみましょう。 tは入力値と返品値のタイプを表します。つまり、入力値タイプと出力値タイプは同じです。別の違いはオプションです。これは、初期値がなく、最初のパラメーターがnullである可能性があるためです。ストリーム要素がnullの場合、オプションを返すことは非常に意味があります。
パラメーターリストを見ると、バイナリオーペレーターのみが残ります。 BinaryOperatorはトリプル関数インターフェイスであり、目標は同じタイプの2つのパラメーターと同じタイプの戻り値を計算することです。 1> 2として理解できますか? 1:2、つまり、2つの数値の最大値を見つけます。最大値を見つけることは、比較的理解しやすいステートメントです。 Lambda式をカスタマイズして、返品値を選択できます。次に、ここでは、2つのストリームの要素タイプtを受信し、タイプTの返品値を返すことです。また、合計を使用して理解することも問題ありません。
上記のデモでは、還元と収集の関数がほぼ同じであることがわかります。どちらも最終結果を返します。たとえば、トーリスト効果を低減できます。
//手動でTolistCollectorを実装します---削減、不変の規制の乱用---並列リスト<integer> calories = dishes.stream()。map(dish :: getCalories).reduce(new ArrayList <Integer>()、(list <integer> l、integer e) - > list <integer> l2) - > {l1(l2);上記のプラクティスを説明させてください。
<u> u reduce(u Identity、bifunction <u、?super t、u> axumulator、binaryoperator <u> combiner);
uは返品値の種類です。ここにリストがあります
二元的<u、?スーパーT、U>アキュムレータはアキュムレータであり、その目標は個々の要素の値と計算ルールを蓄積することです。リストと要素の操作、そして最後に返品リストを次に示します。つまり、リストに要素を追加します。
BinaryOperator <u>コンバイナーはコンバイナーであり、目標は、戻り値タイプの2つの変数を1つにマージすることです。 2つのリストの合併は次のとおりです。
この解決策には2つの問題があります。1つはセマンティックな問題であり、もう1つは実際的な問題です。セマンティックの問題は、削減方法が2つの値を組み合わせて新しい値を生成することを目的としていることです。これは不変の削減です。代わりに、収集方法の設計は、コンテナを変更し、結果を出力するように蓄積することです。これは、上記のコードスニペットがリストをアキュムレータとして配置されたものとして変更するため、削減方法を乱用していることを意味します。還元方法を使用するための間違ったセマンティクスも実用的な問題を作成します。複数のスレッドによる同じデータ構造の同時変更がリスト自体を破壊する可能性があるため、この削減は並行して機能することはできません。この場合、スレッドの安全性が必要な場合は、一度に新しいリストを割り当てる必要があり、オブジェクトの割り当てはパフォーマンスに影響します。これが、収集が可変容器の削減を表現するのに適している理由であり、さらに重要なことには、並列操作に適しています。
概要:減少は不変の容器の削減に適しており、収集は可変容器の削減に適しています。収集は並列性に適しています。
グループ化
データベースは、多くの場合、グループの合計の必要性に遭遇し、グループをPrimitiveによって提供します。 Javaでは、指導スタイル(手動で書き込みループ)に従うと、非常に面倒でエラーが発生しやすくなります。 Java 8は機能ソリューションを提供します。
たとえば、タイプごとにグループディッシュ。以前のTomapと同様ですが、グループ化値は料理ではなく、リストです。
map <type、list <dish >> dishesbytype = dishes.stream()。collect(groupingby(dish :: getType));
ここ
public static <t、k> collector <t、?、map <k、list <t >>> groupingby(function <?super t、?
パラメーター分類器は関数であり、1つのパラメーターを受信して別のタイプに変換するように設計されています。上記のデモは、ストリーム要素ディッシュをタイプのタイプに変換し、タイプに従ってストリームをグループ化することです。その内部グループは、HashMapを通じて実装されます。 GroupingBy(分類器、Hashmap :: new、下流);
ストリーム要素自体のプロパティ関数に従ってグループ化に加えて、熱範囲に応じたグループ化など、グループ化ベースをカスタマイズすることもできます。
グループ化Byのパラメーターが関数であり、関数のパラメータータイプがDISHであることがすでにわかっているため、分類器を次のようにカスタマイズできます。
private caloriclevel getCaloriclevel(dish d){if(d.getCalories()<= 400){return caloriclevel.diet; } else if(d.getCalories()<= 700){caloriclevel.normalを返します。 } else {return caloriclevel.fat; }}パラメーターを渡すだけです
map <caloriclevel、list <dish >> dishesbylevel = dishes.stream().collect(groupingby(this :: getcaloriclevel));
マルチレベルのグループ化
Groupingbyは、他のいくつかの方法も過負荷になります
public static <t、k、a、d> collector <t、?、map <k、d >> groupingby(function <?super t、?extends k> collector <?super t、a、d> downstream)
多くのジェネリックと恐怖があります。簡単に理解しましょう。分類器は分類器でもあり、ストリームの要素タイプを受信し、グループ化する基礎、つまりグループ化ベースのカーディナリティを返します。したがって、tはストリームの現在の要素タイプを表し、kはグループの要素タイプを表します。 2番目のパラメーターは下流で、下流はコレクターコレクターです。このコレクター要素タイプはtのサブクラスであり、コンテナタイプのコンテナはa、削減された戻り値タイプはDです。つまり、グループのkは分類器を介して提供され、グループの値は2番目のパラメーターのコレクターを介して減少します。前のデモのソースコードは次のとおりです。
public static <t、k> collector <t、?、map <k、list <t >>> groupingby(function <?super t、?c> classifier){return groupingby(classifier、tolist()); }トーリストを削減コレクターとして使用し、最終結果はリスト<dish>です。したがって、グループエンドの値タイプはlist <dish>です。次に、値のタイプは、還元コレクターによって同様に決定でき、数千万人の還元コレクターがあります。たとえば、値をもう一度グループ化したいのですが、グループ化も一種の削減です。
//マルチレベルのグループ化マップ<タイプ、マップ<caloriclevel、list <dish >>> bytypeandcalory = dishes.stream()。 System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- System.out.println( "/t" + revel.out.println( "/t" + dishlist);});検証結果は次のとおりです。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [皿(name =ビーフ、ベジタリアン=偽、カロリー= 700、 type=MEAT)]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- type =その他)]
概要:グループ化Byのコアパラメーターは、KジェネレーターとVジェネレーターです。 Vジェネレーターは、あらゆるタイプのコレクターコレクターにすることができます。
たとえば、Vジェネレーターは数を計算でき、したがって、Table AグループごとにSQLステートメントにSELECTカウント(*)を実装できます。
map <type、long> typecount = dishes.stream()。collect(groupingby(dish :: gettype、counting()); system.out.println(typescount); ---------- {fish = 2、meat = 3、other = 4}} sql検索グループ最高スコアをselect MAX(id) from table A group by Type
map <type、optional <dish >> mostcaloricbytype = dishes.stream().collect(groupingby(dish :: gettype、maxby(comparator.comparingint(dish :: getCalories))));
ここでオプションは意味がありません。なぜなら、それは確かにnullではないので。それから私はそれを取り出さなければなりませんでした。 collecting and thenを使用します
Map <Type、Dish> MostCaloricByType = Dishes.Stream().Collect(fride :: GetType、Collecting andThen(maxby(comparator.comparingint(dish :: getCalories))、optional :: get));
結果はここで出てきているようですが、アイデアは同意しません。黄色のアラームをコンパイルし、次のように変更します。
Map <Type、Dish> MostCaloricByType = Dishes.Stream().Collect(dish :: getType、function.identity()、binaryoperator.maxby(comparingint(dish :: getCalories))));
はい、GroupingbyはTomapになり、キーはまだタイプ、値はまだ皿ですが、もう1つのパラメーターがあります! !ここでは、最初にピットに応答します。最初のTomapのデモは、理解しやすいものです。それが本当に使用されている場合、それは殺されます。リストをマップに再編成することは、必然的に同じ問題に直面することを知っています。 Kが同じ場合、Vはそれを上書きまたは無視しますか?以前のデモ法は、kが存在する場合にkを再度挿入し、例外を直接スローすることです。
java.lang.illegalstateException:キーディッシュの重複(name =豚肉、野菜=偽、カロリー= 800、タイプ=肉)java.util.stream.collectors.lambda $ throwingmerger $ 0(collectors.java:133)
正しい方法は、競合を処理するための機能を提供することです。このデモでは、対立を処理する原則は、最大のものをグループ化して見つけるための要件を満たしている最大のものを見つけることです。 (私は本当にJava 8機能学習をしたくありません、私はどこにでもパフォーマンスの問題の落とし穴があると感じています)
データベースSQLマッピングを続行し、 select sum(score) from table a group by Type
map <type、integer> totalcaloriesbytype = dishes.stream().collect(groupingby(dish :: getType、summingInt(dish :: getCalories)));
ただし、GroupingByと組み合わせてよく使用される別のコレクターは、マッピング方法によって生成されます。この方法は、2つのパラメーターを受信します。1つの関数はストリーム内の要素を変換し、もう1つは変換された結果オブジェクトを収集します。目的は、蓄積前に各入力要素にマッピング関数を適用して、特定のタイプの要素を受信するコレクターが異なるタイプのオブジェクトに適応できるようにすることです。このコレクターを使用する実用的な例を見てみましょう。たとえば、各タイプの皿のメニューには、カロリクレブルが存在するものを取得したいと考えています。グループ化とマッピングコレクターを次のように組み合わせることができます。
map <type、set <caloriclevel >> caloriclevelsbytype = dishes.stream().collect(groupingby(dish :: getType、マッピング(this :: getCaloriclevel、toSet()));
ここのトセットはデフォルトでハッシュセットを使用しており、特定の実装トコレクションを手動で指定することもできます(ハッシュセット:: new)
パーティション
パーティション化は、グループ化の特別なケースです。プレンシャ(ブール値を返す関数)は、パーティション関数と呼ばれる分類関数として使用されます。パーティション関数はブール値を返します。つまり、グループ化されたマップの重要なタイプはブール値であるため、最大2つのグループに分けることができます:trueまたはfalse。たとえば、あなたがベジタリアンの場合、メニューをベジタリアンと非ベジタリアンで分離することをお勧めします。
map <boolean、list <dish >> partitionedmenu = dishes.stream()。collect(partitionby(dish :: isvegetarian));
もちろん、フィルターを使用すると同じ効果が得られます。
List <dish> vegetariandishes = dishes.stream()。フィルター(dish :: isvegetarian).collect(collectors.tolist());
パーティション化の利点は、2つのコピーを保存することです。これは、リストを分類するときに役立ちます。同時に、GroupingByのように、PartitioningByはメソッドを過負荷にしているため、グループ化値のタイプを指定できます。
Map <boolean、map <type、list <dish >>> vertariandishesbytype = dishes.stream().collect(dish :: isvegetarian、groupingby(dish :: gettype)); map <boolean、integer> integer> getarisandishestotalcalories = dishes.stition()。 summingint(dish :: getCalories))))))))))))))))))))))))))))))))))))))))
PartitioningBy Collectorを使用する最後の例として、メニューデータモデルを脇に置いて、より複雑で興味深い例を確認します。配列をプライム番号と非プライム番号に分割します。
まず、プライムパーティション関数を定義します。
Private Boolean isprime(int condatis){int candidateroot =(int)math.sqrt((double)候補); return intstream.rangeclosed(2、candidateroot).nonematch(i-> condestate%i == 0);}次に、1から100のプライム数と非プライム番号を見つけます
map <boolean、list <integer >> partitionPrimes = intstream.rangeclosed(2、100).boxed().collect(partitioningby(this :: isprime));