@rion0726ittoti

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

JavaSilver ArrayListでわからないところがあります。

解決したいこと

ArrayListについての疑問を解消したいです。

エンジニアになり2か月、研修でjavaを学習しています。
現在、JavaSilver資格取得に向けて学習中です。
黒本を解いているのですが、第9章APIのArrayListの問題で解説を読んでも理解できないところがあります。
以下黒本問題集から引用です。

下記2点です。

1点目equalsメソッドについて

public class Item{
	private String name;
	private int price;
	public Item(String name, int price){
		this.name = name;
		this.price = price;
	}
	public boolean equals(Object obj){
		if (obj instanceof Item) {
			Item tmp =(Item) obj;
			if(tmp.name.equals(this.name)){
				return true;
			}
		}
		return false;
	}
	public String getName() {
		return name;
	}
}	

public class Main{
	public static void main(String[] args){
		ArrayList<Item> list = new ArrayList<>();
		list.add(new Item("A", 100));
		list.add(new Item("B", 200));
		list.add(new Item("C", 300));
		list.add(new Item("A", 100));
		list.remove(new Item("A",500));
		for (Item item : list) {
            System.out.println(item.getName());
        }
	
	}
}

上記のコードを実行すると、「BCA」が表示される。
処理としては、
1, Itemインスタンスを4つ生成、リストに追加。
2, removeメソッドで、新しく生成したItemインスタンスのname属性の値に合致するインスタンスを1つ削除する。
3, 拡張for文でlistの中身を取り出して、System.out.println(item.getName());で取り出したインスタンスのnameを表示させる。
という流れだと認識しています。

2,どのインスタンスを削除するのかをItemクラスのeaqualsメソッドを呼び出して、name属性が同じインスタンスがあればtrueを返して、指定する。というように解説には記載してありました。

疑問その1

equalsメソッドは、なぜ呼び出されるのか。
list.add(new Item("A", 100));でインスタンスを生成すると、Itemメソッドが呼び出される(コンストラクタ)、System.out.println(item.getName());でgetNameメソッドを呼び出すはわかります。
list.remove(new Item("A",500));でequalsメソッドを使用しているのはわかるのですが、なぜequalsメソッドが呼び出されるのかがわかりません。

2点目カーソル位置について

public class Main{
	public static void main(String[] args){
		ArrayList<String> list = new ArrayList<>();
		list.add("A");
		list.add("B");
		list.add("C");
		for (String str : list) {
			if("B".equals(str)){
				list.remove(str);
			}else{
            	System.out.println(str);
			}	
        }
	
	}
}

上記のコードを実行すると、「A」が表示される。
拡張for文でlistの中身を取り出して、if("B".equals(str))でリストに格納した"B"を削除する。
1, listに「”A", "B", "C"」を格納する。
2, listから”A”を取り出す。
3, コンソールに"A"が表示される。
4, listから"B"を取り出す。
5, if("B".equals(str)で条件に合致するため、"B"が削除される。
6, "B"が削除されたため、"B"の次の位置にあった"C"が"B"の位置に移動する。
7, listから"C"を取り出そうと思ったが、取り出す要素がなかったため終了
(本来の位置にいたはずだが、6,で"B"が削除されて”C"の位置がずれたため)

疑問点その2

7, listから"C"を取り出そうと思ったが、取り出す要素がなかったため終了した理由は、
「本来の位置にいたはずだが、6,で"B"が削除されて”C"の位置がずれたため」で合っていますでしょうか?

自分で試したこと

実際に自分でコードを打ってプログラム実行、インターネットで検索しましたがわかりませんでした。
上記の2点の疑問点を解決したいです。
お力添えをお願いします!!!

0 likes

2Answer

list.remove(new Item("A",500));でequalsメソッドを使用しているのはわかるのですが、なぜequalsメソッドが呼び出されるのかがわかりません。

これはArrayList.remove(Object)の仕様にはっきり書いてあります.equalsがオーバーライドされていれば当然そちらが呼ばれることになります.

つまり、(o==null ? get(i)==null : o.equals(get(i)))となる、最小のインデックス値iを持つ要素を削除します(そのような要素が存在する場合)。

https://docs.oracle.com/javase/jp/8/docs/api/java/util/ArrayList.html#remove-java.lang.Object-

7, listから"C"を取り出そうと思ったが、取り出す要素がなかったため終了した理由は、
「本来の位置にいたはずだが、6,で"B"が削除されて”C"の位置がずれたため」で合っていますでしょうか?

概ねその認識で大丈夫です.が,下記の事情があるためエッジケースとお考え下さい.問題の状況はlistの要素数が1つでも変化すると正しく動作しないコードとなります.

リストの要素でループしている最中に自身の要素を追加したり削除することはConcurrent Modificationと呼ばれ,常に忌避すべき行為とされています.以下のコードはlistの要素を増やしただけですが,例外を生じるはずです.

        ArrayList<String> list = new ArrayList<>();
		list.add("A");
		list.add("B");
		list.add("C");
		list.add("D");
		for (String str : list) {
			if("B".equals(str)){
				list.remove(str);
			}else{
            	System.out.println(str);
			}	
        }
// -> Exception in thread "main" java.util.ConcurrentModificationException

加えてArrayListはスレッドセーフでないため,ループの中身だけでなく別スレッドからの思わぬ変更がないように注意してください.

4Like

Comments

  1. @rion0726ittoti

    Questioner

    @Vercleneさん
    URL、サンプルコードの共有ありがとうございます!
    サンプルコードを実行したところ
    Exception in thread "main" java.util.ConcurrentModificationException
    ように例外がスローされました。
    公式のドキュメントを確認して学習をしていこうと思います!

疑問その1

提供されたJavaコードにおいて、list.remove(new Item("A", 500)); を使用すると、equalsメソッドが呼び出されます。なぜそうなるのかを理解しましょう。

Javaでは、ArrayListremoveメソッドは内部でequalsメソッドを使用してリストからどのオブジェクトを削除するかを判断します。equalsメソッドはオブジェクトの等価性を比較するために使用され、デフォルトではオブジェクトの参照を比較します。しかし、Itemクラスでequalsメソッドをオーバーライドして、オブジェクトをnameフィールドに基づいて比較するようにしています。

list.remove(new Item("A", 500));を呼び出すと、removeメソッドは渡したオブジェクトと等しいオブジェクトをリストから探します。これは、渡したオブジェクトとリスト内の各オブジェクトをequalsメソッドで比較し、渡したオブジェクトと等しいと判断された場合にtrueを返します。

この場合、Itemクラスのequalsメソッドはオブジェクトをnameフィールドに基づいて比較します。リスト内に名前が「A」という名前の2つのItemオブジェクトがあるため、渡したオブジェクト(名前が「A」)をリストのオブジェクト(名前が「A」)と比較した際にequalsメソッドがtrueを返し、removeメソッドはリスト内に一致するオブジェクトがあることを検出して削除します。

したがって、equalsメソッドは、渡したオブジェクトとリスト内のオブジェクトとの一致を判定するために呼び出されます。オブジェクトを参照ではなく、その内容に基づいて比較したい場合には、equalsメソッドをオーバーライドすることが重要です。

なお、equalsメソッドをオーバーライドする際には、HashMapHashSetなどのコレクションのキーとしてオブジェクトを使用する場合に適切な動作を保つために、hashCodeメソッドもオーバーライドする必要があります。

これにより、与えられたシナリオでなぜequalsメソッドが呼び出されるのかが理解できると思います。

0Like

Comments

  1. @rion0726ittoti

    Questioner

    @ORCHESTRA_TAPEさん
    回答ありがとうございます!
    「equalsメソッドをオーバーライドする際には、HashMapやHashSetなどのコレクションのキーとしてオブジェクトを使用する場合に適切な動作を保つために、hashCodeメソッドもオーバーライドする必要があります。」知りませんでした。
    参考にさせていただきます。

Your answer might help someone💌