集合Set
1.Set
常用方法:直接查看api,裏面的方法我們基本都是學過的
Set的遍歷 1:Iterator,2:foreach
2.HashSet(查詢api看說明進行講解)
2.1 元素順序:元素唯一,但是無序(它不保證 set 的迭代順序;特別是它不保證該順序恆久不變)
2.2如何保證元素的唯一性的呢(分析源碼)?
通過簡單的分析,我們知道HashSet集合保證元素的唯一性和add()方法相關。
如何我們想深入的瞭解,就必須看add()方法的源碼,看它的底層依賴什麼內容?
// if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {...}
//
// 左邊:e.hash == hash
// 比較對象的哈希值。
//
// 右邊:((k = e.key) == key || key.equals(k))
// 左邊:(k = e.key) == key
// 比較對象的地址值。
//
// 右邊:key.equals(k)
// 比較的是對象的內容是否相同。默認情況下比較的是地址值
結論:
底層數據結構是哈希表。
哈希表依賴兩個方法:hashCode()和equals()
執行流程:
首先判斷哈希值是否相同,如果不同,就直接添加到集合。
如果相同,繼續執行equals(),看其返回值,
如果是false,就直接添加到集合。
如果是true,說明元素重複不添加。
使用注意事項:如果你看到哈希結構的集合,就要考慮可能需要重寫這兩個方法。add()方法,保證唯一性的原理(底層依賴於hashCode()和equals()方法,保證了元素的唯一性)。
package com.edu.study7;
//需求:舉例說明元素唯一性
import java.util.HashSet;
public class HashSetDemo {
public static void main(String[] args) {
/**HashSet集合
* 元素唯一,但是無序
*
* 基本數據類型包裝類以及String類中已經重寫過hashCode()和equals()方法
* 所以自動保證了元素唯一性
*/
//創建HashSet集合
HashSet<String> hashSet = new HashSet<String>();
//給集合添加元素
hashSet.add("hello");
hashSet.add("world");
hashSet.add("java");
hashSet.add("hello");
hashSet.add("php");
hashSet.add("c");
hashSet.add("你好哦");
hashSet.add("heihei");
//遍歷
for (String s : hashSet) {
System.out.println(s);
}
}
}
//第一次運行結果: 第八次運行結果:
// hello hello
// heihei c
// c heihei
// php php
// 你好哦 你好哦
// java java
// world world
需求:自定義學生對象,保證 集合元素唯一性
package com.edu.study7;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@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) {
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;
}
}
package com.edu.study7;
//HashSet集合,自定義類中必須重寫hashCode()和equals()方法,纔可以保證元素唯一性
import java.util.HashSet;
import java.util.Iterator;
public class HashSetTest {
public static void main(String[] args) {
//創建HashSet集合
HashSet<Student> hashSet = new HashSet<Student>();
//創建學生對象
Student s1 = new Student("張三",13);
Student s2 = new Student("李四",13);
Student s3 = new Student("王五",13);
Student s4 = new Student("張三",13);
//給集合添加元素
hashSet.add(s1);
hashSet.add(s2);
hashSet.add(s3);
hashSet.add(s4);
//遍歷
Iterator<Student> it = hashSet.iterator();
while (it.hasNext()) {
Student s = it.next();
System.out.println(s.getName()+" "+s.getAge());
}
}
}
//不重寫輸出結果: 重寫:
// 李四 13 李四 13
// 張三 13 張三 13
// 王五 13 王五 13
// 張三 13
3.TreeSet(查看api,得出結論)
3.1 元素順序:使用元素的自然順序對元素進行排序,或者根據創建 set時提供的 Comparator進行排序(比較器排序),具體取決於使用的構造方法。
3.2 TreeSet集合的底層算法:二叉樹
package com.edu.study8;
/**
* 基本數據類包裝類以及String類都實現Comparable接口
* 且都重寫了compareTo()方法
*/
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
//創建TreeSet集合
/** TreeSet<String> treeSet = new TreeSet<String>();
treeSet.add("hello");
treeSet.add("world");
treeSet.add("c");
treeSet.add("java");
//遍歷
for (String string : treeSet) {
System.out.println(string);
}
//輸出:
// c
// hello
// java
// world
*/
TreeSet<Integer> ts = new TreeSet<Integer>();
ts.add(12);
ts.add(25);
ts.add(89);
ts.add(3);
//遍歷
for (Integer i : ts) {
System.out.println(i);
}
//輸出結果:
// 3
// 12
// 25
// 89
}
}
3.3 元素要求, 加入自定義JavaBean
需求:存入學生對象(姓名,年齡),1.按年齡排序,2.姓名排序(自然排序實現Comparable接口,並重寫comparaTo()方法)
package com.edu_04;
//新建Student類
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
//實現Comparable接口的同時必須實現這個比較法
@Override
public int compareTo(Student s) {
//就是寫的是元素的比較規則,由你自己去動手寫出
//按照學生的年齡進行排序
/**
* 兩個對象進行比較:
* s
* this
*/
int num = this.age - s.age;
//判斷年齡是否相同,如果相同比較姓名
/**
* 寫這個比較規則的時候注意兩點:
* 1.他有主要條件,先按照主要條件進行排序
* 2.如果主要條件相同,就需要你自己分析出來他的次要條件,再去按照次要條件進行比較
*/
int num2 = num==0?this.name.compareTo(s.name):num;
return num2;
}
}
package com.edu_04;
import java.util.TreeSet;
/**
* java.lang.ClassCastException:類型轉換異常
* TreeSet集合有兩種排序方式:至於哪兩種排序方式,取決於他的構造器
* 自然排序:無參構造public TreeSet()
*/
public class TreeSetDemo {
public static void main(String[] args) {
//創建TreeSet集合,存儲自定義對象
TreeSet<Student> ts = new TreeSet<Student>();
//給集合中添加Student對象
Student s = new Student("guodegang", 50);
Student s6 = new Student("liuyifei", 50);
Student s2 = new Student("zhangxueyou", 55);
Student s3 = new Student("amu", 45);
Student s4 = new Student("tf-boys", 18);
Student s5 = new Student("wangfeng", 49);
ts.add(s);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
//遍歷集合
for (Student student : ts) {
System.out.println(student);
}
}
}
需求:創建set集合的時候,傳入Comparator(比較器排序)進行排序,進行排序(比較器排序)
package com.edu_05;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
//使用匿名內部類
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
int num2 = num==0?s1.getName().compareTo(s2.getName()):num;
return num2;
}
});
//創建對象存入集合
Student s = new Student("guodegang", 50);
Student s6 = new Student("liuyifei", 50);
Student s2 = new Student("zhangxueyou", 55);
Student s3 = new Student("amu", 45);
Student s4 = new Student("tf-boys", 18);
Student s5 = new Student("wangfeng", 49);
ts.add(s);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
//遍歷集合
for (Student student : ts) {
System.out.println(student);
}
}
}
4. HashSet與TreeSet的相同點與不同點
相同點:
單列集合,元素不可重複
不同點
1. 底層存儲的數據結構不同
HashSet底層用的是HashMap哈希表結構存儲,而TreeSet底層用的是TreeMap樹結構存儲
2.存儲時保證數據唯一性依據不同
HashSet是通過複寫hashCode()方法和equals()方法來保證的,而TreeSet通過Compareable接口的compareTo()方法來保證的
3.有序性不一樣
HashSet無序,TreeSet有序
集合Map
一 一對應的映射關係。這種關係的集合在java叫Map。
Map:將鍵映射到值的對象。一個映射不能包含重複的鍵;每個鍵最多隻能映射到一個值。
1. Map接口中的方法:
A:刪除功能
void clear():移除集合中的所有鍵值對元素
V remove(Object key):根據鍵移除鍵值對元素,並返回值
B:判斷功能
boolean containsKey(Object key):判斷集合中是否包含指定的鍵
boolean containsValue(Object value):判斷集合中是否包含指定的值
boolean isEmpty():判斷集合是否爲空
C:獲取功能
Set<Map.Entry<K,V>> entrySet():獲取鍵值對對象的集合,遍歷鍵值對對象,利用getKey(),getValue()取出鍵和值
V get(Object key):根據鍵獲取值
Set<k> keySet():獲取所有的鍵
Collection<v> values():獲取所有的值
D:添加功能
V put(K key,V value):集合添加鍵值對
E:長度功能
int size():鍵值對對數。
2. HashMap
2.1元素順序:元素順序不可預測
2.2底層算法:哈希算法
2.3對鍵沒有要求(僅僅相對於TreeMap來說)
package com.edu.study12;
//練習:存入(String,String)進行練習
import java.util.HashMap;
import java.util.Set;
public class HashMapDemo {
public static void main(String[] args) {
//創建HashMap集合
HashMap<String, String> hashMap = new HashMap<String, String>();
//給集合添加元素
hashMap.put("A", "a");
hashMap.put("B", "b");
hashMap.put("C", "c");
hashMap.put("D", "d");
hashMap.put("A", "e");
//當後面存入的元素和前面的鍵的值相同的時候,前面的元素的值會被後面的元素的值代替
Set<String> keySet = hashMap.keySet();
for (String key : keySet) {
System.out.println(key+" "+hashMap.get(key));
}
}
}
// D d
// A e
// B b
// C c
練習 :
package com.edu.study13;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
package com.edu.study13;
//練習:存入(String,Student)
import java.util.HashMap;
import java.util.Set;
public class HashMapDemo {
public static void main(String[] args) {
//創建HashMap集合
HashMap<String, Student> hashMap = new HashMap<String, Student>();
//創建Student對象
Student s1 = new Student("zhang",6);
Student s2 = new Student("li",25);
Student s3 = new Student("sun",17);
Student s4 = new Student("zhang",6);
//給集合中添加元素
hashMap.put("中國", s1);
hashMap.put("韓國", s2);
hashMap.put("美國", s3);
hashMap.put("日本", s4);
//當後面存入的元素和前面的鍵的值相同的時候,前面的元素的值會被後面的元素的值代替
//通過鍵找值
Set<String> keySet = hashMap.keySet();
//得到的Set本質上是HashSet,元素唯一,無序
//而鍵爲String類型,已經重寫了hashCode()和equals()方法,故保證了唯一性
for (String key : keySet) {//因爲在Student類中重寫toString()方法
System.out.println(key+" "+hashMap.get(key));
}
}
}
//日本 Student [name=zhang, age=6]
//韓國 Student [name=li, age=25]
//美國 Student [name=sun, age=17]
//中國 Student [name=zhang, age=6]
練習:
package com.edu.study14;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@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) {
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;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
package com.edu.study14;
//傳入(Student, String),進行練習
import java.util.HashMap;
import java.util.Set;
public class HashMapDemo {
public static void main(String[] args) {
//創建HashMap集合
HashMap<Student, String> hashMap = new HashMap<Student, String>();
//創建Student對象
Student s1 = new Student("zhang",6);
Student s2 = new Student("liu",13);
Student s3 = new Student("sun",20);
Student s4 = new Student("zhang",6);
//給集合添加元素
hashMap.put(s1, "美國");
hashMap.put(s2, "中國");
hashMap.put(s3, "韓國");
hashMap.put(s4, "中國");
//遍歷
Set<Student> keySet = hashMap.keySet();
//此時的Set實質上是HashSet,元素唯一,無序
//而鍵是Student類對象,應重寫hashCode()和equals()方法,方可保證唯一性
//在Student類中重寫hashCode()和equals()方法
for (Student key : keySet) {
//在Student類中重寫toString()方法
System.out.println(key+" "+hashMap.get(key));
}
}
}
// Student [name=liu, age=13] 中國
// Student [name=sun, age=20] 韓國
// Student [name=zhang, age=6] 中國
3. Treemap
3.1元素順序:元素順序與鍵的排序規則有關
3.2底層算法:Tree算法
4. 遍歷
集合遍歷:①獲取建的集合 ②遍歷鍵 ③根據鍵找值foreach()
1.foreach():根據丈夫找妻子(根據鍵找值)
2.entrySet():(Set<Map.Entry<K,V>> entrySet())先找到夫妻的結婚證,再從結婚證裏面找到丈夫和妻子(先找到鍵值對對象,再從鍵值對對象裏面找到鍵和值)
練習:存入(Integer,String)
package com.edu_11;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapDemo {
public static void main(String[] args) {
//1.存入(Integer,String),主要驗證排序方式
//c創建TreeMap集合
TreeMap<Integer, String> tm = new TreeMap<Integer, String>();
//存入元素
tm.put(1, "趙");
tm.put(4, "劉");
tm.put(2, "坤");
tm.put(3, "李");
//遍歷集合
Set<Integer> keys = tm.keySet();
for (Integer key : keys) {
System.out.println(key+" "+tm.get(key));
}
}
}
/** 1 趙
2 坤
3 李
4 劉
*/
練習:存入(Student,String)
新建學生類
package com.edu_12;
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Student s) {
//先按年齡排序,年齡相同按照姓名排序
//int num = this.age - s.age;
int num =s.age - this.age;
int num2 = num==0?this.name.compareTo(s.name):num;
return num2;
}
}
測試
package com.edu_12;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapDemo {
public static void main(String[] args) {
// 2.存入(Student,String),鍵:Student 值:地址
//創建TreeMap集合
TreeMap<Student, String> tm = new TreeMap<>();
//創建學生對相同
Student s1 = new Student("郭德綱", 50);
Student s2 = new Student("劉德華", 55);
Student s3 = new Student("張學友", 58);
Student s4 = new Student("黎明", 40);
Student s5 = new Student("郭德綱", 50);
//將元素存入集合
tm.put(s1, "中國");
tm.put(s2, "中國");
tm.put(s3, "中國");
tm.put(s4, "中國");
tm.put(s5, "中國");
//遍歷集合
Set<Student> keys = tm.keySet();
for (Student s : keys) {
System.out.println(s+" "+tm.get(s));
}
}
}
/** Student [name=張學友, age=58] 中國
Student [name=劉德華, age=55] 中國
Student [name=郭德綱, age=50] 中國
Student [name=黎明, age=40] 中國
*/
5. HashMap與TreeMap的相同點與不同點
相同點:
主要用於存儲鍵(key)值(value)對,根據鍵得到值,因此鍵不允許鍵重複,但允許值重複。
不同點:
1.HashMap裏面存入的鍵值對在取出的時候是隨機的,也是我們最常用的一個Map.根據鍵可以直接獲取它的值,具有很快的訪問速度。在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。
2.TreeMap取出來的是排序後的鍵值對。但如果您要按自然順序或自定義順序遍歷鍵,那麼TreeMap會更好。