java Set 類,hashSet類, hashCode方法
集合中,Set裡面儲存的元素不能重複,沒有索引,存取順序不一致。
Set的繼承關係圖如下
Set介面繼承了Collection介面,
HashSet存放無序,不重複的元素常用方法
boolean add(E e) 新增一個元素,返回一個布林值,如果新增的元素在Set中存在,則新增不了,返回false
add的實現是先呼叫物件的hashCode方法,如果返回的hashCode值相同,再呼叫equals方法,判斷新增的元素是否在Set中存在,存在則不新增,不存在則新增.如果返回的hashCode值不同,就直接將元素新增到Set中,所以新增的元素要重寫hashCode()和equals()方法
int size() 返回Set的元素個數
void clear() 清空Set中所有元素
boolean contains(Object o) 返回Set中是否包含了特定元素
boolean isEmpty() 檢查Set中是否沒有元素,沒有就返回true,有就返回false
Iterator<E> iterator() 生成Set的迭代器物件
boolean remove(Object o) 在Set中刪除特定元素,如果此元素在Set中返回true,如果Set中沒有此元素返回false
示例:
import java.util.HashSet; import java.util.Set; public class HashSetTest01 { public static void main(String[] args) { Set<String> set = new HashSet<>(); boolean b1 = set.add("a"); boolean b2 = set.add("a"); System.out.println(b1);// true System.out.println(b2);// false set.add("c"); set.add("f"); set.add("b"); for (String s : set) { System.out.print(s + " "); } //列印結果為a b c f 可以看到跟Set中的元素跟存放順序無關 } } out: true false a b c f
hashCode()方法
hashCode方法可以配合基於雜湊的集合,這樣的雜湊集合一般使用在hash容器中包括HashSet、HashMap以及HashTable.
HashCode方法的作用就是將物件均勻雜湊分類,然後獲取到編號值,
類似於下圖,將相同或者相似物件歸為一類(hash code相同),再用equals方法判斷是否相同
equals方法和hashCode方法 的關係
在Map和Set這種儲存不重複元素的容器中,equals方法本來是可以比較出兩個物件是否相同,但是如果物件的個數太多的話,會影響效能,所以hashCode()可以先來一個預先判斷,將物件hashCode方法返回的hash code儲存在hash表中,下個物件hashCode方法返回的hash code如果在hash表中能找到,進行equals比較,如果hash表不存在此hash code, 說明這個物件沒有跟以前的重複,不需要進行equals比較
為什麼有了hashCode()方法還要equals方法,是因為hashCode()方法並不是完全可靠,不同物件的hash code值可能相同
重寫hashCode()方法的必要性 Object類中的hashCode()方法只是返回當前物件的地址,相同的一個類,new建立兩個物件,由於他們在記憶體裡的地址不同,則他們的hashCode()不同,這顯然達不到我們想要hsahCode()預判斷的效果,所以要重寫hashCode()
針對Set,Map這種型別的容器,重寫equals()方法時也要重寫hashCode()方法
重寫hashCode方法時要注意:
- 相等的物件(equals比較為true)必須具有相等的雜湊碼(hash code)。
- 不等的物件(equals比較為false),雜湊碼(hash code)值可以相等也可以不相等.
- 任何時候對同一物件多次呼叫 hashCode 方法,都必須一直返回同樣的整數
示例:
package students;
public class Student {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student(int age, String name) {
super();
this.age = age;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
//eclipse自動生成的hashCode方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
System.out.println("equals");
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
//測試類
import java.util.HashSet;
import java.util.Set;
import students.Student;
public class HashSetTest02 {
public static void main(String[] args) {
Set<Student> set = new HashSet<>();
set.add(new Student(12, "張三"));
set.add(new Student(13, "李四"));
set.add(new Student(14, "趙五"));
set.add(new Student(12, "張三"));
set.add(new Student(13, "李四"));
System.out.println(set); // 如果物件類沒有重寫hashCode,Set不會去重,會把所有元素新增到Set中
// 物件類重寫hashCode方法後,Set就會去重
}
}
out:
equals
equals
[Student [age=14, name=趙五], Student [age=12, name=張三], Student [age=13, name=李四]]
當自定義的類只有int age和String name這兩個屬性時,eclipse自動生成的hashCode方法:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
為什麼eclipse自動重寫的hashCode方法中的prime為31?
其實這個值改成別的也可以,只不過定義為31之後有一些好處:
- 31是一個質數,質數是能被1和自己本身整除的數,並且這個數不大也不小
- 31這個數好算,2的五次方-1,2向左移動5位