やあ!最後のレッスンでは、クラスに慣れ
ArrayList
、このクラスで最も一般的な操作を実行する方法を学びました。さらに、配列ArrayList
と通常の配列のいくつかの違いも指摘しました。ただし、から要素を削除するArrayList
方法という 1 つのトピックを回避しました。それについては今から話し合います。 通常の配列から要素を削除するのはあまり便利ではないことはすでに述べました。要素自体を削除することはできないため、その値を「ゼロにする」(null に設定する) ことしかできません。
public class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public static void main(String[] args) {
Cat[] cats = new Cat[3];
cats[0] = new Cat("Thomas");
cats[1] = new Cat("Behemoth");
cats[2] = new Cat("Lionel Messi");
cats[1] = null;
System.out.println(Arrays.toString(cats));
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
出力: [Cat{name='Thomas'}, null, Cat{name='Lionel Messi'}] ただし、配列要素を null に設定すると、「穴」が残ります。配列内の位置は削除されておらず、その内容のみが削除されています。50 匹の猫がいて、そのうち 17 匹をこの方法で取り除いたらどうなるかを想像してみてください。17 個の穴がある配列になります。ぜひ追跡してみてください。新しい値を書き込むことができる空のセルの数を覚えておくことを期待するのは非現実的です。一歩間違えると、必要なオブジェクト参照が上書きされてしまいます。もちろん、これをもう少し慎重に行う方法もあります。要素を削除した後、要素を配列の先頭に移動して、最後に「穴」を置きます。
public static void main(String[] args) {
Cat[] cats = new Cat[4];
cats[0] = new Cat("Thomas");
cats[1] = new Cat("Behemoth");
cats[2] = new Cat("Lionel Messi");
cats[2] = new Cat("Fluffy");
cats[1] = null;
for (int i = 2; i < cats.length-1; i++) {
cats [i-1] = cats [i];// Move the elements to the front of the array, so the empty position is at the end
}
System.out.println(Arrays.toString(cats));
}
出力: [Cat{name='Thomas'}, Cat{name='Fluffy'}, Cat{name='Fluffy'}, null] これはより良いように思えますが、堅牢なソリューションとは言いがたいです。配列から要素を削除するたびにこのコードを記述する必要があるという事実以外の理由がない場合は、これは悪い選択肢です。別の方法で別のメソッドを作成することもできます。
public void deleteCat(Cat[] cats, int indexToDelete) {
//...delete the cat corresponding to the index and move the elements
}
しかし、これもあまり役に立ちません。このメソッドはオブジェクトでのみCat
機能し、他のタイプでは機能しません。言い換えれば、プログラムに配列で使用したいクラスがさらに 100 個ある場合、それぞれのクラスにまったく同じロジックを備えた同じメソッドを記述する必要があります。これはまったくの惨事です -_- しかし、ArrayList
クラスはこの問題を解決します! 要素を削除するための特別なメソッドを実装します。remove()
public static void main(String[] args) {
ArrayList<Cat> cats = new ArrayList<>();
Cat thomas = new Cat("Thomas");
Cat behemoth = new Cat("Behemoth");
Cat lionel = new Cat("Lionel Messi");
Cat fluffy = new Cat ("Fluffy");
cats.add(thomas);
cats.add(behemoth);
cats.add(lionel);
cats.add(fluffy);
System.out.println(cats.toString());
cats.remove(1);
System.out.println(cats.toString());
}
オブジェクトのインデックスをメソッドに渡すと、(配列の場合と同様に) オブジェクトが削除されます。このremove()
方法には 2 つの特別な特徴があります。まず、「穴」が残りません。これには、要素が中央から削除されたときに要素を移動するために必要なロジックがすでに実装されており、以前に自分たちで作成しました。前のコードの出力を見てください。
[Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
真ん中の猫を1匹取り除き、残りは空きスペースがないように移動させました。 2 番目に、(通常の配列のように) インデックスによってだけでなく、参照によってもオブジェクトを削除できます。
public static void main(String[] args) {
ArrayList<Cat> cats = new ArrayList<>();
Cat thomas = new Cat("Thomas");
Cat behemoth = new Cat("Behemoth");
Cat lionel = new Cat("Lionel Messi");
Cat fluffy = new Cat ("Fluffy");
cats.add(thomas);
cats.add(behemoth);
cats.add(lionel);
cats.add(fluffy);
System.out.println(cats.toString());
cats.remove(lionel);
System.out.println(cats.toString());
}
出力: [猫{name='トーマス'}, 猫{name='ベヒモス'}, 猫{name='ライオネル・メッシ'}, 猫{name='ふわふわ'}] [猫{name='トーマス'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] これは、目的のオブジェクトのインデックスを常に追跡したくない場合に非常に便利です。通常の削除を理解したようです。ここで、次の状況を想像してみましょう。リストを反復処理して、特定の名前の猫を削除したいと考えています。for
これを行うには、 Rishi のレッスンで紹介された 高速ループ (for-each ループとも呼ばれます) を使用します。
public static void main(String[] args) {
ArrayList<Cat> cats = new ArrayList<>();
Cat thomas = new Cat("Thomas");
Cat behemoth = new Cat("Behemoth");
Cat lionel = new Cat("Lionel Messi");
Cat fluffy = new Cat ("Fluffy");
cats.add(thomas);
cats.add(behemoth);
cats.add(lionel);
cats.add(fluffy);
for (Cat cat: cats) {
if (cat.name.equals("Behemoth")) {
cats.remove(cat);
}
}
System.out.println(cats);
}
コードは完全に論理的に見えます。しかし、結果は驚くべきものになるかもしれません。 スレッド「メイン」の例外 java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) at java.util.ArrayList$Itr.next(ArrayList. java:831) at Cat.main(Cat.java:25) 何らかのエラーが発生していますが、なぜ発生したのかは不明です。このプロセスには、対処する必要がある多くの微妙な点が含まれます。覚えておく必要がある一般的なルールは次のとおりです。 コレクションの反復処理とその要素の変更を同時に行うことはできません。 ここで言うのは、単なる削除ではなく、あらゆる種類の変更を意味します。猫の削除を新しい猫の挿入に置き換えても、結果は同じになります。
for (Cat cat: cats) {
cats.add(new Cat("Salem Saberhagen"));
}
System.out.println(cats);
スレッド「メイン」での例外 java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) at java.util.ArrayList$Itr.next(ArrayList.java:831) at Cat.main( Cat.java:25) ある操作を別の操作に変更しましたが、結果は変わりませんでした。同じConcurrentModificationExceptionが発生しました。これは、反復処理中にリストを変更して上記のルールを破ろうとしたときにまさに発生します。Java では、コレクションの反復処理中に項目を削除するには、イテレーター(と呼ばれる特別なオブジェクトが必要ですIterator
このIterator
クラスは、要素のリストを安全に反復処理する責任があります。方法は 3 つだけなので非常に簡単です。
hasNext()
- リストに次の項目があるか、すでに最後の項目に到達しているかに応じて、true または false を返します。next()
- リスト内の次の項目を返しますremove()
- リストから項目を削除します
Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list
Cat nextCat = catIterator.next();// Get the next element
System.out.println(nextCat);// Display it
}
出力: Cat{name='Thomas'} Cat{name='Behemoth'} Cat{name='Lionel Messi'} Cat{name='Fluffy'} ご覧のとおり、 はArrayList
、イテレータ: iterator()
. さらに、イテレータを作成するときは、イテレータが操作するオブジェクトのクラスを指定することに注意してください ( <Cat>
)。要するに、イテレーターは元のタスクを簡単に処理できるということです。たとえば、「Lionel Messi」という名前の猫を削除します。
Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list
Cat nextCat = catIterator.next();// Get the next element
if (nextCat.name.equals("Lionel Messi")) {
catIterator.remove();// Delete the cat with the specified name
}
}
System.out.println(cats);
出力: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] イテレータのメソッドでインデックスも名前も指定していないことに気づいたかもしれremove()
ませ ん!イテレータは見かけよりも賢く、remove()
イテレータによって返された最後の要素を削除します。ご覧のとおり、これは私たちが望んでいたとおりのことを行いました :) 原則として、. から要素を削除することについて知っておくべきことはこれですべてですArrayList
。まあ、ほぼすべてです。次のレッスンでは、このクラスの内部を調べて、さまざまなメソッド呼び出し中にそこで何が起こるかを見ていきます :) それまで!
GO TO FULL VERSION