Setに重複するオブジェクトが存在しない理由を理解する
最近あちこちで筆記試験に参加しても、これらの問題をよく研究していませんが、私は多くの筆記試験の中で、Stringが本当に悪いことに気づいて、それから==equals()の使い方で、ほとんど何度も試験しても気分が悪いです.昨日はどうやってequals()を手に入れたのか、equals()メソッドはObjectの中の1つのメソッドで、中で実現するのは簡単です.
public boolean equals(Object obj) {
return (this == obj);
}
実際には、2つのオブジェクトの参照が同じかどうか(すなわち、オブジェクトのアドレス値が同じかどうか)を比較していますが、String、Integerなどの参照データのタイプには、Stringのequals()メソッドのようなObjectのequals()メソッドが書き換えられています.
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
1番目のif比較参照が同じかどうか,2番目のifは比較文字列の値がすべて等しいかどうかであり,これらの書き換え後のequals()メソッドは実際にオブジェクトの内容が同じかどうかを比較していると言える.実はSetがどのように実現したのかを考えることもできます.それはあなたがaddのオブジェクトを持っている間に、equalsメソッドを呼び出して、中に同じオブジェクトがあるかどうかを見たに違いありません.次に例を示します:Studentクラス
public class Student {
private int age;
private String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return age + " " + name;
}
}
テスト:
public static void main(String args[]) {
Set<Student> set = new HashSet<Student>();
set.add(new Student(1, "czc"));
set.add(new Student(2, "yqq"));
set.add(new Student(3, "lz"));
set.add(new Student(1, "czc"));
Iterator<Student> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
出力結果:2 yqq 1 czc 3 lz 1 czcはなぜ同じオブジェクトが2つあるのですか?HashSetのaddメソッドを見ると、オブジェクトをHashMapの中に配置していることがわかります.HashMapの中のputメソッドは次のように実現されています.
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))){
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
赤い行から、同じオブジェクトかどうかを判断し、hash値、すなわちオブジェクトのhashCode値も使用されていることがわかります.hashCode()メソッドはObjectの中の1つのローカル(native、実装は見えない、C/C++で実装すべきで、具体的には分からない)メソッドで、String、IntegerなどのクラスはhashCodeメソッドを書き換えて、異なるクラスの実装はすべて異なるアルゴリズムを使って、やはりIntegerが最も簡単で、直接入力したその数です.2つの同じオブジェクトを出力する理由がわかるでしょう.Studentは自分で書いたクラスなので、Objectの中のequals()メソッドを使っています.Objectの中のメソッドの実現を知っています.実は比較引用が同じかどうか、明らかにnewが出てきたのは2つの異なるものなので、Setは2つの異なるオブジェクトだと思って、すべて入れました.同じ文字列のオブジェクトを2つ定義してSetに入れ、印刷時に1つのオブジェクトしか出力しないのは、StringがhashCode()とequals()メソッドを書き換えているため、さっきの問題を解決するのは簡単です.私たちはStudentでhashCode()とequals()を書き換えるだけでいいのです.Studioに次のコードを追加します.
/**
* hashcode
*
* @return
*/
public int hashCode() {
return age * name.hashCode();
}
/**
* equals
*/
public boolean equals(Object obj) {
Student s = (Student) obj;
return age == s.age && name.equals(s.name);
}
最後のテスト結果は:2 yqq 3 lz 1 czc最後にequals()とオブジェクトのhash値の関係をまとめた:a.equals()が等しい2つのオブジェクト、hashcode()は必ず等しい;b.equals()メソッドが等しくない2つのオブジェクトは、hashcode()が等しい可能性があります.例えば、Integer s 1=new Integer(97); String s2 = new String("a"); これらは等しくありませんが、hashcodeの値は97 cです.逆に、hashcode()は等しくなく、equals()を出すことができるに違いありません.hashcode()は等しくて、equals()は一定で等しくなくて、上述のような例は原理的なものに対して、私も多くの分からないところがあって、文章の書いたのはすべて個人の見解で、どうしても理解のずれがあることができて、達人が教えを惜しまないことを望みます.