Javaプログラミング:List要素を削除する3つの正しい方法
8481 ワード
リストの要素を削除すると、次の2つの問題が発生します。
正しい削除ロジックをコード例で実証します
package com.ips.list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ArrayListRemove {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("beijing");
list.add("shanghai");
list.add("shanghai");
list.add("guangzhou");
list.add("shenzhen");
list.add("hangzhou");
remove11(list, "shanghai");
}
private static void print(List list){
for (String item : list) {
System.out.println(" :" + item);
}
}
/*
*
*/
public static void remove11(List list, String target){
int size = list.size();
for(int i = 0; i < size; i++){
String item = list.get(i);
if(target.equals(item)){
list.remove(item);
}
}
print(list);
}
/*
*
*/
public static void remove12(List list, String target){
for(int i = 0; i < list.size(); i++){
String item = list.get(i);
if(target.equals(item)){
list.remove(item);
}
}
print(list);
}
/*
*
*/
public static void remove13(List list, String target){
int size = list.size();
for(int i = size - 1; i >= 0; i--){
String item = list.get(i);
if(target.equals(item)){
list.remove(item);
}
}
print(list);
}
/*
*
*/
public static void remove14(List list, String target){
for(int i = list.size() - 1; i >= 0; i--){
String item = list.get(i);
if(target.equals(item)){
list.remove(item);
}
}
print(list);
}
/*
*
*/
public static void remove21(List list, String target){
for(String item : list){
if(target.equals(item)){
list.remove(item);
}
}
print(list);
}
/*
*
*/
public static void remove22(ArrayList list, String target) {
final CopyOnWriteArrayList cowList = new CopyOnWriteArrayList(list);
for (String item : cowList) {
if (item.equals(target)) {
cowList.remove(item);
}
}
print(cowList);
}
/*
*
*/
public static void remove31(List list, String target){
Iterator iter = list.iterator();
while (iter.hasNext()) {
String item = iter.next();
if (item.equals(target)) {
list.remove(item);
}
}
print(list);
}
/*
*
*/
public static void remove32(List list, String target){
Iterator iter = list.iterator();
while (iter.hasNext()) {
String item = iter.next();
if (item.equals(target)) {
iter.remove();
}
}
print(list);
}
}
remove 11メソッドを実行すると、次のエラーが発生します。
Exception in thread “main” java.lang.IndexOutOfBoundsException: Index: 5, Size: 5 at java.util.ArrayList.rangeCheck(ArrayList.java:635) at java.util.ArrayList.get(ArrayList.java:411) at com.ips.list.ArrayListRemove.remove11(ArrayListRemove.java:33) at com.ips.list.ArrayListRemove.main(ArrayListRemove.java:17)
int size = list.size();
がリストのサイズを早めに取得したため、forループで2つの要素が削除され、配列境界の問題が発生した.remove 12メソッドを実行すると、次のエラーが発生します。
要素値:beijing要素値:shanghai要素値:guangzhou要素値:shenzhen要素値:hangzhou
文字列"shanghai"は削除する、この方法は配列の境界を越えた問題を解決したが、完全にデータを削除する問題を解決しなかった.remove(Object 0)メソッド:
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
要素を削除するときにelseロジックを実行し、fastRemove(index)メソッドを呼び出します.
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
コードにより、リスト削除要素の論理は、ターゲット要素の後の要素をインデックス位置に移動し、最後の要素をnullに設定し、size-1であることが分かった.これは、2番目の「shanghai」が削除されていない理由を説明します.
remove 13メソッドを実行します。正しいです。
要素値:beijing要素値:guangzhou要素値:shenzhen要素値:hangzhou
remove 14メソッドを実行します。正しいです。
要素値:beijing要素値:guangzhou要素値:shenzhen要素値:hangzhou
ではremove 13とremove 14の違いは何でしょうか.答えは違いませんが、remove 11とremove 12には違いがあり、remove 12では
for(int i = 0; i < list.size(); i++)
の実行ごとにsize値が計算され、消費性能が比較されます.remove 21メソッドを実行すると、次のエラーが発生します。
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at com.ips.list.ArrayListRemove.remove21(ArrayListRemove.java:82)
at com.ips.list.ArrayListRemove.main(ArrayListRemove.java:17)
java.util.ConcurrentModificationException
異常が発生しました.foreachの書き方は実際には正しいIterable,hasNext,nextメソッドの略です.そこで,List.iterator()
から解析に着手し,Itr反復器オブジェクトを返すiterator()
法を追跡した. public Iterator iterator() {
return new Itr();
}
Itrクラス定義コード:
private class Itr implements Iterator {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
コードによりItrはArrayListで定義されたプライベート内部クラスであり,next,removeメソッドではいずれもcheckForComodificationメソッドが呼び出され,
modCount != expectedModCount
が等しいか否かを判断し,等しくなければConcurrentModificationException
異常を放出する役割を果たす.removeメソッドが正常に実行されるたびに、expectedModCount = modCount
を実行し、2つの値が等しいことを保証すると、問題は基本的に明らかになり、foreachサイクルでlist.remove(item);
を実行し、listオブジェクトのmodCount値を変更したが、listオブジェクトの反復器のexpectedModCount値は変更されなかったため、ConcurrentModificationException
異常が投げ出された.remove 22メソッドを実行します。正しいです。
要素値:beijing要素値:guangzhou要素値:shenzhen要素値:hangzhouは
CopyOnWriteArrayList
によってListの同時問題を解決した.remove 31メソッドを実行します。次のエラーが発生しました。
Exception in thread “main” java.util.ConcurrentModificationException at java.util.ArrayList I t r . c h e c k F o r C o m o d i f i c a t i o n ( A r r a y L i s t . j a v a : 859 ) a t j a v a . u t i l . A r r a y L i s t Itr.checkForComodification(ArrayList.java:859) at java.util.ArrayList Itr.checkForComodification(ArrayList.java:859)atjava.util.ArrayListItr.next(ArrayList.java:831) at com.ips.list.ArrayListRemove.remove31(ArrayListRemove.java:109) at com.ips.list.ArrayListRemove.main(ArrayListRemove.java:17)
実行remove 21による異常と一致し、問題発生の原因も一致する.
remove 32メソッドを実行します。正しいです。
要素値:beijing要素値:guangzhou要素値:shenzhen要素値:hangzhou