[解決済み】Kotlinの標準ライブラリで利用できるJava 8 Stream.collectに相当するものは?
質問
Java 8 では
Stream.collect
であり、コレクションに対する集約を可能にするものです。 Kotlinでは、これはstdlibの拡張関数のコレクションとして存在する以外には、同じようには存在しない。 しかし、異なるユースケースに対してどのような等価性があるかは明確でない。
例えば
のJavaDocのトップ
Collectors
はJava 8用に書かれた例で、Kolinに移植する場合、異なるJDKバージョンではJava 8のクラスを使用できないので、おそらく別の書き方をする必要があります。
ネットでKotlinのコレクションの例を紹介している資料を見ても、一般的につまらないものばかりで、同じユースケースとは比較にならない。 Java 8のドキュメントにあるような、ケースに本当にマッチした良い例とは何でしょうか?
Stream.collect
? そこにリストアップされているのは
- 名前をListに蓄積する
- 名前をTreeSetに蓄積する
- 要素を文字列に変換し、カンマで区切って連結する。
- 従業員の給与の合計を計算する
- 部門別グループ分け
- 部門別給与の合計を計算する
- 学生を合格と不合格に分ける
詳細は上記リンク先のJavaDocで。
注意 この質問は、意図的に筆者が書き、回答しています( 自分で回答した質問 ) のように、よく聞かれるKotlinのトピックに対するイディオム的な回答がSOに存在するようにします。また、Kotlinのアルファ版として書かれた、現在のKotlinでは正確ではない本当に古い答えを明らかにするためでもあります。
解決方法は?
Kotlinの標準ライブラリには、平均、カウント、区別、フィルタリング、発見、グループ化、結合、マッピング、最小、最大、分割、スライス、ソート、合計、配列との間、リストとの間、マップとの間、ユニオン、協調反復、すべての関数パラダイム、その他の関数が用意されています。 ですから、それらを使って小さな1ライナーを作ることができますし、Java 8のより複雑な構文を使う必要はありません。
私は、ビルトインのJava 8に唯一欠けているのは
Collectors
クラスは要約です(ただし
もう一つの答え
が簡単な解決策です)
.
両者に欠けているのは、カウントによるバッチ処理で、これは次のようになります。
別のStack Overflowの回答
で、こちらも簡単な回答があります。もう一つ興味深いのは、同じくStack Overflowに掲載されているこのケースです。
Kotlin を使ってシーケンスを 3 つのリストに分割する直感的な方法
. また、次のようなものを作りたい場合
Stream.collect
を参照してください。
KotlinでカスタムStream.collect
2017.08.11を編集しました。 kotlin 1.2 M2 で Chunked/window コレクション操作が追加されたので、以下を参照。 https://blog.jetbrains.com/kotlin/2017/08/kotlin-1-2-m2-is-out/
を探求することは常に良いことです。 kotlin.collectionsのAPIリファレンス は、すでに存在する可能性のある関数を新たに作成する前に、全体として作成する必要があります。
以下は、Java 8 からの変換です。
Stream.collect
の例からKotlinの同等の例へ。
名前をリストに蓄積する
// Java:
List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());
// Kotlin:
val list = people.map { it.name } // toList() not needed
要素を文字列に変換し、カンマで区切って連結します。
// Java:
String joined = things.stream()
.map(Object::toString)
.collect(Collectors.joining(", "));
// Kotlin:
val joined = things.joinToString(", ")
従業員の給与の合計を計算する
// Java:
int total = employees.stream()
.collect(Collectors.summingInt(Employee::getSalary)));
// Kotlin:
val total = employees.sumBy { it.salary }
部署ごとに社員をグループ化
// Java:
Map<Department, List<Employee>> byDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
// Kotlin:
val byDept = employees.groupBy { it.department }
部門別給与の合計を計算する
// Java:
Map<Department, Integer> totalByDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary)));
// Kotlin:
val totalByDept = employees.groupBy { it.dept }.mapValues { it.value.sumBy { it.salary }}
合格と不合格に生徒を仕切る
// Java:
Map<Boolean, List<Student>> passingFailing =
students.stream()
.collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
// Kotlin:
val passingFailing = students.partition { it.grade >= PASS_THRESHOLD }
男性メンバーの名前
// Java:
List<String> namesOfMaleMembers = roster
.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.map(p -> p.getName())
.collect(Collectors.toList());
// Kotlin:
val namesOfMaleMembers = roster.filter { it.gender == Person.Sex.MALE }.map { it.name }
名簿に登録されているメンバーの名前を性別でグループ分けする
// Java:
Map<Person.Sex, List<String>> namesByGender =
roster.stream().collect(
Collectors.groupingBy(
Person::getGender,
Collectors.mapping(
Person::getName,
Collectors.toList())));
// Kotlin:
val namesByGender = roster.groupBy { it.gender }.mapValues { it.value.map { it.name } }
リストから別のリストへのフィルタリング
// Java:
List<String> filtered = items.stream()
.filter( item -> item.startsWith("o") )
.collect(Collectors.toList());
// Kotlin:
val filtered = items.filter { it.startsWith('o') }
リストの最短文字列の検索
// Java:
String shortest = items.stream()
.min(Comparator.comparing(item -> item.length()))
.get();
// Kotlin:
val shortest = items.minBy { it.length }
フィルタを適用した後のリスト内の項目を数える
// Java:
long count = items.stream().filter( item -> item.startsWith("t")).count();
// Kotlin:
val count = items.filter { it.startsWith('t') }.size
// but better to not filter, but count with a predicate
val count = items.count { it.startsWith('t') }
といった具合です。 すべての場合において、特別なfoldやreduceなどの機能は必要ありません。
Stream.collect
. もし、さらに詳しい使用例があれば、コメントで追加してください。
怠け心について
チェーンの処理を遅延させたい場合は、そのチェーンを
Sequence
を使って
asSequence()
をチェーンの前に置く。 関数の連鎖の最後には、通常は
Sequence
と同じです。 そうすると
toList()
,
toSet()
,
toMap()
を実体化させるための何らかの関数が必要です。
Sequence
を最後に追加します。
// switch to and from lazy
val someList = items.asSequence().filter { ... }.take(10).map { ... }.toList()
// switch to lazy, but sorted() brings us out again at the end
val someList = items.asSequence().filter { ... }.take(10).map { ... }.sorted()
なぜTypeがないのか!?
Kotlinの例では、型を指定していないことにお気づきでしょう。 これは、Kotlinが完全な型推論を持ち、コンパイル時に完全に型安全であるためです。 Javaよりもさらに、NULL可能な型もあり、恐ろしいNPEを防ぐのに役立つからです。 だからKotlinでこれ。
val someList = people.filter { it.age <= 30 }.map { it.name }
と同じです。
val someList: List<String> = people.filter { it.age <= 30 }.map { it.name }
なぜならKotlinは
people
があり、その
people.age
は
Int
したがって、このフィルタ式では
Int
であり、その
people.name
は
String
したがって
map
ステップでは
List<String>
(読み取り専用
List
の
String
).
さて、もし
people
はもしかしたら
null
というように
List<People>?
では
val someList = people?.filter { it.age <= 30 }?.map { it.name }
を返します。
List<String>?
を使用すると、NULLチェックが必要になります (
または、Kotlin の他の演算子を使って null 可能な値を指定することもできます。
Kotlin の慣用的な null 値の扱い方
また
Kotlin で null 可能なリストまたは空のリストを処理する慣例的な方法
)
こちらもご覧ください。
- のAPIリファレンス Iterableの拡張関数
- のAPIリファレンス 配列の拡張関数
- のAPIリファレンス リスト用拡張関数
- のAPIリファレンス Mapへの拡張関数
関連
-
スタイルシートとして解釈されるリソースが、MIMEタイプtext/htmlで転送される。
-
[解決済み] JVM起動時のパラメータ「-Xms」「-Xmx」とは何ですか?
-
をインスタンス化することができません。
-
org.glassfish.jersey.servlet.ServletContainer
-
[解決済み] JavaにおけるHashMapとHashtableの違いは何ですか?
-
[解決済み] Javaにおけるpublic、protected、package-private、privateの違いは何ですか?
-
[解決済み] Javaクラスにおけるcanonical name、simple name、class nameの違いは何ですか?
-
[解決済み】KotlinでJavaの静的メソッドに相当するものは何ですか?
-
[解決済み】オプショナルの使用方法
-
[解決済み] Kotlinで、nullableな値を扱う慣用的な方法、参照または変換する方法は何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
エラーが報告されました。リソースの読み込みに失敗しました:サーバーは500(内部サーバーエラー)のステータスで応答しました。
-
JavaMailのメール送信が失敗するケースとその説明の分析
-
Spring boot runs with Error creating bean with name 'entityManagerFactory' defined in class path resource
-
Spring BootのテストメソッドFailed to load ApplicationContextの問題を解決する
-
javaコンパイル時のエラー:不正な文字 '\ufeff' に対する解決策です。
-
linux run jarfile Invalid or corrupt jarfile error.
-
javaでクラスを作成すると、enclosing classでないように見える
-
ローカルリソースのロードが許可されていない場合の解決策
-
[解決済み] Kotlinで、nullableな値を扱う慣用的な方法、参照または変換する方法は何ですか?
-
[解決済み] Kotlin - 配列から重複する文字列を削除する方法は?