JavaのStackWalker APIを使ってさまざまなスタックフレームを出力する方法
StackWalker APIとは
Java 9では、遅延評価(レイジー処理)とフレームフィルタリングを提供するStackWalker APIが定義されました。StackWalkerオブジェクトを使用することで、スタックの走査やアクセスが可能になり、特に有用なメソッドとしてwalk()が用意されています。
walk()メソッドは、現在のスレッドに対してStackFrameのストリームを開き、そのStackFrameストリームに関数を適用します。StackWalkerオブジェクトを取得するには、StackWalker.getInstance()メソッドを使用します。
以下の例では、StackWalker APIを使って、次の3つのパターンでスタックフレームを出力する方法を紹介します。
- すべてのスタックフレームを出力する
- 一部のスタックフレームをスキップする
- 出力するスタックフレームの数を制限(limit)する
コード例
import java.lang.StackWalker.StackFrame;
import java.util.*;
import java.util.stream.*;
public class StackWalkerTest {
public static void main(String args[]) {
new StackWalkerTest().walk();
}
private void walk() {
new Walker1().walk();
}
private class Walker1 {
public void walk() {
new Walker2().walk();
}
}
private class Walker2 {
public void walk() {
Method1();
}
void Method1() {
Method2();
}
void Method2() {
// オプションと推定スタック深度を指定してインスタンスを取得
StackWalker stackWalker = StackWalker.getInstance(Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE, StackWalker.Option.SHOW_HIDDEN_FRAMES), 16);
Stream<StackFrame> stackStream = StackWalker.getInstance().walk(f -> f);
System.out.println("--- Walk all StackFrames ---");
List<String> stacks = walkAllStackframes();
System.out.println(stacks);
System.out.println("--- Skip some StackFrames ---");
List<String> stacksAfterSkip = walkSomeStackframes(3);
System.out.println(stacksAfterSkip);
System.out.println("--- Limit StackFrames ---");
List<String> stacksByLimit = walkLimitStackframes(3);
System.out.println(stacksByLimit);
}
private List<String> walkAllStackframes() {
return StackWalker.getInstance().walk(s -> s.map(frame -> "\n" + frame.getClassName() + "/" + frame.getMethodName()).collect(Collectors.toList()));
}
private List<String> walkSomeStackframes(int numberOfFrames) {
return StackWalker.getInstance().walk(s -> s.map(frame -> "\n" + frame.getClassName() + "/" + frame.getMethodName()).skip(numberOfFrames).collect(Collectors.toList()));
}
private List<String> walkLimitStackframes(int numberOfFrames) {
return StackWalker.getInstance().walk(s -> s.map(frame -> "\n" + frame.getClassName() + "/" + frame.getMethodName()).limit(numberOfFrames).collect(Collectors.toList()));
}
}
}
実行結果
--- Walk all StackFrames --- [ StackWalkerTest$Walker2/walkAllStackframes, StackWalkerTest$Walker2/Method2, StackWalkerTest$Walker2/Method1, StackWalkerTest$Walker2/walk, StackWalkerTest$Walker1/walk, StackWalkerTest/walk, StackWalkerTest/main ] --- Skip some StackFrames --- [ StackWalkerTest$Walker2/walk, StackWalkerTest$Walker1/walk, StackWalkerTest/walk, StackWalkerTest/main ] --- Limit StackFrames --- [ StackWalkerTest$Walker2/walkLimitStackframes, StackWalkerTest$Walker2/Method2, StackWalkerTest$Walker2/Method1 ]
ポイント解説
上記のコードにおける重要なポイントは以下の通りです。
- walkAllStackframes():ストリームの
map()で各フレームのクラス名とメソッド名を結合した文字列に変換し、collect()ですべてのフレームをリストとして収集しています。 - walkSomeStackframes():中間操作の
skip(3)により、先頭から3つのフレームをスキップした結果を出力しています。 - walkLimitStackframes():中間操作の
limit(3)により、最初の3つのフレームのみを取得しています。 - StackWalker.Option.RETAIN_CLASS_REFERENCE:各スタックフレームから呼び出し元クラスのClassオブジェクトへの参照を保持できるようにするオプションです。
- StackWalker.Option.SHOW_HIDDEN_FRAMES:通常は非表示となる特殊なフレーム(ラムダ式など)も表示対象に含めるオプションです。
-
Javaでスタックを使ってキュー(Queue)を実装する方法を解説
キューとスタックの基本Queue(キュー)は Collection インターフェースを継承したクラスで、FIFO(First-In-First-Out:先入れ先出し)方式による要素の挿入と削除をサポートします。一方、Stack(スタック)は Vector クラスのサブクラスであり、LIFO(Last-In-First-Out:後入れ先出し)方式でオブジェクトを管理します。つまり、スタックの一番上に追加された最後の要素が、最初に取り出される要素になります。この2つのデータ構造の性質は正反対ですが、スタックを2つ組み合わせることで、キューを実装することが可能です。以下では、その具体的な実装方法を紹
-
Javaでキュー(Queue)を使ってスタック(Stack)を実装する方法
Stack(スタック)はVectorクラスのサブクラスであり、オブジェクトを後入れ先出し(LIFO:Last-In-First-Out)で管理するデータ構造です。スタックの最上部に最後に追加された要素が、最初に取り出される要素になります。 一方、Queue(キュー)はCollectionインターフェースを継承しており、先入れ先出し(FIFO:First-In-First-Out)に基づく挿入(insert)と削除(remove)の操作をサポートします。 一見すると性質の異なるこの2つのデータ構造ですが、キューの使い方を少し工夫するだけで、キューのみを使ってスタックを実装することができます。 キ