2017-10-25 5 views
0

私は、設定された重みを持つアイテムのリストを取り、ランダムに1を選択するメソッドを作成しようとしています。私の解決策は、Integerをハッシュマップから1つのキーをランダムに選択するウェイトとして使用するハッシュマップを使用することでした。 HashMapのキーは、Objectの種類を混在させることができ、選択したキーのうち1つを返すことができます。突然変異を避けながらヌル値を返さないようにするにはどうすればよいですか?

しかし、私はnullの値を突然変異を避けることの上に返すことを避けたいと思います。はい、私はこれがJavaであることは分かっていますが、Javaを書いて、この問題を解決するためにはより洗練された方法があります。

public <T> T getRandomValue(HashMap<?, Integer> VALUES) { 
     final int SIZE = VALUES.values().stream().reduce(0, (a, b) -> a + b); 
     final int RAND_SELECTION = ThreadLocalRandom.current().nextInt(SIZE) + 1; 
     int currentWeightSum = 0; 
     for (Map.Entry<?, Integer> entry : VALUES.entrySet()) { 
      if (RAND_SELECTION > currentWeightSum && RAND_SELECTION <= (currentWeightSum + entry.getValue())) { 
       return (T) entry.getKey(); 
      } else { 
       currentWeightSum += entry.getValue(); 
      } 
     } 
     return null; 
    } 
+0

最初に、listがnullであり、結果をOptionalにラップするかどうかを確認します。だから、オブジェクトが戻ってきて、いくつかの値を持っていて、簡単にチェックすることができます。しかし、nullを返すJavaでは、nullが正常であれば+ valueing + valueingをチェックするので、nullを避けることを考えるかもしれない – LenglBoy

答えて

1

ループの後のコードは、通常の状況下で到達すべきではありませんので、不規則な条件は右この時点で発見することができるように、あなたは確かに、この時点でreturn nullのようなものを書くのではなく、例外をスローしてはいけませんが呼び出し元に最終的にNullPointerExceptionをデバッグするよう強制するのではなく、まったく別の場所で発生する可能性があります。

public static <T> T getRandomValue(Map<T, Integer> values) { 
    if(values.isEmpty()) 
     throw new NoSuchElementException(); 
    final int totalSize = values.values().stream().mapToInt(Integer::intValue).sum(); 
    if(totalSize<=0) 
     throw new IllegalArgumentException("sum of weights is "+totalSize); 
    final int threshold = ThreadLocalRandom.current().nextInt(totalSize) + 1; 
    int currentWeightSum = 0; 
    for (Map.Entry<T, Integer> entry : values.entrySet()) { 
     currentWeightSum += entry.getValue(); 
     if(threshold <= currentWeightSum) { 
      return entry.getKey(); 
     } 
    } 
    // if we reach this point, the map's content must have been changed in-between 
    throw new ConcurrentModificationException(); 
} 

コードでは、コードのその他の問題を修正しています。実際のマップの種類を知らなくても、任意のTを返すと約束してはいけません。マップに異なるタイプのオブジェクトがキーとして含まれている場合、つまりMap<Object,Integer>の場合、呼び出し元はObjectより具体的なものを取得することはできません。それ以外に、Mapで十分である場合は、パラメータをHashMapとするべきではありません。さらに、変数名をJavaの命名規則に準拠するように変更し、ループの本体を単純化しました。

空のマップを有効な入力としてサポートする場合は、戻り値の型をOptional<T>に変更すると最適な解決方法が得られます。空の場合は空のオプション、それ以外の場合はオプションです(nullのキーは使用できません)。それでも、ループの後の到達不可能なコードポイントには例外が設定されていなければなりません。

関連する問題