これは、最近のインタビューで尋ねられたインタビューの質問です。このブログ投稿では、この質問を要約して共有しています。
1.初心者が犯すよくある間違い
たぶん、多くの初心者(私を含む)は最初に次のように書くことを考えました:
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add("博客园");
platformList.add("CSDN");
platformList.add("掘金");
for (String platform : platformList) {
if (platform.equals("博客园")) {
platformList.remove(platform);
} } System.out.println(platformList);
}
それから私は自信を持ってそれを実行しましたが、java.util.ConcurrentModificationExceptionがスローされたことがわかりました。
なぜこれがそうなのかと不思議に思っていますか?
以下に示すように、最初に上記のコードによって生成されたバイトコードを見てみましょう。
foreachループが実際に実行されると、実際には反復子が使用され、使用されるコアメソッドはhasnext()およびnext()であることがわかります。
次に、ArrayListクラスのIteratorがどのように実装されているかを見てみましょう。
次の要素を取得するためにnext()メソッドが呼び出されると、コードの最初の行はcheckForComodification();を呼び出すことであり、このメソッドのコアロジックは、2つの変数modCountとexpectedModCountの値を比較することです。
上記の例では、最初にmodCountとexpectedModCountの値が両方とも3であるため、要素「Blog Garden」を初めて取得することは問題ありませんが、次のコード行が実行されるときは問題ありません。
platformList.remove(platform);
modCountの値が4に変更されます。
したがって、2回目に要素を取得すると、modCountとexpectedModCountの値が等しくないため、java.util.ConcurrentModificationExceptionがスローされます。
foreachを使用して達成することはできないので、どうすればそれを達成できますか?
主に次の3つの方法があります。
- イテレータのremove()メソッドを使用する
- forループを使用して正の順序でトラバースする
- forループを使用して逆順でトラバースします
次に一つずつ説明します。
2.イテレーターのremove()メソッドを使用します
Iteratorを使用したremove()メソッドの実装は次のとおりです。
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add("博客园");
platformList.add("CSDN");
platformList.add("掘金");
Iterator<String> iterator = platformList.iterator(); while (iterator.hasNext()) {
String platform = iterator.next(); if (platform.equals("博客园")) {
iterator.remove();
} } System.out.println(platformList);
}
出力は次のとおりです。
[CSDN, 掘金]
iterator.remove();を使用する理由
そのソースコードを見てみましょう:
要素が削除されるたびに、modCountの値がexpectedModCountに再割り当てされるため、2つの変数が等しくなり、java.util.ConcurrentModificationExceptionがトリガーされないことがわかります。インタビューの詳細については、公式アカウントのアマチュアグラスに注意してください。
3. forループを使用して正の順序でトラバースする
forループを使用した正の順序の走査の実装は次のとおりです。
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add("博客园");
platformList.add("CSDN");
platformList.add("掘金");
for (int i = 0; i < platformList.size(); i++) {
String item = platformList.get(i);
if (item.equals("博客园")) {
platformList.remove(i);
i = i - 1;
} } System.out.println(platformList);
}
この実装方法は理解が容易です。つまり、配列の添え字を使用して削除しますが、要素を削除した後、添え字の値を変更する必要があることに注意してください。
i = i - 1;
下付き文字の値を修正するのはなぜですか?
最初の要素のインデックスは次のようになっているためです:
最初のサイクルで要素「ブログガーデン」を削除した後、要素の添え字は次のようになります。
2番目のループでは、iの値は1です。つまり、要素「Nuggets」が取得されます。これにより、要素「CSDN」がスキップされてチェックされるため、要素を削除した後、上記の添え字を変更する必要があります。コードでi = i-1;の目的。その他のインタビューの質問については、WeChatサブスクリプション番号「Amateur Grass」に従って、インタビューに返信してください。
4. forループを使用して逆順でトラバースします
forループを使用した逆トラバーサルの実装は次のとおりです。
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add("博客园");
platformList.add("CSDN");
platformList.add("掘金");
for (int i = platformList.size() - 1; i >= 0; i--) {
String item = platformList.get(i);
if (item.equals("掘金")) {
platformList.remove(i);
} } System.out.println(platformList);
}
この実装は、forループを使用して正の順序でトラバースするのと似ていますが、最初の要素の添え字は次のようになるため、添え字を変更する必要はありません。
最初のサイクルで要素「ナゲット」を削除した後、要素の添え字は次のようになります。
2番目のループでは、iの値は1です。つまり、要素「CSDN」が取得されるため、要素がスキップされることはないため、添え字を変更する必要はありません。