1.Set接口
Set接口是通過元素的equals方法顷啼,來判斷是否為重復(fù)元素。
Set接口常用的實現(xiàn)類有:
- HashSet類
- LinkedHashSet類
- TreeSet類
Set接口的特點
- 唯一
- 無序
1.1HashSet類
1.1.1介紹
- HashSet類實現(xiàn)了Set接口
- 存儲到HashSet中的對象必須是唯一的嫡秕,不能重復(fù)
- 對象唯一性的判斷依賴于hashCode()與equals()方法
- HashSet集合不能保證的迭代順序與元素存儲順序相同
1.1.2常用方法
Modifier and Type | Method and Description |
---|---|
boolean |
add(E e) 將指定的元素添加到此集合(如果尚未存在)。 |
void |
clear() 從此集合中刪除所有元素苹威。 |
boolean |
contains(Object o) 如果此集合包含指定的元素昆咽,則返回 true 。 |
boolean |
isEmpty() 如果此集合不包含元素牙甫,則返回 true 掷酗。 |
Iterator<E> |
iterator() 返回此集合中元素的迭代器。 |
boolean |
remove(Object o) 如果存在窟哺,則從該集合中刪除指定的元素泻轰。 |
int |
size() 返回此集合中的元素數(shù)(其基數(shù))。 |
1.1.3代碼演示
public static void main(String[] args) {
//1:創(chuàng)建集合且轨,向集合中添加5個對象
HashSet<String> set = new HashSet<>();
set.add("林沖");
set.add("李逵");
set.add("燕青");
set.add("武松");//重復(fù)的
set.add("武松");//重復(fù)的
//2:迭代集合 Set集合沒有g(shù)et()方法糕殉,獲取元素只能通過迭代
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
運行結(jié)果
燕青
武松
林沖
李逵
集合中只存入了四個元素亩鬼,重復(fù)的元素武松
只存入了一個殖告。
另外也發(fā)現(xiàn)HashSet對元素輸出的順序與輸入的順序不一致阿蝶。
1.1.4底層數(shù)據(jù)結(jié)構(gòu)
HashSet底層是使用哈希表存儲數(shù)據(jù)的,什么是哈希表呢黄绩?
- 哈希表(Hashtable)羡洁,也叫散列表,是通過key訪問value的一種數(shù)據(jù)結(jié)構(gòu)爽丹,與數(shù)組不同的是數(shù)組通過index訪問value筑煮。
- 哈希表最大的優(yōu)勢在于查找速度快≡列快速查找的原理是key通過散列函數(shù)(也叫哈希函數(shù))能夠快速定位value存儲的位置真仲。
- 哈希表底層是數(shù)組+鏈表的數(shù)據(jù)結(jié)構(gòu),哈希表數(shù)據(jù)結(jié)構(gòu)如下圖所示:
哈希表中數(shù)組元素不存儲添加到哈希表中的元素初澎,數(shù)組元素存放的是鏈表秸应。
添加到哈希表中的元素存儲在鏈表的節(jié)點上。
當(dāng)向哈希表中存放元素時碑宴,需要根據(jù)元素的特有的數(shù)據(jù)結(jié)合相應(yīng)的算法软啼,計算出元素應(yīng)該放到哪個數(shù)組元素的鏈表上。
這個算法其實就是Object類中的hashCode方法延柠。由于任何對象都是Object類的子類祸挪,所以任何對象有擁有這個方法。也就是在給哈希表中存放對象時贞间,會調(diào)用對象的hashCode方法贿条,算出對象在表中的存放位置,這里需要注意增热,如果兩個對象hashCode方法算出結(jié)果一樣整以,這樣現(xiàn)象稱為哈希沖突,這時會調(diào)用對象的equals方法钓葫,比較這兩個對象是不是同一個對象悄蕾,如果equals方法返回的是true,那么就不會把第二個對象存放在哈希表中础浮,如果返回的是false帆调,就會把這個值存放在哈希表中。
總結(jié):保證HashSet集合元素的唯一豆同,其實就是根據(jù)對象的hashCode和equals方法來決定的番刊。如果我們往集合中存放自定義的對象,那么保證其唯一影锈,就必須復(fù)寫hashCode和equals方法建立屬于當(dāng)前對象的比較方式芹务。
1.1.5代碼演示:HashSet中存儲自定義類型的元素
class Student{
private String name;
private int math;
private int cn;
public Student(String name, int math, int cn) {
super();
this.name = name;
this.math = math;
this.cn = cn;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", math=" + math +
", cn=" + cn +
'}';
}
//省略 get/set方法
}
public class Demo {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
Student s1 = new Student("林沖", 90, 90);
Student s2 = new Student("李逵", 92, 60);
Student s3 = new Student("燕青", 71, 70);
Student s4 = new Student("時遷", 80, 80);//重復(fù)對象
Student s5 = new Student("時遷", 80, 80);//重復(fù)對象
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
Iterator<Student> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("s4 哈希碼="+s4.hashCode());
System.out.println("s5 哈希碼="+s5.hashCode());
}
}
運行結(jié)果
Student{name='燕青', math=71, cn=70}
Student{name='時遷', math=80, cn=80}
Student{name='李逵', math=92, cn=60}
Student{name='時遷', math=80, cn=80}
Student{name='林沖', math=90, cn=90}
s4 哈希碼=325040804
s5 哈希碼=1173230247
2.Map接口
2.1.1介紹
Map接口下的集合與Collection接口下的集合蝉绷,它們存儲數(shù)據(jù)的形式不同,如下圖枣抱。
Collection中的集合熔吗,元素是孤立存在的(理解為單身),向集合中存儲元素采用一個個元素的方式存儲佳晶。
Map中的集合桅狠,元素是成對存在的(理解為夫妻)。每個元素由鍵與值(key - value)兩部分組成轿秧,通過鍵可以找對所對應(yīng)的值中跌。
Collection中的集合稱為單列集合,Map中的集合稱為雙列集合菇篡。
需要注意的是漩符,Map中的集合不能包含重復(fù)的鍵,值可以重復(fù)驱还;每個鍵只能對應(yīng)一個值嗜暴。
Map中常用的集合為
HashMap
集合、LinkedHashMap
集合铝侵。
Map接口有多個子類灼伤,這里我們主要講解常用的HashMap集合、LinkedHashMap集合咪鲜、Properties集合狐赡。HashMap<K,V>:存儲數(shù)據(jù)采用的哈希表結(jié)構(gòu),元素的存取順序不能保證一致疟丙。由于要保證鍵的唯一颖侄、不重復(fù),需要重寫鍵的hashCode()方法享郊、equals()方法览祖。
LinkedHashMap<K,V>:HashMap下有個子類LinkedHashMap,存儲數(shù)據(jù)采用的哈希表結(jié)構(gòu)+鏈表結(jié)構(gòu)炊琉。通過鏈表結(jié)構(gòu)可以保證元素的存取順序一致展蒂;通過哈希表結(jié)構(gòu)可以保證的鍵的唯一、不重復(fù)苔咪,需要重寫鍵的hashCode()方法锰悼、equals()方法。
注意:Map接口中的集合都有兩個泛型變量<K,V>,在使用時团赏,要為兩個泛型變量賦予數(shù)據(jù)類型箕般。
2.1.2常用方法
Modifier and Type | Method and Description |
---|---|
void |
clear() 從這張地圖中刪除所有的映射。 |
Set<Map.Entry<K,V>> |
entrySet() 返回此地圖中包含的映射的[Set ]視圖舔清。 |
V |
get(Object key) 返回到指定鍵所映射的值丝里,或 null 如果此映射包含該鍵的映射曲初。 |
boolean |
isEmpty() 如果此地圖不包含鍵值映射,則返回 true 杯聚。 |
Set<K> |
keySet() 返回此地圖中包含的鍵的[Set ]視圖臼婆。 |
V |
put(K key, V value) 將指定的值與此映射中的指定鍵相關(guān)聯(lián)。 |
V |
remove(Object key) 從該地圖中刪除指定鍵的映射(如果存在)械媒。 |
int |
size() 返回此地圖中鍵值映射的數(shù)量目锭。 |
Collection<V> |
values() 返回此地圖中包含的值的[Collection ]視圖。 |
- put方法:將指定的鍵與值對應(yīng)起來纷捞,并添加到集合中
- 方法返回值為鍵所對應(yīng)的值
- 使用put方法時,若指定的鍵(key)在鍵集中不存在被去,那么對應(yīng)的值也不存在主儡,返回null,并把指定的鍵值添加到集合中
- 使用put方法時惨缆,若指定的鍵(key)在鍵集中存在糜值,則返回值為集合中鍵對應(yīng)的值(該值為替換前的值),并把指定鍵所對應(yīng)的值坯墨,替換成指定的新值寂汇。
- 方法返回值為鍵所對應(yīng)的值
- get方法:獲取指定鍵(key)所對應(yīng)的值(value)
- remove方法:根據(jù)指定的鍵(key)刪除元素,返回被刪除元素的值(value)
2.1.3Map集合的遍歷
- Map集合遍歷方式是鍵找值:即通過元素中的鍵捣染,獲取鍵所對應(yīng)的值骄瓣。
- Map集合的鍵集是一個Set集合
- 遍歷Set集合,分別取出key
- 通過key找到value
public static void main(String[] args) {
Map<String, String> map = new HashMap<String,String>();
map.put("豹子頭", "林沖");
map.put("黑旋風(fēng)", "李逵");
map.put("浪子","燕青");
map.put("行者","武松");
Set<String> sets = map.keySet();
Iterator<String> iterator = sets.iterator();
while (iterator.hasNext()){
String key = iterator.next();
String value = map.get(key);
System.out.println("key="+key +",value="+value);
}
}
運行結(jié)果
key=豹子頭,value=林沖
key=行者,value=武松
key=黑旋風(fēng),value=李逵
key=浪子,value=燕青
2.1.4Entry鍵值對對象
Map集合除了通過keySet()迭代集合外耍攘,還可以通過entrySet()迭代集合榕栏。
在Map類設(shè)計時,提供了一個嵌套接口:Entry蕾各。Entry將鍵值對的對應(yīng)關(guān)系封裝成了對象扒磁。即鍵值對對象,這樣我們在遍歷Map集合時式曲,就可以從每一個鍵值對(Entry)對象中獲取對應(yīng)的鍵與對應(yīng)的值妨托。
entrySet()
方法:用于返回Map集合中所有的鍵值對(Entry)對象,以Set集合形式返回吝羞。
遍歷set集合得到 Entry<String , String>
對象
Entry<String , String>
對象調(diào)用 getKey()
得到 key
Entry<String , String>
對象調(diào)用 getValue()
得到 value
public static void main(String[] args) {
Map<String, String> map = new HashMap<String,String>();
map.put("豹子頭", "林沖");
map.put("黑旋風(fēng)", "李逵");
map.put("浪子","燕青");
map.put("行者","武松");
Set<Map.Entry<String, String>> entrySet = map.entrySet();
Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
while (iterator.hasNext()){
Map.Entry<String, String> entry = iterator.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println("key="+key +",value="+value);
}
}
運行結(jié)果
key=豹子頭,value=林沖
key=行者,value=武松
key=黑旋風(fēng),value=李逵
key=浪子,value=燕青
2.2.1HashMap類
需求:學(xué)生有姓名兰伤、年齡的屬性,學(xué)生都有自己的家庭住址脆贵。將學(xué)生對象和家庭住址存儲到map集合中医清。學(xué)生作為鍵, 家庭住址作為值。
注意卖氨,學(xué)生姓名相同并且年齡相同視為同一名學(xué)生会烙。
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//重寫equals方法
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if(!(o instanceof Student)){
System.out.println("類型錯誤");
return false;
}
Student other = (Student) o;
return this.age == other.age && this.name.equals(other.name);
}
//重寫hashCode方法
@Override
public int hashCode() {
final int base = 31;
int result = 1;
result = base * result + age;
result = base * result + ((name == null) ? 0 : name.hashCode());
return result;
}
//重寫 toString 方法
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
//省略了 get/set 方法
}
/**
* HashMap集合的用法
*/
public class Demo {
public static void main(String[] args) {
//1:創(chuàng)建Map集合對象
Map<Student, String> map = new HashMap<Student, String>();
//2:創(chuàng)建學(xué)生對象
Student s1 = new Student("林沖", 30);
Student s2 = new Student("李逵", 32);
Student s3 = new Student("燕青", 22);
Student s4 = new Student("武松", 29);//重復(fù)對象
Student s5 = new Student("武松", 29);//重復(fù)對象
//3:添加到map集合中 學(xué)生對象作為key负懦,家庭地址作為value
map.put(s1, "河南開封");
map.put(s2, "山東沂水");
map.put(s3, "北京大名府");
map.put(s4, "河北清河");
map.put(s5, "遼寧鐵嶺");
//3:取出元素,鍵值對方式
Set<Map.Entry<Student, String>> entrySet = map.entrySet();
Iterator<Map.Entry<Student, String>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
Map.Entry<Student, String> entry = iterator.next();
Student key = entry.getKey();
String value = entry.getValue();
System.out.println("key=" + key + ",value=" + value);
}
System.out.println("-------------------- 分界線 ----------------------");
//3:取出元素柏腻,鍵找值方式
Set<Student> sets = map.keySet();
Iterator<Student> iterator1 = sets.iterator();
while (iterator1.hasNext()) {
Student key = iterator1.next();
String value = map.get(key);
System.out.println("key=" + key + ",value=" + value);
}
}
}
運行結(jié)果
key=Student{name='武松', age='29'},value=遼寧鐵嶺
key=Student{name='林沖', age='30'},value=河南開封
key=Student{name='李逵', age='32'},value=山東沂水
key=Student{name='燕青', age='22'},value=北京大名府
-------------------- 分界線 ----------------------
key=Student{name='武松', age='29'},value=遼寧鐵嶺
key=Student{name='林沖', age='30'},value=河南開封
key=Student{name='李逵', age='32'},value=山東沂水
key=Student{name='燕青', age='22'},value=北京大名府
2.2.2底層數(shù)據(jù)結(jié)構(gòu)
HashMap底層數(shù)據(jù)結(jié)構(gòu)是( 數(shù)組 + 鏈表 | 紅黑樹)
默認情況下纸厉,當(dāng)節(jié)點數(shù)量不超過8個時,使用鏈表五嫂,當(dāng)節(jié)點數(shù)量超過8個時使用紅黑樹颗品。這樣設(shè)計是為了提高查詢效率,因為當(dāng)節(jié)點數(shù)量多時沃缘,紅黑樹的查詢效率高于鏈表躯枢。