5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaAdvent Calendar 2024

Day 10

Javaで学ぶアルゴリズム -スタック(java.util.Stack と java.util.ArrayDeque のパフォーマンス比較)

Posted at

Javaでの効率的なスタック実装:ArrayDeque vs Stack

Javaの標準ライブラリには java.util.Stackjava.util.ArrayDeque という二つのスタック実装があるけど、

スタックとして使われるときはStackよりも高速

と言われているので検証。

実行環境

  • Java 8
  • ヒープサイズ:-Xms512m -Xmx1024m で設定

テスト(検証用)コード

  • スタックの基本的な操作である push / pop / peek をそれぞれ 1000000 回やる
  • System.nanoTime() を使用して、1000000 回実行したときのナノ秒単位の時間を取得する
StackPerformanceTest.java
import java.util.ArrayDeque;
import java.util.Stack;

public class StackPerformanceTest {

	private static final int OPERATIONS_COUNT = 1000000;

	public static void main(String[] args) {

		long[] stackTimes = measureStackPerformance();
		long[] dequeTimes = measureArrayDequePerformance();

		System.out.println("Operation\tStack (ns)\tArrayDeque (ns)");
		System.out.println("Push\t\t" + stackTimes[0] + "\t\t" + dequeTimes[0]);
		System.out.println("Peek\t\t" + stackTimes[1] + "\t\t" + dequeTimes[1]);
		System.out.println("Pop\t\t" + stackTimes[2] + "\t\t" + dequeTimes[2]);
	}

	private static long[] measureStackPerformance() {
		Stack<Integer> stack = new Stack<>();
		long pushTime = 0;
		long peekTime = 0;
		long popTime = 0;

		long pushStart = System.nanoTime();
		for (int i = 0; i < OPERATIONS_COUNT; i++) {
			stack.push(i);
		}
		pushTime += System.nanoTime() - pushStart;

		long peekStart = System.nanoTime();
		for (int i = 0; i < OPERATIONS_COUNT; i++) {
			stack.peek();
		}
		peekTime += System.nanoTime() - peekStart;

		long popStart = System.nanoTime();
		for (int i = 0; i < OPERATIONS_COUNT; i++) {
			stack.pop();
		}
		popTime += System.nanoTime() - popStart;

		return new long[] { pushTime, peekTime, popTime };
	}

	private static long[] measureArrayDequePerformance() {
		ArrayDeque<Integer> deque = new ArrayDeque<>();
		long pushTime = 0;
		long peekTime = 0;
		long popTime = 0;

		long pushStart = System.nanoTime();
		for (int i = 0; i < OPERATIONS_COUNT; i++) {
			deque.push(i);
		}
		pushTime += System.nanoTime() - pushStart;

		long peekStart = System.nanoTime();
		for (int i = 0; i < OPERATIONS_COUNT; i++) {
			deque.peek();
		}
		peekTime += System.nanoTime() - peekStart;

		long popStart = System.nanoTime();
		for (int i = 0; i < OPERATIONS_COUNT; i++) {
			deque.pop();
		}
		popTime += System.nanoTime() - popStart;

		return new long[] { pushTime, peekTime, popTime };
	}
}

実行結果

平均値比較(※ 単位はナノ秒)

観点 \ ライブラリ java.util.Stack java.util.ArrayDeque
push 20,356,860 6,374,430
peek 17,176,950 254,450
pop 19,884,760 795,100

各回比較(※ 単位はナノ秒)

Stack Stack Stack ArrayDeque ArrayDeque ArrayDeque
- push peek pop push peek pop
1 28770200 34148300 44782999 14775600 2544100 3526000
2 27759301 15423599 15435001 13483700 0 536701
3 25714001 15431900 15267799 6342900 0 519700
4 16711800 15421600 15442400 3009800 0 475001
5 16810599 15161099 15479701 2940400 100 477399
6 22395800 15311601 15372400 3061600 100 544300
7 16666500 16591599 32503301 3013500 0 474600
8 16678900 15316400 15426399 11209401 100 488500
9 16178200 14491301 14553700 2948001 0 454999
10 15883300 14472101 14583899 2959400 100 453800

優位に java.util.ArrayDeque の方がパフォーマンスよいことが分かるけど、ArrayDequeのpeeks処理があまりにも最適化されてそう(キャッシュの影響かなぁ?)なので、プログラムを書き換えて再実行してみる。

テスト(検証用)コードその2

計測の仕様を以下のように変更したうえで、全体のパフォーマンスを測定してみる。

  • すべての要素を push する
  • その後 peek と pop を繰り返す
  • 1000000 この要素分の push / peek / pop の合計時間を測定
StackPerformanceTest.java
import java.util.ArrayDeque;
import java.util.Stack;

public class StackPerformanceTest {

	private static final int OPERATIONS_COUNT = 1000000;

	public static void main(String[] args) {

		StringBuilder sb = new StringBuilder();
		sb.append("回数, java.util.Stack, java.util.ArrayDeque");
		sb.append(System.lineSeparator());

		for (int i = 1; i < 11; i++) {

			long stackTimes = measureStackPerformance();
			long dequeTimes = measureArrayDequePerformance();

			sb.append(i + ", " + stackTimes + ", " + dequeTimes);
			sb.append(System.lineSeparator());
		}
		System.out.println(sb.toString());
	}

	private static long measureStackPerformance() {
		Stack<Integer> stack = new Stack<>();
		long stackTime = 0;

		long start = System.nanoTime();
		for (int i = 0; i < OPERATIONS_COUNT; i++) {
			stack.push(i);
		}

		for (int i = 0; i < OPERATIONS_COUNT; i++) {
			stack.peek();
			stack.pop();
		}
		stackTime = System.nanoTime() - start;

		return stackTime;
	}

	private static long measureArrayDequePerformance() {
		ArrayDeque<Integer> deque = new ArrayDeque<>();
		long dequeTime = 0;

		long start = System.nanoTime();
		for (int i = 0; i < OPERATIONS_COUNT; i++) {
			deque.push(i);
		}

		for (int i = 0; i < OPERATIONS_COUNT; i++) {
			deque.peek();
			deque.pop();
		}

		dequeTime = System.nanoTime() - start;

		return dequeTime;
	}
}

実行結果

とはいえ、優位に java.util.ArrayDeque の方がパフォーマンスよいね。

平均値比較(※ 単位はナノ秒)

回数 \ ライブラリ java.util.Stack java.util.ArrayDeque
平均 38,941,760 6,880,650

各回比較(※ 単位はナノ秒)

回数 java.util.Stack java.util.ArrayDeque
1 88,468,199 17,380,101
2 41,395,700 13,799,700
3 39,710,899 7,140,700
4 30,828,800 3,426,101
5 31,008,800 3,410,800
6 36,107,300 3,417,100
7 30,359,300 3,375,900
8 30,327,900 10,002,101
9 30,658,101 3,418,000
10 30,552,600 3,435,999
5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?