java-beginner.com ブログ

プログラミングを学習するブログ(Javaをメインに)

Stream APIを使い、int型配列を降順に並び替える

投稿日:

最終更新日:2021年03月21日

アイキャッチ

こんにちは。「Javaを復習する初心者」です。今日はStream APIの復習として、IntStreamのsorted()メソッドを使ってみました。

IntStream#sorted()

最初に試したのは、IntStreamインターフェースに定義されているsorted()メソッドです。自宅のノートパソコンにはJava12のAPI仕様書がダウンロードされているので、それで使い方を調べてみました。このメソッドは引数がないことが分かりました。

以下のサンプルプログラムでは、int型配列からストリームを取得して、sorted()メソッドを呼び出しています。

ソース

import java.util.Arrays;
import java.util.stream.IntStream;

public class Sample0001 {

	public static void main(String[] args) {
		int[] numbers_before = {10 ,5, 2, 4, 1, 7, 9, 5, 3, 11, 2};

		int[] numbers_after =IntStream.of(numbers_before).sorted().toArray();

		System.out.println("before");
		System.out.println(Arrays.toString(numbers_before));
		System.out.println("after");
		System.out.println(Arrays.toString(numbers_after));
	}

}

結果

before
[10, 5, 2, 4, 1, 7, 9, 5, 3, 11, 2]
after
[1, 2, 2, 3, 4, 5, 5, 7, 9, 10, 11]

上記のように、昇順に並び変わりました。

降順に並び替える

降順に並び変えるにはどうすればよいでしょうか。用意されているメソッドを使う場合、以下のメソッドを使うのが一般的だと思います。

  • public static <T> void sort(List<T> list, Comparator<? super T> c)</li>

ただし、上記のメソッドは引数にListを指定しなければなりません。今回はint型の配列を使っているので、これをListにする必要があります。

Eclipseで試行錯誤した結果、以下のように喜寿すると降順に並び替えることができました。

ソース

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;

public class Sample0002 {

	public static void main(String[] args) {
		int[] numbers_before = {10 ,5, 2, 4, 1, 7, 9, 5, 3, 11, 2};

		System.out.println("before");
		System.out.println(Arrays.toString(numbers_before));

		// 降順に並び替える
		Integer[] integers = IntStream.of(numbers_before).boxed().toArray(Integer[]::new);

		List<Integer> list = Arrays.asList(integers);
		Collections.sort(list, Collections.reverseOrder());

		int[] numbers_after = list.stream().mapToInt(i -> i).toArray();

		System.out.println("after");
		System.out.println(Arrays.toString(numbers_after));
	}

}

結果

before
[10, 5, 2, 4, 1, 7, 9, 5, 3, 11, 2]
after
[11, 10, 9, 7, 5, 5, 4, 3, 2, 2, 1]

変数integersの宣言の個所では、最初of()メソッドを使い、IntStreamを取得しています。このストリームはint型の値で構成されています。

取得したストリームのboxed()メソッドを呼び出しています。このメソッドはIntegerにボクシングした結果から構成されるStreamを返します。このメソッドにより、Integer型の値で構成されているストリームを取得することができました。

toArray(Integer[]::new)という記述で、Integer型の配列を取得できました。

変数listの宣言では、Arrays#asList()メソッドを使っています。このメソッドでは引数にオブジェクトの配列を指定できます。今回はInteger型の配列を指定することにより、Integer型のリストを取得しています。

リストをソートするために、Collections#sort()メソッドを呼び出しています。このメソッドは2種類用意されていますが、今回使ったのは以下の定義のメソッドです。

  • public static <T> void sort(List<T> list, Comparator<? super T> c)

第1引数にソートしたいリストを指定します。第2引数はインタフェースComparator<T>を実装したクラス指定します。今回は数値の降順に並び変えることが目的です。そのためのオブジェクトはCollectionsクラスのreverseOrder()メソッドで取得することができます。このメソッドはstaticメソッドなので、Collections.reverseOrder()という記述ができます。

sort()メソッドにより、第1引数に指定したlistは降順にソートされました。あとはint型配列に変換するだけです。今回はStream APIを使った方法で、int型配列に変換しました。

int型配列を取得するために、mapToInt()メソッドを使ってIntStreamを取得しました。このメソッドの引数はインタフェースToIntFunction<T>です。これは関数型インターフェースなので、ラムダ式の書き方ができます。int型配列の各値に対して、同じ値を持つInteger型配列が取得できれば良いので、引数iからiをそのまま返却するというラムダ式を書きました。

最後に、toArray()メソッドを呼び出すことで、int型配列を取得できました。

Stream APIを使わない方法

今回の目的はint型配列を降順にしたいだけなので、Stream APIを使わなくてもできます。

次のようなメソッドを作ってみました。引数で受け取ったint型配列を降順に並べ替えた新しい配列を返却します。

ソース

	public static int[] sort_reverse(int[] numbers_before) {
		int[] numbers_tmp = Arrays.copyOf(numbers_before, numbers_before.length);

		Arrays.sort(numbers_tmp);

		int[] numbers_after = new int[numbers_tmp.length];
		for (int i = 0; i < numbers_after.length; i++) {
			numbers_after[i] = numbers_tmp[numbers_after.length -1 - i];
		}

		return numbers_after;
	}

上記プログラムでは、配列の後ろから順番に新しい配列に格納しているだけです。

処理の速さを比較

このプログラムを作った段階で、Stream APIを使った方法とどのくらい処理速度に差ができるのか疑問に思いました。なので、次のような比較用のプログラムを作りました。

ソース

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;

public class Sample0004 {

	public static void main(String[] args) {
		int[] numbers_before = {10 ,5, 2, 4, 1, 7, 9, 5, 3, 11, 2};

		final int MAX_COUNT = 10000;

		// Stream APIを使った場合
		long start = System.currentTimeMillis();
		for (int i = 0; i < MAX_COUNT; i++) {
			sort_reverse1(numbers_before);
		}
		long end = System.currentTimeMillis();
		System.out.println("Stream APIを使った場合");
		System.out.println(end - start);

		// Stream APIを使った場合
		start = System.currentTimeMillis();
		for (int i = 0; i < MAX_COUNT; i++) {
			sort_reverse2(numbers_before);
		}
		end = System.currentTimeMillis();
		System.out.println("Stream APIを使わない場合");
		System.out.println(end - start);
	}

	public static int[] sort_reverse1(int[] numbers_before) {
		Integer[] integers = IntStream.of(numbers_before).boxed().toArray(Integer[]::new);

		List<Integer> list = Arrays.asList(integers);
		Collections.sort(list, Collections.reverseOrder());

		int[] numbers_after = list.stream().mapToInt(i -> i).toArray();

		return numbers_after;
	}
	public static int[] sort_reverse2(int[] numbers_before) {
		int[] numbers_tmp = Arrays.copyOf(numbers_before, numbers_before.length);

		Arrays.sort(numbers_tmp);

		int[] numbers_after = new int[numbers_tmp.length];
		for (int i = 0; i < numbers_after.length; i++) {
			numbers_after[i] = numbers_tmp[numbers_after.length -1 - i];
		}

		return numbers_after;
	}

}

結果

Stream APIを使った場合
74
Stream APIを使わない場合
7

結果を見るとStream APIを使わないメソッドの方がかなり速いです。

話をまとめると以下のことがわかりました。

[my_box_with_title title=”まとめ”]

  • Stream APIを使ってint型の配列を降順に並び替えることが出来る。
  • Stream APIを使わない方が処理速度が速い。

[/my_box_with_title]

以上、参考になれば幸いです。