列挙型の valueOf メソッドの簡単なフェールセーフ実装を提供する小さなヘルパー クラスを実装しました。これは、値が見つからない場合、例外ではなく null を返すことを意味します。
コードは次のとおりです。
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.io.Serializable;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* <p>
* This permits to easily implement a failsafe implementation of the enums's valueOf method
* </p>
*
* <p>
* Basic usage exemple on an enum class called MyEnum:
* FailSafeValueOf.get(MyEnum.class).valueOf("EnumName");
* </p>
*
* @author Sebastien Lorber <i>(lorber.sebastien@gmail.com)</i>
*/
public class FailSafeValueOf<T extends Enum<T>> implements Serializable {
/**
* This will cache a FailSafeValueOf for each enum so that we do not need to recompute a map each time
*/
private static final Map< Class<? extends Enum<?>> , FailSafeValueOf<? extends Enum<?>> > CACHE = Maps.newHashMap();
private final Map<String,T> nameToEnumMap;
private FailSafeValueOf(Class<T> enumClass) {
Map<String,T> map = Maps.newHashMap();
for ( T value : EnumSet.allOf(enumClass)) {
map.put( value.name() , value);
}
this.nameToEnumMap = ImmutableMap.copyOf(map);
}
/**
* Returns the value of the given enum element
* If the element is not found, null will be returned, and no exception will be thrown
* @param enumName
* @return
*/
public T valueOf(String enumName) {
return nameToEnumMap.get(enumName);
}
/**
* Get a failsafe value of implementation for a given enum
* @param enumClass
* @param <U>
* @return
*/
public static <U extends Enum<U>> FailSafeValueOf<U> get(Class<U> enumClass) {
FailSafeValueOf<U> fsvo = (FailSafeValueOf<U>)CACHE.get(enumClass);
if ( fsvo == null ) {
synchronized (FailSafeValueOf.class) {
fsvo = (FailSafeValueOf<U>)CACHE.get(enumClass);
if ( fsvo == null ) {
fsvo = new FailSafeValueOf<U>(enumClass);
CACHE.put(enumClass,fsvo);
}
}
}
return fsvo;
}
}
アクセスごとに新しい FailSafeValueOf を作成する (少しの) オーバーヘッドが必要ないため、既にビルド済みの FailSafeValueOf インスタンスにアクセスした列挙型ごとに保持するキャッシュを作成しました。
私は並行性の処理に慣れていません。FailSafeValueOf が不変であり、FailSafeValueOf の 2 つの異なるインスタンスが同じ列挙型の get メソッドによって返される可能性がある場合、同時アクセスは大きな問題にはなりません。しかし、スレッドセーフの私の実装がその方法であるかどうか、そしてそれが本当にスレッドセーフであるかどうかを知りたいですか? (主に学習目的)
しばらくすると、すべての FailSafeValueOf がキャッシュに作成され、同時スレッドが get メソッドに入るのを禁止する必要がないため、メソッドを同期させたくありません。
だから私が作ったのは、最初にキャッシュミスがあるかどうかをチェックしてから、アトミックに同期されたブロックを作成することです:キャッシュを再度チェックし、最終的にインスタンスを作成します。それはスレッドセーフであり、そのような必要性のために行う方法はありますか?
ちなみに、enum は値の数が少ないことがよくあります。そのような場合、HashMap は適切な構造ですか? キャッシュを使用する代わりに、EnumSet を反復処理して適切な値を取得する方が高速ですか?
編集:
Guava チームが Optional を返すメソッド Enums.getIfPresent() をリリースしたため、私のクラスはあまり役に立たないことに注意してください。