S-JIS[2014-04-11/2021-03-21] 変更履歴

Java Stream

JavaのStreamインターフェースについて。


概要

JDK1.8で導入されたStreamは、複数の値(オブジェクト)に対して何らかの処理(変換や集計)を行う事を分かりやすく記述できる。

import java.util.stream.Stream;

Streamの値の持ち方はjava.util.Listのようなイメージ。
しかしStreamの操作は一度しか行えないので(ScalaのTraversableOnce相当)、java.util.Iteratorの方が近いかも。
つまり複数の値を保持し、順次処理していく。


Streamには色々な処理を行うメソッドが用意されている。
一部のメソッド(forEach)はコレクションクラス(List・Queue・Set(Iterable))に直接用意されているが、基本的には、コレクションや配列からStreamを生成して使用する。
Streamで行った操作結果をコレクションにしたい場合は、collectメソッドを使ってコレクションに変換する。

ちなみにScalaの場合は、コレクションはJavaのStreamのようなメソッドをまとめたインターフェースを実装している(Scalaなので、厳密にはトレイトミックスインしている)ので、
List(Seq)や配列(Array)のままで(Streamへの変換のような事をせずに)JavaのStreamのような記述を行うことが出来る。
なお、java.util.stream.Streamと同様な位置付け(無限データを扱える)のscala.collection.immutable.Streamというクラスもある。


Streamを使うと、以下のように処理が簡単に(宣言的に)書ける。

Streamを使用 Streamを使わずに記述 Scala相当
String→Pathの変換 List<String> slist = Arrays.asList("a.txt", "b.txt");
List<Path> plist = slist.stream()
  .map(Paths::get)
  .collect(Collectors.toList());
List<String> slist = Arrays.asList("a.txt", "b.txt");
List<Path> plist = new ArrayList<>();
for (String s : slist) {
  plist.add(Paths.get(s));
}
val slist = Seq("a.txt", "b.txt")
val plist = slist.map(Paths.get(_))
Path→Stringの変換 List<String> slist2 = plist.stream()
  .map(path -> path.toString())
  .collect(Collectors.toList());
List<String> slist2 = new ArrayList<>();
for (Path path : plist) {
  slist2.add(path.toString());
}
val slist2 = plist.map(_.toString)
Stringのメソッド名一覧取得
(重複排除つき)
List<String> mlist = Arrays.stream(String.class.getMethods())
  .map(method -> method.getName())
  .distinct()
  .collect(Collectors.toList());
List<String> mlist = new ArrayList<>();
Set<String> set = new HashSet<>();
for (Method method : String.class.getMethods()) {
  String name = method.getName();
  if (!set.contains(name)) {
    mlist.add(name);
    set.add(name);
  }
}
val mlist = classOf[String].getMethods().
  map(_.getName).
  distinct
Excelのシート一覧取得
POI
Workbook workbook = WorkbookFactory.create(new File(excelFile));
List<Sheet> sheets = IntStream.range(0, workbook.getNumberOfSheets())
  .mapToObj(i -> workbook.getSheetAt(i))
  .collect(Collectors.toList());
Workbook workbook = WorkbookFactory.create(new File(excelFile));
List<Sheet> sheets = new ArrayList<>();
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
  sheets.add(workbook.getSheetAt(i));
}
val workbook = WorkbookFactory.create(new File(excelFile))
val sheets = (0 until workbook.getNumberOfSheets()).
  map(workbook.getSheetAt(_))
 
val sheets = for (i <- 0 until workbook.getNumberOfSheets()) yield workbook.getSheetAt(i)

Stream系クラスの種類

Streamはオブジェクト(クラス)を対象としている。

プリミティブ型を対象とするStreamとして、専用のIntStream・LongStream・DoubleStreamが用意されている。

Stream<Integer>とIntStreamは意味的には同じかもしれないが、用意されているメソッドやintに対する処理速度は異なる。

ちなみにScalaの場合はIntもクラスなので、専用のクラスは無く、クラスとIntが同一に扱える。


それと、ストリームにはシーケンシャル(直列)なStreamパラレル(並列)なStreamがある。

並列ストリームだと、(処理内容によっては)マルチスレッドで並列に処理される。
ただしオーバーヘッドはかかるので、対象件数が少ない場合は直列ストリームより遅くなることはある。

並列ストリームかどうかはisParallelメソッドを呼び出せば分かる。
並列ストリームを取得するにはparallelメソッドを、直列ストリームを取得するにはsequentialメソッドを呼び出す。


Stream系クラスのメソッド

ストリームのメソッドは、動作内容に応じて中間処理終端処理(末端処理)の2種類に分けられる。
おおまかに言うと、Streamを返すメソッドが中間処理で、それ以外は終端処理。

filter()map()等の処理を記述する(関数を渡す)メソッドは中間処理なので、“渡された関数を保持した新しいStream”を返す。
Stream内に保持されている値を使った演算は、中間処理では行わない。

forEach()collect()といった 終端処理のメソッドが呼ばれると、実際に処理を行う。
したがって、大量のデータを対象とするStreamの場合でも、実行時間がかかるのは終端処理であり、中間処理のメソッド呼び出しでは実行時間はほとんどかからない。

staticメソッド 中間処理メソッド 終端処理メソッド その他

Streamインスタンスの生成

Stream系のインターフェースは(インターフェースなので)newでインスタンスを作ることは出来ない。
Streamインスタンスを生成するメソッドが用意されているので、それを使う。

主なStream生成方法
生成メソッド 説明 Scala相当
生成方法 生成内容
Stream
IntStream
LongStream
DoubleStream
of() Stream<String> s = Stream.of("abc", "def"); abc, def 初期値を指定して生成する。 val s = Stream("abc", "def")
IntStream s = IntStream.of(123, 456); 123, 456 val s = Stream(123, 456)
ofNullable() Stream<String> s = Stream.ofNullable("abc"); abc Java9以降。[2017-09-24]
引数がnull以外の場合、その値を持つStreamを生成する。
引数がnullの場合は空のStreamを生成する。
 
empty() Stream<String> s = Stream.empty();   空のStreamを生成する。 val s = Stream.empty[String]
IntStream s = IntStream.empty();   val s = Stream.empty[Int]
builder() Builder<String> builder = Stream.builder();
Stream<String> s = builder.add("abc").add("def").build();
abc, def ビルダーを使って生成する。  
IntStream s = IntStream.builder().add(123).add(456).build(); 123, 456
iterate() Stream<String> s = Stream.iterate("a", c -> c + c); a, aa, aaaa, 「前の値を使って新しい値を生成する関数」(UnaryOperator)を指定する。
第1引数は初期値。Streamの1つ目の値はこれになる。
2つ目以降の値は、1つ前の値を第2引数の関数に渡した結果となる。
このStreamは無限に続くので、limit()等を使って終了させる必要がある。
val s = Stream.iterate("a")(c => c + c)
IntStream s = IntStream.iterate(1, n -> n * 2); 1, 2, 4, 8, val s = Stream.iterate(1)(_ * 2)
Stream<String> s = Stream.iterate("a", c -> c.length() <= 4, c -> c + c); a, aa, aaaa Java9以降。[2017-09-24]
終了条件付きiterate。
Stream.iterate("a")(c => c + c).takeWhile(_.length <= 4)
IntStream s = IntStream.iterate(1, n -> n < 10, n -> n * 2); 1, 2, 4, 8 val s = Stream.iterate(1)(_ * 2).takeWhile(_ < 10)
generate() Stream<String> s = Stream.generate(() -> "abc"); abc, abc, abc, 「値を返す関数」(Supplier)を指定する。
このStreamは無限に続くので、limit()等を使って終了させる必要がある。
val s = Stream.continually("abc")
Random rnd = new Random();
IntStream s = IntStream.generate(() -> rnd.nextInt() & 7);
3, 4, 2, 7, val rnd = new Random
val s = Stream.continually(rnd.nextInt & 7)
concat() Stream<String> s1 = Stream.of("a", "b");
Stream<String> s2 = Stream.of("x", "y");
Stream<String> s = Stream.concat(s1, s2);
a, b, x, y 2つのStreamを結合する。 val s1 = Stream("a", "b")
val s2 = Stream("x", "y")
val s = s1 ++ s2
IntStream s1 = IntStream.of(1, 2);
IntStream s2 = IntStream.of(8, 9);
IntStream s = IntStream.concat(s1, s2);
1, 2, 8, 9 val s1 = Stream(1, 2)
val s2 = Stream(8, 9)
val s = s1 ++ s2
Stream<String> s1 = 〜;
Stream<String> s2 = 〜;
Stream<String> s3 = 〜;
Stream<String> s = Stream.of(s1, s2, s3).flatMap(t -> t);
  3つ以上のStreamを結合する場合は、技巧的だがflatMapを使う方が綺麗かも?[2017-09-24] val s1 = Stream(〜)
val s2 = Stream(〜)
val s3 = Stream(〜)
val s = s1 ++ s2 ++ s3
mapToInt(Integer::intValue) Stream<Integer> s = 〜;
IntStream is = s.mapToInt(Integer::intValue);
  Stream<Integer>をIntStreamに変換する。[2017-04-11]
IntStreamをStream<Integer>に変換するにはboxedを使う。
 
IntStream
LongStream
range() IntStream s = IntStream.range(2, 6); 2, 3, 4, 5 指定された範囲のStreamを生成する。
指定された末尾の値は含まない。
val s = 2 until 6
val s = Range(2, 6)
rangeClosed() IntStream s = IntStream.rangeClosed(2, 6); 2, 3, 4, 5, 6 指定された範囲のStreamを生成する。
指定された末尾の値を含む。
val s = 2 to 6
val s = Range.inclusive(2, 6)
コレクション
(List・Queue・Set)
stream() List<String> list = Arrays.asList("abc", "def");
Stream<String> s = list.stream();
abc, def コレクションから直列ストリームを生成する。
(Streamからコレクションを生成するにはcollect()Collectors.toList等)を使う)
val list = Seq("abc", "def")
val s = list.toStream
parallelStream() List<String> list = Arrays.asList("abc", "def");
Stream<String> s = list.parallelStream();
abc, def コレクションから並列ストリームを生成する。 val list = Seq("abc", "def")
val s = list.toStream.par
配列 stream() String[] ss = { "abc", "def" };
Stream<String> s = Arrays.stream(ss);
abc, def 配列から直列ストリームを生成する。 val ss = Array("abc", "def")
val s = ss.toStream
int[] ns = { 123, 456 };
IntStream s = Arrays.stream(ns);
123, 456 val ns = Array(123, 456)
val s = ns.toStream
String[] ss = { "a", "b", "c", "d", "e", "f" };
Stream<String> s = Arrays.stream(ss, 2, 5);
c, d, e val ss = Array("a", "b", "c", "d", "e", "f")
val s = ss.view(2, 5).toStream
Spliterator
[2015-12-12]
  Iterable<String> i = Arrays.asList("a", "b", "c");
Stream<String> s = StreamSupport.stream(i.spliterator(), true);
a, b, c SpliteratorからStreamを作ることが出来る。
(プリミティブStreamも対応している)
 

他にも以下のクラス等にStreamを生成するメソッドが存在している。


Stream

Streamはオブジェクト(クラス)のデータを対象としている。

Streamの中間処理メソッド
メソッド 戻り型 引数 説明 Streamなし相当 Scala相当
コーディング 結果
filter Stream<T> (T) -> boolean predicate 条件に一致したオブジェクトだけを抽出する。
条件判定の関数(Predicate)を渡す。
filterの例
List<String> list = Arrays.asList("a", "bb", "ccc");
List<String> list2 = list.stream()
  .filter(s -> s.length() == 2)
  .collect(Collectors.toList());
System.out.println(list2);
[bb] List<String> list = Arrays.asList("a", "bb", "ccc");
List<String> list2 = new ArrayList<>();
for (String s : list) {
  if (s.length() == 2) {
    list2.add(s);
  }
}
System.out.println(list2);
val list = Seq("a", "bb", "ccc")
val list2 = list.filter(_.length == 2)
println(list2)
条件に一致しないオブジェクトを抽出する場合はPredicate.notを併用する。(Java11以降)[2018-10-01] var stream = Stream.of("a", "", "c");
stream.filter(Predicate.not(String::isEmpty))
.forEach(System.out::println);
a
c
  val stream = Stream("a", "", "c")
stream.filterNot(_.isEmpty).
foreach(println)
map Stream<R> (T) -> R mapper 保持している値を変換する(値を変更した新しいStreamを返す)。
入力TをRに変換する関数(Function)を渡す。
(出力の型が入力の型(T)と同じでも構わない)
mapの例
List<String> slist = Arrays.asList("a.txt", "b.txt");
List<Path> plist = slist.stream()
  .map(Paths::get)
  .collect(Collectors.toList());
System.out.println(plist);
[a.txt, b.txt] List<String> slist = Arrays.asList("a.txt", "b.txt");
for (String s : slist) {
  plist.add(Paths.get(s));
}
System.out.println(plist);
val slist = Seq("a.txt", "b.txt")
val plist = slist.map(Paths.get(_))
println(plist)
mapToInt IntStream (T) -> int mapper intに変換するmapメソッド
入力Tをintに変換する関数(ToIntFunction)を渡す。
mapToの例
List<String> list = Arrays.asList("a", "bb", "ccc");
int len = list.stream()
  .mapToInt(s -> s.length())
  .sum();
System.out.println(len);
6 // mapToInt
int[] temp = new int[list.size()];
int i = 0;
for (String s : list) {
  temp[i++] = s.length();
}

// sum
int len = 0;
for (int n : temp) {
  len += n;
}
val list = Seq("a", "bb", "ccc")
val len = list.map(_.length).sum
println(len)
int len = 0;
for (String s : list) {
  len += s.length();
}
mapToLong LongStream (T) -> long mapper longに変換するmapメソッド
入力Tをlongに変換する関数(ToLongFunction)を渡す。
mapToの例
       
mapToDouble DoubleStream (T) -> double mapper doubleに変換するmapメソッド
入力Tをdoubleに変換する関数(ToDoubleFunction)を渡す。
mapToの例
       
flatMap Stream<R> (T) -> Stream<R> mapper 複数の値(0個も可)に変換するmapメソッド
入力Tを複数個のRに変換する関数(Function)を渡す。
flatMapの例

なお、flatMap内で作られたStreamは自動的にcloseされる[2015-12-13]

mapMultiの方がStreamを作らなくていいので便利。[2021-03-21]

List<String> list = Arrays.asList("a2", "b0", "c3");
List<String> result = list.stream().flatMap(s -> {
  String c = s.substring(0, 1);
  int n = Integer.parseInt(s.substring(1));

  String[] array = new String[n];
  Arrays.fill(array, c);
  return Stream.of(array);
}).collect(Collectors.toList());
System.out.println(result);
[a, a, c, c, c] List<String> list = Arrays.asList("a2", "b0", "c3");
List<String> result = new ArrayList<>();
for (String s : list) {
  String c = s.substring(0, 1);
  int n = Integer.parseInt(s.substring(1));

  String[] array = new String[n];
  Arrays.fill(array, c);
  List<String> temp = Arrays.asList(array);

  // フラット化
  result.addAll(temp);
}
System.out.println(result);
 
val list = Seq("a2", "b0", "c3")
val result = list.flatMap { s =>
  val c = s.substring(0, 1)
  val n = s.substring(1).toInt

  Seq.fill(n)(c)
}
println(result)
flatMapToInt IntStream (T) -> IntStream mapper intに変換するflatMapメソッド
入力TをIntStreamに変換する関数(Function)を渡す。
       
flatMapToLong LongStream (T) -> LongStream mapper longに変換するflatMapメソッド
入力TをLongStreamに変換する関数(Function)を渡す。
       
flatMapToDouble DoubleStream (T) -> DoubleStream mapper doubleに変換するflatMapメソッド
入力TをDoubleStreamに変換する関数(Function)を渡す。
       
mapMulti Stream<R> (T, Consumer<R>) -> void mapper flatMapと同様に)複数の値に変換するメソッド。(Java16以降)[2021-03-21]
返したい値をconsumerに登録する形になっている。
mapMultiの例
var stream = Stream.of("a2", "b0", "c3");
var result = stream.mapMulti((s, consumer) -> {
String c = s.substring(0, 1);
int n = Integer.parseInt(s.substring(1));
for (int i = 0; i < n; i++) {
consumer.accept(c);
}
}).toList();
[a, a, c, c, c]    
mapMultiToInt IntStream (T, IntConsumer) -> void mapper int(IntStream)に変換するmapMultiメソッド。(Java16以降)[2021-03-21]        
mapMultiToLong LongStream (T, LongConsumer) -> void mapper long(LongStream)に変換するmapMultiメソッド。(Java16以降)[2021-03-21]        
mapMultiToDouble DoubleStream (T, DoubleConsumer) -> void mapper double(DoubleStream)に変換するmapMultiメソッド。(Java16以降)[2021-03-21]        
distinct Stream<T>     重複した値を排除する。
重複の判定はObject#equals()で行われる。(実装上はLinkedHashMapなのでhashCode()も使われる[2018-07-15])
順序のあるStreamの場合、データの並び順は変更されないことが保証されている。
(重複するデータがあった場合、最初のデータの位置が保持される)
List<String> list = Arrays.asList("b", "a", "b", "c", "a");
List<String> dist = list.stream()
  .distinct()
  .collect(Collectors.toList());
System.out.println(dist);
 
[b, a, c] List<String> list = Arrays.asList("b", "a", "b", "c", "a");
Set<String> set = new LinkedHashSet<>(list);
List<String> dist = new ArrayList<>(set);
System.out.println(dist);
 
val list = Seq("b", "a", "b", "c", "a")
val dist = list.distinct
println(dist)
sorted Stream<T>     ソートする。
このメソッドを使う場合、保持されているデータはComparableを実装している必要がある。
List<String> list = Arrays.asList("c", "b", "d", "a");
List<String> sort = list.stream()
  .sorted()
  .collect(Collectors.toList());
System.out.println(sort);
[a, b, c, d] List<String> list = Arrays.asList("c", "b", "d", "a");
List<String> sort = new ArrayList<>(list);
Collections.sort(sort);
System.out.println(sort);
val list = Seq("c", "b", "d", "a")
val sort = list.sorted
println(sort)
sorted Stream<T> (T, T) -> int comparator ソートする。
比較関数(Comparator)を渡す。
List<String> list = Arrays.asList("aa", "b", "ccc", "dd");
List<String> sort = list.stream()
  .sorted((s1, s2) -> s1.length() - s2.length())
  .collect(Collectors.toList());
System.out.println(sort);
[b, aa, dd, ccc] List<String> list = Arrays.asList("aa", "b", "ccc", "dd");
List<String> sort = new ArrayList<>(list);
sort.sort((s1, s2) -> s1.length() - s2.length());
System.out.println(sort);
val list = Seq("aa", "b", "ccc", "dd")
val sort = list.sortBy(_.length)
println(sort)
peek Stream<T> (T) -> void action 値を受け取る関数(Consumer)を渡す。
保持している値を関数に渡すが、出力Streamとしては何も変わらない。
デバッグ用途で、どんな値が入っているか確認したいとき等に使う。
peekの例
Stream<String> s = Stream.of("a", "b", "c");
s.peek(t -> System.out.printf("peek1=%s%n", t)).
  map(t -> t + t).
  peek(t -> System.out.printf("peek2=%s%n", t)).
  forEach(System.out::println);
peek1=a
peek2=aa
aa
peek1=b
peek2=bb
bb
peek1=c
peek2=cc
cc
   
limit Stream<T> long maxSize 指定された個数までに限定する。
最大個数を渡す。
Stream<String> s = Stream.iterate("a", c -> c + "b");
s.limit(4).forEach(System.out::println);
a
ab
abb
abbb
  val s = Stream.iterate("a")(c => c + "b")
s.take(4).foreach(println)
skip Stream<T> long n 指定された個数の値をスキップ(除去)する。
スキップする個数を渡す。
Stream<String> s = Stream.iterate("a", c -> c + "b");
s.skip(2).limit(3).forEach(System.out::println);
abb
abbb
abbbb
  val s = Stream.iterate("a")(c => c + "b")
s.drop(2).take(3).foreach(println)
 
takeWhile Stream<T> (T) -> boolean predicate Java9以降。[2017-09-24]
指定された条件を満たしている間、その値を取得する。
Stream<String> s = Stream.iterate("a", c -> c + "b");
s.takeWhile(c -> c.length() <= 3).forEach(System.out::println);
a
ab
abb
  val s = Stream.iterate("a")(c => c + "b")
s.takeWhile(_.length <= 3).foreach(println)
dropWhile Stream<T> (T) -> boolean predicate Java9以降。[2017-09-24]
指定された条件を満たしている間、その値をスキップ(除去)する。
Stream<String> s = Stream.iterate("a", c -> c + "b");
s.dropWhile(c -> c.length() <= 3).limit(4).forEach(System.out::println);
abbb
abbbb
abbbbb
abbbbbb
  val s = Stream.iterate("a")(c => c + "b")
s.dropWhile(_.length <= 3).take(4).foreach(println)

Streamの終端処理メソッド
メソッド 戻り型 引数 説明 Streamなし相当 Scala相当
コーディング 結果
forEach void (T) -> void action 値を処理する。
処理する関数(Consumer)を渡す。
副作用(値を表示したりとか)を起こす専用のメソッド。
並列ストリームの場合、処理順序や実行されるスレッドは保証されない。
処理を同期する必要があるのであれば、渡した関数の中で同期処理しなければならない。
forEachの例
List<String> list = Arrays.asList("a", "b", "c");
list.stream().forEach(s -> {
  System.out.printf("value=%s%n", s);
});
value=a
value=b
value=c
List<String> list = Arrays.asList("a", "b", "c");
for (String s : list) {
  System.out.printf("value=%s%n", s);
}
val list = Seq("a", "b", "c")
list.foreach { s =>
  printf("value=%s%n", s)
}
forEachOrdered void (T) -> void action 値を1つずつ順番に処理するforEachメソッド。(順序があるStreamの場合)
ただし、実行されるスレッドは保証されない。
       
toArray Object[]     Object配列に変換する。 Stream<String> s = Stream.of("a", "b", "c");
Object[] array = s.toArray();
System.out.println(Arrays.toString(array));
[a, b, c]   val s = Stream("a", "b", "c")
val array : Array[Object] = s.toArray
println(Arrays.toString(array))
toArray A[] (int) -> A[] generator 配列に変換する。
配列インスタンスを生成する関数(IntFunction)を渡す。
Stream<String> s = Stream.of("a", "b", "c");
String[] array = s.toArray(String[]::new);
System.out.println(Arrays.toString(array));
[a, b, c]   val s = Stream("a", "b", "c")
val array = s.toArray
println(array.toSeq)
reduce T T identity 値を集約する。
第1引数で初期値、第2引数で集約する関数(BinaryOperator)を渡す。
List<String> list = Arrays.asList("a", "b", "c");
String s = list.stream().reduce("1", (i, t) -> i + t);
System.out.println(s);

順序は保証されないので、普通はStringのようなものには使わない。
1abc List<String> list = Arrays.asList("a", "b", "c");
String i = "1";
for (String t : list) {
  i = i + t;
}
String s = i;
System.out.println(s);
val list = Seq("a", "b", "c")
val s = list.fold("1")(_ + _)
println(s)
(T, T) -> T accumulator
reduce Optional<T> (T, T) -> T accumulator 値を集約する。
集約する関数(BinaryOperator)を渡す。
集約された値はOptionalに包まれて返ってくる。
Stream内に値が無い場合はOptional.emptyが返る。
List<String> list = Arrays.asList("a", "b", "c");
Optional<String> s = list.stream().reduce((i, t) -> i + t);
System.out.println(s);

順序は保証されないので、普通はStringのようなものには使わない。
Optional[abc] List<String> list = Arrays.asList("a", "b", "c");
String i = null;
for (String t : list) {
  if (i == null) {
    i = t;
  } else {
    i = i + t;
  }
}
Optional<String> s = Optional.ofNullable(i);
System.out.println(s);
val list = Seq("a", "b", "c")
val s = list.reduceOption(_ + _)
println(s)
reduce U U identity 値を集約する。
初期値と集約する関数(BiFunction)と中間集約結果を最終結果にまとめる関数(BinaryOperator)を渡す。[/2017-02-25]
class Result {
  public final int m, p;
  public Result(int multi, int plus) {
    this.m = multi;
    this.p = plus;
  }
}
List<Integer> list = Arrays.asList(1, 2, 3, 4);
Result result = list.stream().reduce(
  new Result(1, 0),
  (r, n) -> new Result(r.m * n, r.p + n),
  (r1, r2) -> new Result(r1.m * r2.m, r1.p + r2.p));
System.out.println(result.m);
System.out.println(result.p);
24
10
List<Integer> list = Arrays.asList(1, 2, 3, 4);
Result identity = new Result(1, 0);
Result r1 = identity;
for (Integer n : list.subList(0, 2)) {
  r1 = new Result(r1.m * n, r1.p + n);
}
Result r2 = identity;
for (Integer n : list.subList(2, 4)) {
  r2 = new Result(r2.m * n, r2.p + n);
}
Result result = new Result(r1.m * r2.m, r1.p + r2.p);
System.out.println(result.m);
System.out.println(result.p);
case class Result(m: Int, p: Int)
val list = Seq(1, 2, 3, 4)
val result = list.foldLeft(Result(1, 0))((r, n) => Result(r.m * n, r.p + n))
println(result.m)
println(result.p)

ScalaのfoldLeftは先頭から順に処理するので、最終結果をまとめるような関数は不要。
(U, T) -> U accumulator
(U, U) -> U combiner
collect R () -> R supplier 結果を作成する。
第1引数は結果を入れるオブジェクトを作成する関数。
第2引数はStream内の値を結果オブジェクトに入れる関数。
第3引数は結果オブジェクト同士をつないで1つの結果にする関数。
collectの例

並列ストリームだと複数回supplierが呼ばれて
複数の中間結果オブジェクトが作られる可能性があり、
その場合はcombinerで結合する。
List<String> list = Arrays.asList("a", "b", "c", "d");
StringBuilder sb = list.stream().collect(
  StringBuilder::new,
  (b, t) -> b.append(t),
  (b1, b2) -> b1.append(b2));
System.out.println(sb);
abcd List<String> list = Arrays.asList("a", "b", "c", "d");
StringBuilder sb1 = new StringBuilder(); // supplier
for (String t : list.subList(0, 2)) {
  sb1.append(t); // accumulator
}
StringBuilder sb2 = new StringBuilder(); // supplier
for (String t : list.subList(2, 4)) {
  sb2.append(t); // accumulator
}

// combiner
StringBuilder sb = sb1.append(sb2);

System.out.println(sb);
 
(R, T) -> void accumulator
(R, R) -> void combiner
collect R Collector<T, A, R> collector 結果を作成する。
Collector
Stream<String> s = Stream.of("a", "b", "c");
List<String> list = s.collect(Collectors.toList());
System.out.println(list);
[a, b, c]   val s = Stream("a", "b", "c")
val list = s.toList
toList List<T>     不変Listを返す。(Java16以降)[2021-03-21] Stream<String> s = Stream.of("a", "b", "c");
List<String> list = s.toList();
System.out.println(list);
[a, b, c]   val s = Stream("a", "b", "c")
val list = s.toList
min Optional<T> (T, T) -> int comparator 最小値を返す。
比較関数(Comparator)を渡す。
空ストリームの場合はOptional.emptyが返る。
List<String> list = Arrays.asList("b", "a", "d", "c");
Optional<String> m = list.stream().min(Comparator.naturalOrder());
System.out.println(m);
Optional[a] List<String> list = Arrays.asList("b", "a", "d", "c");
Optional<String> m;
if (list.isEmpty()) {
  m = Optional.empty();
} else {
  String s = Collections.min(list, Comparator.naturalOrder());
  m = Optional.of(s);
}
System.out.println(m);
val list = Seq("b", "a", "d", "c")
val m = if (list.isEmpty) None else Some(list.minBy(identity))
println(m)
max Optional<T> (T, T) -> int comparator 最大値を返す。
比較関数(Comparator)を渡す。
空ストリームの場合はOptional.emptyが返る。
List<String> list = Arrays.asList("b", "a", "d", "c");
Optional<String> m = list.stream().max(Comparator.naturalOrder());
System.out.println(m);
Optional[d] List<String> list = Arrays.asList("b", "a", "d", "c");
Optional<String> m;
if (list.isEmpty()) {
  m = Optional.empty();
} else {
  String s = Collections.max(list, Comparator.naturalOrder());
  m = Optional.of(s);
}
System.out.println(m);
val list = Seq("b", "a", "d", "c")
val m = if (list.isEmpty) None else Some(list.maxBy(identity))
println(m)
count long     要素数を返す。 List<String> list = Arrays.asList("a", "b", "c");
long c = list.stream().count();
System.out.println(c);
3 List<String> list = Arrays.asList("a", "b", "c");
int c = list.size();
System.out.println(c);
val list = Seq("a", "b", "c")
val c = list.size
println(c)
anyMatch boolean (T) -> boolean predicate 1つでも条件に合致する値があればtrueを返す。
条件判定の関数(Predicate)を渡す。
条件に合致する値が見つかった時点で処理を終了する。(短絡評価。「||」相当)
空ストリームの場合はfalseを返す。
List<String> list = Arrays.asList("a", "b", "c");
boolean m = list.stream().anyMatch(s -> s.equals("b"));
System.out.println(m);
true List<String> list = Arrays.asList("a", "b", "c");
boolean m = false;
for (String s : list) {
  if (s.equals("b")) {
    m = true;
    break;
  }
}
System.out.println(m);
val list = Seq("a", "b", "c")
val m = list.exists(_.equals("b"))
println(m)
allMatch boolean (T) -> boolean predicate 全ての値が条件に合致したらtrueを返す。
条件判定の関数(Predicate)を渡す。
条件に合致しない値が見つかった時点で処理を終了する。(短絡評価。「&&」相当)
空ストリームの場合はtrueを返す。
List<String> list = Arrays.asList("a", "b", "c");
boolean m = list.stream().allMatch(s -> s.length() == 1);
System.out.println(m);
true List<String> list = Arrays.asList("a", "b", "c");
boolean m = true;
for (String s : list) {
  if (s.length() != 1) {
    m = false;
    break;
  }
}
System.out.println(m);
val list = Seq("a", "b", "c")
val m = list.forall(_.length == 1)
println(m)
noneMatch boolean (T) -> boolean predicate 条件に合致する値が1つもなかったらtrueを返す。
条件判定の関数(Predicate)を渡す。
条件に合致する値が見つかった時点で処理を終了する。(短絡評価)
空ストリームの場合はtrueを返す。
List<String> list = Arrays.asList("a", "b", "c");
boolean m = list.stream().noneMatch(s -> Character.isUpperCase(s.charAt(0)));
System.out.println(m);
true List<String> list = Arrays.asList("a", "b", "c");
boolean m = true;
for (String s : list) {
  if (Character.isUpperCase(s.charAt(0))) {
    m = false;
    break;
  }
}
System.out.println(m);
val list = Seq("a", "b", "c")
val m = list.find(_.charAt(0).isUpper).isEmpty
println(m)
findFirst Optional<T>     先頭の値を返す。
空ストリームの場合はOptional.emptyを返す。
List<String> list = Arrays.asList("a", "b", "c");
Optional<String> s = list.stream().findFirst();
System.out.println(s);
Optional[a] List<String> list = Arrays.asList("a", "b", "c");
Optional<String> s = list.isEmpty() ? Optional.empty() : Optional.of(list.get(0));
System.out.println(s);
val list = Seq("a", "b", "c")
val s = list.headOption
println(s)
findAny Optional<T>     ストリーム内のいずれかの値を返す。
空ストリームの場合はOptional.emptyを返す。
List<String> list = Arrays.asList("a", "b", "c");
Optional<String> s = list.stream().findAny();
System.out.println(s);
Optional[a]    

BaseStream

BaseStreamは、Stream系インターフェースに共通なメソッドを宣言しているインターフェース。

BaseStreamの中間処理メソッド
メソッド 戻り型 引数 説明 Scala相当
コーディング 結果
sequential Stream     直列ストリームを返す。 Stream<String> s = Stream.of("a");
Stream<String> ss = s.sequential();
System.out.println(ss.isParallel());
false val s = Stream("a")
val ss = s.seq
println(ss.isParallel)
parallel Stream     並列ストリームを返す。
isParallelメソッド
Stream<String> s = Stream.of("a");
Stream<String> ps = s.parallel();
System.out.println(ps.isParallel());
true val s = Stream("a")
val ps = s.par
println(ps.isParallel)
unordered Stream     順序を持たないストリームを返す。 Stream<String> s = Stream.of("a");
Stream<String> us = s.unordered();
   
onClose Stream () -> void closeHandler クローズ時に呼ばれるハンドラーを登録する。
closeメソッド
closeを明示的に呼ばないとonCloseは呼ばれないので注意。[2015-12-13]
Stream<String> s = Stream.of("a")
  .onClose(() -> {
    System.out.println("close");
  });
   

BaseStreamの終端処理メソッド
メソッド 戻り型 引数 説明 Scala相当
コーディング 結果
iterator Iterator<T>     イテレーターを返す。
(→Iterableに変換してfor-each文を使う例[2019-11-28]
Stream<String> s = Stream.of("a", "b");
for (Iterator<String> i = s.iterator(); i.hasNext();) {
  System.out.println(i.next());
}
a
b
val s = Stream("a", "b")
val i = s.iterator
while (i.hasNext) {
  println(i.next)
}
spliterator Spliterator<T>     Streamを作成するのに使う。[2015-12-12]
StreamSupport
     

BaseStreamのその他のメソッド
メソッド 戻り型 引数 説明 Scala相当
コーディング 結果
isParallel boolean     並列ストリームかどうかを返す。
sequentialメソッドparallelメソッド
Stream<String> s = Stream.of("a").parallel();
System.out.println(s.isParallel());
true val s = Stream("a").par
println(s.isParallel)
close void     ストリームをクローズする。
具体的には、onCloseメソッドで登録されたハンドラーを呼び出す。
クローズ後に他の処理を実行することは出来ない。
BaseStreamはAutoCloseableを継承しているので、リソース付きtry文も使える。
try (Stream<String> s = Stream.of("a", "b").onClose(() -> {
  System.out.println("close");
})) {
  s.forEach(System.out::println);
}
a
b
close
 

プリミティブStream

Streamとは別に、intを扱うIntStream、longを扱うLongStream、doubleを扱うDoubleStreamがある。
Streamと同名のメソッドであれば処理内容は同等だが、引数や戻り値の型がプリミティブ用になっている。

プリミティブStreamの中間処理メソッド
メソッド IntStream LongStream DoubleStream 説明
戻り型 引数 戻り型 引数 戻り型 引数
filter IntStream (int) -> boolean predicate LongStream (long) -> boolean predicate DoubleStream (double) -> boolean predicate  
map IntStream (int) -> int mapper LongStream (long) -> long mapper DoubleStream (double) -> double mapper  
mapToObj Stream<U> (int) -> U mapper Stream<U> (long) -> U mapper Stream<U> (double) -> U mapper 値をオブジェクトに変換する。
mapToInt   IntStream (long) -> int mapper IntStream (double) -> int mapper 値をintに変換する。
mapToLong LongStream (int) -> long mapper   LongStream (double) -> long mapper 値をlongに変換する。
mapToDouble DoubleStream (int) -> double mapper DoubleStream (long) -> double mapper   値をdoubleに変換する。
flatMap IntStream (int) -> IntStream mapper LongStream (long) -> LongStream mapper DoubleStream (double) -> DoubleStream mapper  
mapMulti IntStream (int, IntConsumer) -> void mapper LongStream (long, LongConsumer) -> void mapper DoubleStream (double, DoubleConsumer) -> void mapper Java16以降。[2021-03-21]
distinct IntStream     LongStream     DoubleStream      
sorted IntStream     LongStream     DoubleStream      
peek IntStream (int) -> void action LongStream (long) -> void action DoubleStream (double) -> void action  
limit IntStream long maxSize LongStream long maxSize DoubleStream long maxSize  
skip IntStream long n LongStream long n DoubleStream long n  
takeWhile IntStream (int) -> boolean predicate LongStream (long) -> boolean predicate DoubleStream (double) -> boolean predicate Java9以降。[2017-09-24]
dropWhile IntStream (int) -> boolean predicate LongStream (long) -> boolean predicate DoubleStream (double) -> boolean predicate Java9以降。[2017-09-24]
asLongStream LongStream         LongStreamを返す。
asDoubleStream DoubleStream     DoubleStream       DoubleStreamを返す。
boxed Stream<Integer>     Stream<Long>     Stream<Double>     ラッパークラスのStreamを返す。
逆変換はmapToInt等を使う。

プリミティブStreamの終端処理メソッド
メソッド IntStream LongStream DoubleStream 説明
戻り型 引数 戻り型 引数 戻り型 引数
forEach void (int) -> void action void (long) -> void action void (double) -> void action  
forEachOrdered void (int) -> void action void (long) -> void action void (double) -> void action  
toArray int[]     long[]     double[]      
reduce int int identity long long identity double double identity  
(int, int) -> int op (long, long) -> long op (double, double) -> double op
reduce OptionalInt (int, int) -> int op OptionalLong (long, long) -> long op OptionalDouble (double, double) -> double op  
collect R () -> R supplier R () -> R supplier R () -> R supplier  
(R, int) -> void accumulator (R, long) -> void accumulator (R, double) -> void accumulator
(R, R) -> void combiner (R, R) -> void combiner (R, R) -> void combiner
sum int     long     double     合計値を算出する。
min OptionalInt     OptionalLong     OptionalDouble     最小値を取得する。
max OptionalInt     OptionalLong     OptionalDouble     最大値を取得する。
count long     long     long     要素数を取得する。
average OptionalDouble     OptionalDouble     OptionalDouble     平均値を算出する。
summaryStatistics IntSummary
Statistics
    LongSummary
Statistics
    DoubleSummary
Statistics
    要素数・合計値・最小値・最大値・平均値を取得する。
anyMatch boolean (int) -> boolean predicate boolean (long) -> boolean predicate boolean (double) -> boolean predicate  
allMatch boolean (int) -> boolean predicate boolean (long) -> boolean predicate boolean (double) -> boolean predicate  
noneMatch boolean (int) -> boolean predicate boolean (long) -> boolean predicate boolean (double) -> boolean predicate  
findFirst OptionalInt     OptionalLong     OptionalDouble      
findAny OptionalInt     OptionalLong     OptionalDouble      

onCloseが呼ばれるタイミング

Streamでは、Streamクローズ時の処理をonCloseメソッドによって指定できる。[2015-12-13]

ファイルをオープンする等のStreamでは、onCloseでファイルをクローズするような使い方がされている。

Files.lines()の例:

	public static Stream<String> lines(Path path, Charset cs) throws IOException {
		BufferedReader br = Files.newBufferedReader(path, cs);
		try {
			return br.lines().onClose(asUncheckedRunnable(br));
		} catch (Error|RuntimeException e) {
			try {
				br.close();
			} catch (IOException ex) {
				try {
					e.addSuppressed(ex);
				} catch (Throwable ignore) {}
			}
			throw e;
		}
	}

ただし、onCloseが呼ばれるのは、Streamがクローズされた場合である。
つまり、closeメソッドが呼ばれた際にonCloseが呼ばれる。
(StreamはAutoCloseableを実装しているのでcloseメソッドを持っており、try-with-resources構文が使える)
そして、終端処理(forEachメソッド等)を実行しただけではcloseメソッドは呼ばれない。closeメソッドは自分で呼び出す必要がある。

したがって、Files.lines()を使う例だと、明示的にクローズする使い方が正しい。

try(Stream<String> s = Files.lines(Path)) {
    s.forEach(System.out::println);
}
Stream<String> s = Files.lines(Path));
s.forEach(System.out::println);

なお、作られたStreamがonCloseを使っているかどうかを知る方法は無い(と思う)ので、返されたStreamをクローズすべきかどうかはメソッド毎に注意する必要がある。
(全てのStreamに対して必ずクローズを呼び出せば大丈夫ではありそう^^;)


flatMapメソッドを使う場合、flatMap内で作られたStreamは自動的にクローズされる。
(全体のStreamがクローズされるかどうかとは無関係に、そのStreamが使い終わった時点でクローズされるようだ)

  最終クローズあり 最終クローズなし
static Stream<String> create() {
    return Stream.of(Arrays.asList("a", "b", "c"), Arrays.asList("d", "e"))
        .onClose(() -> System.out.println("close2"))
        .flatMap(list -> list.stream().onClose(() -> System.out.println("close1")));
}
try (Stream<String> s = create()) {
    s.forEach(System.out::println);
}
Stream<String> s = create();
s.forEach(System.out::println);
実行結果 a
b
c
close1
d
e
close1
close2
a
b
c
close1
d
e
close1

Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま