インタフェースStableValue<T>
- 型パラメータ:
T- コンテンツのタイプ
StableValueは、JavaプラットフォームのプレビューAPIです。
StableValue<T>は通常、ファクトリ・メソッドStableValue.of()を使用して作成されます。 このように作成すると、安定した値は unsetになります。これは、contentsを保持しないことを意味します。 T型のコンテンツは、trySet()、setOrThrow()またはorElseSet()をコールして設定できます。 一度設定すると、コンテンツは変更できず、orElseThrow()、orElse()またはorElseSet()をコールして取得できます。
次の例を考えてみます。安定値フィールド"logger"は、Logger型の浅く不変なコンテンツ・ホルダーであり、最初にunsetとして作成され、コンテンツが保持されないことを意味します。 この例の後半で、「logger」フィールドの状態がチェックされ、まだ unsetである場合は、内容が setになります。
public class Component {
// Creates a new unset stable value with no contents
private final StableValue<Logger> logger = StableValue.of();
private Logger getLogger() {
if (!logger.isSet()) {
logger.trySet(Logger.create(Component.class));
}
return logger.orElseThrow();
}
public void process() {
getLogger().info("Process started");
// ...
}
}
getLogger()が複数のスレッドからコールされると、Loggerの複数のインスタンスが作成される場合があります。 ただし、コンテンツは、最初のライターが勝つことを意味する最大1回のみ設定できます。
Loggerのインスタンスが1つしか作成されないことを保証するために、orElseSet()メソッドをかわりに使用できます。このメソッドでは、コンテンツが遅延計算され、サプライヤを介してアトミックに設定されます。 次の例では、サプライヤはラムダ式の形式で提供されます。
public class Component {
// Creates a new unset stable value with no contents
private final StableValue<Logger> logger = StableValue.of();
private Logger getLogger() {
return logger.orElseSet( () -> Logger.create(Component.class) );
}
public void process() {
getLogger().info("Process started");
// ...
}
}
getLogger()メソッドは、安定した値に対してlogger.orElseSet()をコールして、その内容を取得します。 安定値がunsetの場合、orElseSet()は指定されたサプライヤを評価し、結果にコンテンツを設定します。その結果はクライアントに返されます。 つまり、orElseSet()は、安定した値の内容が戻される前に設定されていることを保証します。
さらに、orElseSet()は、提供されている1つ以上のサプライヤのうち、最大で1つのサプライヤのみが評価され、logger.orElseSet()が同時に起動された場合でも1回のみ評価されることを保証します。 サプライヤの評価に副作用がある可能性があるため、このプロパティは重要です。たとえば、Logger.create()への前述のコールによってストレージ・リソースが準備される場合があります。
安定関数
安定した値は、より高いレベルの機能抽象化の基礎となります。 安定供給者は、値を計算し、その値をバッキング安定値ストレージにキャッシュしてその後に使用するための供給者です。 安定サプライヤは、StableValue.supplier()ファクトリを介して作成され、安定サプライヤへの初回アクセス時に起動される基礎となるサプライヤを提供します。 public class Component {
private final Supplier<Logger> logger =
StableValue.supplier( () -> Logger.getLogger(Component.class) );
public void process() {
logger.get().info("Process started");
// ...
}
}
Component内のコードは、getLogger()などのアクセッサ・メソッドを介さずに、安定サプライヤから直接ロガー・オブジェクトを取得できます。
stable int関数は、intパラメータを取得し、それを使用して、そのパラメータ値のバッキング安定値記憶域によってキャッシュされる結果を計算する関数です。 安定したIntFunctionは、StableValue.intFunction()ファクトリを介して作成されます。 作成時に、入力範囲([0, size))は、入力値ごとに最大1回起動される基礎となるIntFunctionとともに指定されます。 実際、stable int関数は、基礎となるIntFunctionのキャッシュのように動作します。
final class PowerOf2Util {
private PowerOf2Util() {}
private static final int SIZE = 6;
private static final IntFunction<Integer> UNDERLYING_POWER_OF_TWO =
v -> 1 << v;
private static final IntFunction<Integer> POWER_OF_TWO =
StableValue.intFunction(SIZE, UNDERLYING_POWER_OF_TWO);
public static int powerOfTwo(int a) {
return POWER_OF_TWO.apply(a);
}
}
int result = PowerOf2Util.powerOfTwo(4); // May eventually constant fold to 16 at runtime
PowerOf2Util.powerOfTwo()ファンクションは、基礎となるファンクションのUNDERLYING_POWER_OF_TWO入力範囲のサブセット[0, 5]のみを許可する部分ファンクションです。
安定関数は、パラメータ(T型)を取得し、それを使用して結果(R型)を計算し、そのパラメータ値のバッキング安定値記憶域によってキャッシュされる関数です。 安定した関数は、StableValue.function()ファクトリを介して作成されます。 作成時に、入力Setは、入力パラメータごとに最大1回起動される基礎となる関数とともに指定されます。 実際には、安定した関数は基礎となる関数のキャッシュのように動作します。
class Log2Util {
private Log2Util() {}
private static final Set<Integer> KEYS =
Set.of(1, 2, 4, 8, 16, 32);
private static final UnaryOperator<Integer> UNDERLYING_LOG2 =
i -> 31 - Integer.numberOfLeadingZeros(i);
private static final Function<Integer, Integer> LOG2 =
StableValue.function(KEYS, UNDERLYING_LOG2);
public static int log2(int a) {
return LOG2.apply(a);
}
}
int result = Log2Util.log2(16); // May eventually constant fold to 4 at runtime
Log2Util.log2()ファンクションは、基礎となるファンクションのUNDERLYING_LOG2入力範囲のサブセット{1, 2, 4, 8, 16, 32}のみを許可する部分ファンクションです。
安定したコレクション
安定した値は、変更不可能なコレクションのバッキング・ストレージとしても使用できます。 安定リストは、安定した値の配列に裏付けられた変更不可能なリストです。 安定したリスト要素は、指定されたIntFunctionを使用して、最初にアクセスされたときに計算されます。final class PowerOf2Util {
private PowerOf2Util() {}
private static final int SIZE = 6;
private static final IntFunction<Integer> UNDERLYING_POWER_OF_TWO =
v -> 1 << v;
private static final List<Integer> POWER_OF_TWO =
StableValue.list(SIZE, UNDERLYING_POWER_OF_TWO);
public static int powerOfTwo(int a) {
return POWER_OF_TWO.get(a);
}
}
int result = PowerOf2Util.powerOfTwo(4); // May eventually constant fold to 16 at runtime
同様に、安定マップは、構築時にキーが認識される変更不可能なマップです。 安定したマップ値は、指定された関数を使用して、最初にアクセスしたときに計算されます。
class Log2Util {
private Log2Util() {}
private static final Set<Integer> KEYS =
Set.of(1, 2, 4, 8, 16, 32);
private static final UnaryOperator<Integer> UNDERLYING_LOG2 =
i -> 31 - Integer.numberOfLeadingZeros(i);
private static final Map<Integer, INTEGER> LOG2 =
StableValue.map(CACHED_KEYS, UNDERLYING_LOG2);
public static int log2(int a) {
return LOG2.get(a);
}
}
int result = Log2Util.log2(16); // May eventually constant fold to 4 at runtime
安定した値の合成
安定した値は、他の安定した値に依存し、遅延計算できるが、個々の要素へのアクセスが依然として高い依存性グラフを形成できます。 次の例では、単一のFooおよびBarインスタンス(Fooインスタンスに依存)が遅延的に作成され、どちらも安定した値で保持されます。
public final class DependencyUtil {
private DependencyUtil() {}
public static class Foo {
// ...
}
public static class Bar {
public Bar(Foo foo) {
// ...
}
}
private static final Supplier<Foo> FOO = StableValue.supplier(Foo::new);
private static final Supplier<Bar> BAR = StableValue.supplier(() -> new Bar(FOO.get()));
public static Foo foo() {
return FOO.get();
}
public static Bar bar() {
return BAR.get();
}
}
bar()をコールすると、Barシングルトンが作成されます(まだ作成されていない場合)。 そのような作成時に、Fooがまだ存在しない場合は、依存するFooが最初に作成されます。
より複雑な依存関係グラフを持つ別の例は、フィボナッチシーケンスを遅延的に計算することです。
public final class Fibonacci {
private Fibonacci() {}
private static final int MAX_SIZE_INT = 46;
private static final IntFunction<Integer> FIB =
StableValue.intFunction(MAX_SIZE_INT, Fibonacci::fib);
public static int fib(int n) {
return n < 2
? n
: FIB.apply(n - 1) + FIB.apply(n - 2);
}
}
FIBとFibonacci::fibの両方が相互に再帰します。 安定したint関数FIBは中間結果をキャッシュするため、従来の非キャッシュ再帰フィボナッチ法と比較して、初期計算の複雑さが指数関数から線形に減少します。 計算されると、VMはFibonacci.fib(5)のような定数倍式の自由になります。
前述のfibonacciの例は有向環状グラフです(つまり、循環依存関係がなく、したがって依存関係ツリーです)。
___________fib(5)____________
/ \
____fib(4)____ ____fib(3)____
/ \ / \
fib(3) fib(2) fib(2) fib(1)
/ \ / \ / \
fib(2) fib(1) fib(1) fib(0) fib(1) fib(0)
スレッド安全性
安定した値の内容は、最大1回設定することが保証されます。 競合しているスレッドが安定した値を設定するために競合している場合、1つの更新のみが成功し、他の更新は安定した値が設定されるまでブロックされます。その後、他の更新では安定した値が設定され、安定した値は変更されません。正常な読取り操作(orElseThrow()など)の前に成功する安定した値に対する書込み操作が1回以上実行されます(trySet()など)。 正常な書込み操作は次のいずれかです。
trueを返すtrySet(Object)setOrThrow(Object)がスローされない、または- サプライヤを正常に実行している
orElseSet(Supplier)
orElseThrow()がスローされないother値を返さないorElse(other)throwではないorElseSet(Supplier)、またはtrueを返すisSet()
メソッドorElseSet(Supplier)は、指定されたサプライヤが競合中であっても、最大で1回正常に起動することを保証します。 orElseSet(Supplier)の呼出しは、ゼロ以上の例外的な呼出しの合計順序を形成し、その後にゼロ(コンテンツがすでに設定されている場合)または1回の正常な呼出しが続きます。 安定した関数と安定したコレクションは orElseSet()と同じ原則の上に構築されるため、スレッドも安全であり、入力ごとに1回以上起動することが保証されます。
パフォーマンス
安定した値の内容は、設定後に変更されることはないため、JVM実装は、安定した値を設定するために、その安定した値の将来のすべての読取りを取り除き、以前に観察した内容を直接使用する場合があります。 これは、安定値への参照が定数である場合(たとえば、安定値自体がstatic finalフィールドに格納されている場合)に当てはまります。 安定した関数およびコレクションは、StableValueの上に構築されます。 そのため、StableValueと同じJVM最適化の対象となる場合もあります。 - 実装要件:
StableValueのクラスの実装は、thisで自由に同期できるため、StableValueで(直接的または間接的に)同期しないようにする必要があります。 したがって、thisで同期するとデッドロックが発生する可能性があります。StableValueの内容自体、orElse(other)パラメータおよびequals(obj)パラメータを除き、すべてのメソッド・パラメータがnull以外であるか、NullPointerExceptionがスローされます。- 実装上のノート:
StableValueは、主にクラスの非公開フィールドであることを意図しており、通常、アクセッサを介して直接公開されることも、メソッド・パラメータとして渡されることもありません。安定した関数およびコレクションは、呼び出されたときに内部安定値の評価をトリガーしない
Object.toString()操作を提供するために合理的な努力をします。 安定したコレクションには、コール時に内部安定値の評価を最小限に抑えようとするObject.equals(Object)操作があります。オブジェクトは安定した値で設定できますが、削除されないため、意図しないメモリー・リークのソースになる可能性があります。 安定した値の内容は強く到達可能です。 到達可能な安定した値は、安定した値自体が収集されるまで、設定された内容を保持することに注意してください。
(任意のランクの)配列型である型パラメータ
Tを持つStableValueでは、JVMは配列参照を安定した値としてただし、そのコンポーネントではないとしてのみ処理できます。 代わりに、任意の深さの安定したリストを使用でき、安定したコンポーネントが提供されます。 より一般的には、安定した値は任意の深さの他の安定した値を保持することができ、それでも推移的な定数を提供する。安定した値、関数、およびコレクションは
Serializableではありません。- 導入されたバージョン:
- 25
-
メソッドのサマリー
修飾子と型メソッド説明booleanthis == objの場合はtrue、それ以外の場合はfalseを返します。static <T,R> Function <T, R> 新しい安定した関数を返します。inthashCode()thisオブジェクトのアイデンティティ・ハッシュ・コードを返します。static <R> IntFunction<R> intFunction(int size, IntFunction<? extends R> underlying) 新しい安定したIntFunctionを返します。booleanisSet()内容が設定されている場合はtrue、それ以外の場合はfalseを返します。static <E> List<E> list(int size, IntFunction<? extends E> mapper) 指定されたsizeを持つ新しい安定リストを返します。static <K,V> Map <K, V> 指定されたkeysを持つ新しい安定したマップを返します。static <T> StableValuePREVIEW<T> of()新しい未設定の安定値を返します。static <T> StableValuePREVIEW<T> of(T contents) 指定されたcontentsで新しい事前設定された安定値を返します。設定されている場合はコンテンツを戻し、それ以外の場合は指定されたother値を戻します。内容を返します。設定を解除すると、最初に指定されたsupplierを使用して内容の計算および設定を試みます。設定されている場合はコンテンツを戻し、それ以外の場合はNoSuchElementExceptionをスローします。voidsetOrThrow(T contents) このStableValueの内容を指定されたcontentsに設定するか、すでに設定されている場合はIllegalStateExceptionをスローします。static <T> Supplier<T> 新規安定仕入先を返します。booleanこのStableValueの内容を指定されたcontentsに設定しようとします。
-
メソッドの詳細
-
trySet
boolean trySet(T contents) このStableValueの内容を指定されたcontentsに設定しようとします。 このStableValueの内容は1回のみ設定でき、このメソッドはtrueを1回のみ返します。このメソッドが戻ると、このStableValueの内容は常に設定されます。
- パラメータ:
contents- 設定- 戻り値:
- このStableValueの内容が指定された
contentsに設定されている場合はtrue、それ以外の場合はfalse - スロー:
IllegalStateException-orElseSet(Supplier)によって起動されたサプライヤが、このメソッドを直接または間接的にコールして、この安定した値を再帰的に設定しようとする場合。
-
orElse
-
orElseThrow
T orElseThrow()設定されている場合はコンテンツを戻し、それ以外の場合はNoSuchElementExceptionをスローします。- 戻り値:
- 設定されている場合は内容、設定されていない場合は
NoSuchElementExceptionをスローします。 - スロー:
NoSuchElementException- コンテンツが設定されていない場合
-
isSet
boolean isSet()内容が設定されている場合はtrue、それ以外の場合はfalseを返します。- 戻り値:
- 内容が設定されている場合は
true、それ以外の場合はfalse
-
orElseSet
内容を返します。設定を解除すると、最初に指定されたsupplierを使用して内容の計算および設定を試みます。指定された
supplierは、例外をスローせずに完了した場合、最大1回起動することが保証されます。 このメソッドが別のサプライヤで複数回呼び出された場合、例外をスローせずに完了すると、そのうちの1つのみが起動されます。サプライヤが(未チェック)例外をスローすると、その例外は再スローされ、コンテンツは設定されません。 最も一般的な使用方法は、次のように、遅延計算値または記憶された結果として機能する新しいオブジェクトを作成することです。
Value v = stable.orElseSet(Value::new);このメソッドが正常に戻ると、コンテンツは常に設定されます。
指定された
supplierは、supplierが例外をスローしないかぎり、複数のスレッドから呼び出された場合でも1回のみ起動されます。- パラメータ:
supplier- 以前に設定されていない場合、内容の計算に使用されます。- 戻り値:
- 内容。設定を解除すると、最初に指定された
supplierを使用して内容の計算および設定が試行されます。 - スロー:
IllegalStateException- 指定されたsupplierが、この安定した値を再帰的に設定しようとした場合。
-
setOrThrow
void setOrThrow(T contents) このStableValueの内容を指定されたcontentsに設定するか、すでに設定されている場合はIllegalStateExceptionをスローします。このメソッドが戻り(または例外をスロー)すると、コンテンツは常に設定されます。
- パラメータ:
contents- 設定- スロー:
IllegalStateException- 内容がすでに設定されている場合IllegalStateException-orElseSet(Supplier)によって起動されたサプライヤが、このメソッドを直接または間接的にコールして、この安定した値を再帰的に設定しようとする場合。
-
equals
-
hashCode
int hashCode()thisオブジェクトのアイデンティティ・ハッシュ・コードを返します。- オーバーライド:
hashCode、クラスObject- 戻り値:
thisオブジェクトのアイデンティティ・ハッシュ・コード- 関連項目:
-
of
static <T> StableValuePREVIEW<T> of()新しい未設定の安定値を返します。設定されていない安定した値には内容がありません。
- 型パラメータ:
T- コンテンツのタイプ- 戻り値:
- 新しい未設定の安定値
-
of
static <T> StableValuePREVIEW<T> of(T contents) 指定されたcontentsで新しい事前設定された安定値を返します。- 型パラメータ:
T- コンテンツのタイプ- パラメータ:
contents- 設定- 戻り値:
- 指定された
contentsの新しい事前設定安定値
-
supplier
新規安定仕入先を返します。返されたサプライヤは、返されたサプライヤのget()メソッドを介して最初にアクセスされたときに、提供された
underlyingサプライヤの値を記録するキャッシュ・サプライヤです。指定された
underlyingサプライヤは、マルチスレッド環境でも最大1回正常に起動することが保証されます。 値がすでに計算中である場合に、返されたサプライヤのget()メソッドを呼び出す競合スレッドは、値が計算されるか、または計算スレッドによって例外がスローされるまでブロックされます。 その後、競合するスレッドは、新しく計算された値(ある場合)を監視し、underlyingサプライヤを実行しません。指定された
underlyingサプライヤが例外をスローすると、最初のコール元に再スローされ、コンテンツは記録されません。指定された
underlyingサプライヤが戻されたサプライヤを再帰的にコールすると、IllegalStateExceptionがスローされます。- 型パラメータ:
T- 返されたサプライヤから提供された結果のタイプ- パラメータ:
underlying- キャッシュされた値の計算に使用されるサプライヤ- 戻り値:
- 新しい安定したサプライヤー
-
intFunction
static <R> IntFunction<R> intFunction(int size, IntFunction<? extends R> underlying) 新しい安定したIntFunctionを返します。戻される関数は、許可される
int入力ごとに、戻される関数のapply()メソッドを介して最初にアクセスされたときに、指定されたunderlying関数の値を記録するキャッシュ関数です。 戻されたファンクションが[0, size)の範囲内にない入力で呼び出されると、IllegalArgumentExceptionがスローされます。指定された
underlying関数は、マルチスレッド環境でも、許可される入力ごとに最大1回正常に起動することが保証されます。 値がすでに計算中であるときに、返された関数の apply()メソッドを呼び出す競合スレッドは、値が計算されるか、または計算スレッドによって例外がスローされるまでブロックされます。指定された
underlying関数を呼び出すと例外がスローされ、最初の呼出し元に再スローされ、内容は記録されません。指定された
underlying関数が、同じ入力に対して戻された関数を再帰的にコールすると、IllegalStateExceptionがスローされます。- 型パラメータ:
R- 返されるIntFunctionによって提供される結果のタイプ- パラメータ:
size- 許容される入力を示す範囲[0, size)の上限underlying- キャッシュされた値の計算に使用されるIntFunction- 戻り値:
- 新しい安定した IntFunction
- スロー:
IllegalArgumentException- 指定されたsizeが負の場合。
-
function
static <T,R> Function<T,R> function(Set<? extends T> inputs, Function<? super T, ? extends R> underlying) 新しい安定した関数を返します。戻される関数は、
inputsの指定されたセットで許可される各入力について、戻される関数のapply()メソッドを介して最初にアクセスされたときに、指定されたunderlying関数の値を記録するキャッシュ関数です。 戻された関数がinputsにない入力で呼び出されると、IllegalArgumentExceptionがスローされます。指定された
underlying関数は、マルチスレッド環境でも、許可される入力ごとに最大1回正常に起動することが保証されます。 値がすでに計算中であるときに、返された関数の apply()メソッドを呼び出す競合スレッドは、値が計算されるか、または計算スレッドによって例外がスローされるまでブロックされます。指定された
underlying関数を呼び出すと例外がスローされ、最初の呼出し元に再スローされ、内容は記録されません。指定された
underlying関数が、同じ入力に対して戻された関数を再帰的にコールすると、IllegalStateExceptionがスローされます。- 型パラメータ:
T- 返されるファンクションへの入力のタイプR- 返されるファンクションによって提供される結果のタイプ- パラメータ:
inputs- (NULL以外)許可される入力値のセットunderlying- キャッシュされた値の計算に使用されるFunction- 戻り値:
- 新しい安定した機能
- スロー:
NullPointerException- 指定されたinputsセットにnull要素が含まれている場合。
-
list
static <E> List<E> list(int size, IntFunction<? extends E> mapper) 指定されたsizeを持つ新しい安定リストを返します。返されるリストは、指定された
sizeを含む変更できないリストです。 リストの要素は、最初にアクセスされたときに(たとえば、List::getを介して)提供されたmapperを介して計算されます。指定された
mapperファンクションは、マルチスレッド環境でも、リスト索引ごとに最大1回正常に起動することが保証されます。 計算中の要素にすでにアクセスしている競合スレッドは、要素が計算されるか、計算スレッドによって例外がスローされるまでブロックされます。指定された
mapper関数を呼び出すと例外がスローされ、最初の呼出し元に再スローされ、要素の値は記録されません。返されるリストの
subListビューまたはList.reversed()ビューも安定しています。返されるリストとその
subListビューまたはList.reversed()ビューは、RandomAccessインタフェースを実装します。返されるリストは変更できず、Listインタフェースにオプションの操作を実装しません。
指定された
mapperが、同じ索引に対して戻されたリストを再帰的にコールすると、IllegalStateExceptionがスローされます。- 型パラメータ:
E- 返されるリスト内の要素のタイプ- パラメータ:
size- 返されるリストのサイズmapper- 要素が最初にアクセスされるたびに起動します(nullを返す場合があります)- 戻り値:
- 指定された
sizeの新しい安定したリスト - スロー:
IllegalArgumentException- 指定されたsizeが負の場合。
-
map
指定されたkeysを持つ新しい安定したマップを返します。返されるマップは、構築時にキーが認識される変更不可のマップです。 マップの値は、指定された
mapperを使用して、最初にアクセスされたとき(たとえば、Map::getを介して)計算されます。指定された
mapper関数は、マルチスレッド環境でも、キーごとに最大1回正常に起動することが保証されます。 すでに計算中の値にアクセスしている競合スレッドは、要素が計算されるか、または計算スレッドによって例外がスローされるまでブロックされます。指定された
mapper関数を呼び出すと例外がスローされ、初期コール元に再スローされ、指定されたキーに関連付けられた値は記録されません。返されるマップの
Map.values()ビューまたはMap.entrySet()ビューも安定しています。返されるマップは変更できず、Mapインタフェースにオプションの操作を実装しません。
指定された
mapperが、返されたマップを同じキーに対して再帰的にコールすると、IllegalStateExceptionがスローされます。- 型パラメータ:
K- 返されるマップによって保持されるキーのタイプV- 返されるマップ内のマップされた値のタイプ- パラメータ:
keys- 返されるマップ内の(null以外の)キーmapper- 関連付けられた値が最初にアクセスされるたびに起動します(nullを返す場合があります)- 戻り値:
- 指定された
keysの新しい安定したマップ - スロー:
NullPointerException- 指定されたinputsセットにnull要素が含まれている場合。
-
StableValueを使用できます。