2016-08-12 1 views
2

私はこの状況にあります:HashSetで特定の値を見つけたら、フィールドを更新し、セットをクリアしてフィールドを返す必要があります。後で、私はセットを再利用する必要があるという事実を考えると、それは安全ですセットにこれを行う場合、私は思ったんだけど正しい値が見つかるとSetをループでクリアしても安全ですか?

static Set<Integer> testSet = new HashSet<>(); 
static Integer myField = null;  // the field could be already != null 

public static int testClearSet() 
{ 
    for (int i = 0; i < 100; i++) { // this is just for the test 
     testSet.add(i); 
    } 
    for (Integer n : testSet) { 
     if (n == 50) { 
      myField = n; 
      testSet.clear(); 
      return myField; 
     } 
    } 
    return -1; 
} 

:ここでは一例 。

私はこれを聞いています。反復しながらコレクションを変更することを知っていたので、「良い練習」ではありませんが、このケースは少し違うと思います。

可能な解決策は、次のようになります。だから、

boolean clear = false; 
for (Integer n : testSet) { 
    if (n == 50) { 
     myField = n; 
     clear = true; 
     break; 
    } 
} 
if (clear) { 
    testSet.clear(); 
    return myField; 
} 

、正しい方法は一つですか?

+0

あなたの解決策は大丈夫だと思います。内側がnまたは50に戻り、コードが少なくなると内側が変わり、常に50を返します。だから大丈夫です。 – Raskayu

+1

'iterator'が' clear() 'の後で使用され、その内部状態がSetと一致しない場合、イテレータによって送出されたConcurrentModificationExceptionが発生した場合にのみ有効です。 –

答えて

0

ConcurrentModificationExceptionのみがスローされます。

あなたがやることは、それを変更して反復を中止することです。つまり、for-eachの実装に関係なく、100%安全でなければなりません。

実際の問題は、コードの可読性です。理想的には1つのコードを1つの仕事にする必要があり、この仕事が複雑な場合は分割してください。具体的には、あなたのコードは二つの部分、条件とアクションました:だから

if (some condition) do some action 

:あなたが把握するため

public static int testClearSet() { 
    if (setConatins(50)) { 
     myField = 50; 
     testSet.clear(); 
     return myField; 
    } 
    return -1; 
} 

private static boolean setConatins(int searchFor) { 
    for (Integer n : testSet) { 
     if (n == searchFor) { 
      return true; 
     } 
    } 
    return false; 
} 

後者の方法は、単一のAPI呼び出しに置き換えることができます。

+0

ええ、これは私の最初の考えでした。なぜなら、 'testSet.clear()'の直後の繰り返しを中断しているので、すべてがうまくいくはずですが、この疑いが出てきました。実際には、私の実際の問題はちょっと「複雑すぎる」という複雑さがあり、「分割する」アプローチは本当にうまくいきます。 –

0

Setが1つのスレッドでのみ変更されることがわかっている場合は、最初の例のように修正できます。

方法clear()ConcurrentModificationExceptionを投げません。

1

明示的なイテレータを使用する場合、要素をセットから削除することは安全です。そこで以下では、安全である必要があります:あなたはそれを手動で変更した後、反復継続する場合

Iterator<Integer> iterator = testSet.iterator(); 
while (iterator.hasNext()) { 
    Integer element = iterator.next(); 
    if (element.intValue() == 50) { 
     testSet.clear(); 
     break; 
    } 
} 
0

両方のコードが機能します。

フェイル・ファスト・イテレータを使用して反復処理を行う場合、実際にはコレクションの変更に制限があります。つまり、イテレータの作成後にコレクションに変更があると、フェイル・ファスト・イテレータを使用した反復は失敗します。 Javaコレクションクラスによって返されるすべてのデフォルトイテレータは、フェイルファーストイテレータです。

private void removeDataTest (Collection<String> c, String item) { 
    Iterator<String> iter = c.iterator(); //Iterator is created. 
    while (iter.hasNext()) { 
     String data = iter.next(); 
     if (data.equals(item)) { 
      //c.remove(data); //Problem. Directly modifying collection after this iterator is created. In the next iteration it will throw concurrent modification exception. 
      iter.remove(); //This is fine. Modify collection through iterator. 
      //c.clear(); break; //This is also should be okay. Modifying the collection directly, but after that it is breaking out and not using the iterator. 
     } 
    } 
} 

あなたのコードでは、セットが変更された後も繰り返しを続けません。だからうまくいくはずです。

関連する問題