集合心得總結
- 2021 年 12 月 31 日
- 筆記
為什麼需要集合? 數組有不足:擴容麻煩 保存元素類型固定,同一數組無法改變 ——> 引出集合的概念
集合體系
集合分為兩大類 一類是Collection(一組對象) 一類是Map(一組鍵值對)
底層維護實現關係
ArrayList/Vector實現Collection的List介面 底層是可變類型數組Object[]
LinkedList實現Collection的List介面 底層是雙向鏈表
TreeSet實現了Collection的Set介面 底層是TreeMap
HashSet實現了Collection的Set介面 底層是HashMap
LinkedHashSet實現了Map介面 底層是LinkedHashMap
Hashtable實現了Map介面 底層Entry數組
HashMap實現了Map介面 底層是Node類型數組table 在JDK7中 實現形式是數組+鏈表 在JDK8以後 實現形式是數組+鏈表+紅黑樹
LinkedHashMap實現了Map介面 底層是數組+雙向鏈表
選擇
1.判斷存儲的類型
一組對象/一組鍵值對
2.一組對象【單列】
Collection介面
允許重複:List
增刪多:LinkedList【雙向鏈表】
改查多:ArrayList/Vector【可變類型Object數組】
不允許重複:Set 底層都是Map介面的實現類 而Map的Key不允許重複
無序:HashSet
排序:TreeSet
插入和取出順序一致: LinkedHashSet
3.一組鍵值對
Map介面 Key不允許重複 可以為空 Values可以重複 可以為null(多個) 一對K-V —> 放在HashMap$Node Node實現了Entry介面 可以說一對K-V是一個Entry
1. k-v 最後是HashMap$Node
2. k-v為了方便程式設計師的遍歷 還會創建 EntrySet 集合 該集合存放的元素的類型 Entry
而一個Entry對象 就有k,v EntrySet<Entry<k,v>> 即 transicent Set<Map.Entry<k,v>> entrySet;
3. entrySet 中 定義的類型是 Map.Entry 但是實際上存放的還是 HashMap$Node
這是因為 static class Node<K,V> implements Map.Entry<K,V>
4. 當把HashMap$Node 對象 存放到 entrySet 就方便我們的遍歷
K getKey(); V getValue();
鍵無序:HashMap
鍵排序:TreeMap
鍵插入和取出一致:LinkedHashMap
讀取文件:Properties
剖析類
Vector 安全 效率低---ArrayList 不安全 效率高 !!!擴容機制
ArrayList擴容完整過程
TreeSet–TreeMap 強調訂製排序(匿名內部類)
HashSet–HashMap執行緒不安全 !!!擴容機制
重寫hashCode equals 來控制當什麼條件相同時返回相同的hashCode 就不能添加元素
LinkedHashSet
加入順序和取出元素/數據的順序一致
LinkedHashSet 底層維護的是一個LinkedHashMap(是HashMap的子類)
LinkedHashSet 底層結構 (數組 + 雙向鏈表)
添加第一次時 直接將 數組table 擴容到16 存放的結點類型 LinkedHashMap\(Entry
數組是 HashMap\)Node[] 存放的元素/數據是 dHashMap$EntrLinkey類型
繼承關係是在內部類完成
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
HashTable
Hashtable底層
底層有一個數組 Hashtable$Entry[] 初始化大小為11 臨界值 threshold 8 = 11 * 0.75 擴容 按照自己的擴容機制來進行即可 執行 方法 addEntry(hash, key, value, index); 添加 k-v 裝到Entry 當 if (count >= threshold) 滿足時 進行擴容 按照 int newCapacity = (oldCapacity << 1) + 1;
方法
Collection:
Collections工具類方法
List/Set方法
add
remove
contains
size
isEmpty
addAll
containsAll
removeAll
get List介面有 Set介面無
Map:
put
remove
get
size
isEmpty
clear
containsKey
遍歷
Collection:
Iterator
增強for
普通for
Set介面實現類沒有get方法 不能通過普通for遍歷
Map
Collections類
集合關係密切—–> 泛型配合使用
傳統方法 不能對加入集合的元素進行約束 可能存在安全隱患
遍歷的時候 需要類型轉換 當數據量比較大的時候 會影響效率

經典題目
點擊查看程式碼
public class Main {
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
Employee em1 = new Employee("zhc", 20000, new MyDate(2002, 7, 9));
Employee em2 = new Employee("myh", 10000, new MyDate(2000, 4, 2));
Employee em3 = new Employee("zhc", 5000, new MyDate(2001, 2, 5));
employees.add(em2);
employees.add(em1);
employees.add(em3);
System.out.println("排序前");
System.out.println(employees);
employees.sort(new Comparator<Employee>() {
@Override
public int compare(Employee emp1, Employee emp2) {
//先按照 name 排序,如果 name 相同,則按生日日期的先後排序。【即:訂製排序】
//先對傳入的參數進行驗證
if(!(emp1 instanceof Employee && emp2 instanceof Employee)) {
System.out.println("類型不正確..");
return 0;
}
//比較 name
int i = emp1.getName().compareTo(emp2.getName());
if(i != 0) {
return i;
}
return emp1.getBirthday().compareTo(emp2.getBirthday());//動態綁定到MyDate類的compareto方法
}
});
System.out.println("排序後");
System.out.println(employees);
}
}
@SuppressWarnings({"all"})
class Employee {
private String name;
private double sal;
private MyDate birthday;
public Employee(String name, double sal, MyDate birthday) {
this.name = name;
this.sal = sal;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "\nEmployee{" +
"name='" + name + '\'' +
", sal=" + sal +
", birthday=" + birthday +
'}';
}
}
public class MyDate implements Comparable<MyDate> {
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
@Override
public String toString() {
return "MyDate{" +
"month=" + month +
", day=" + day +
", year=" + year +
'}';
}
@Override
public int compareTo(MyDate o) { //把對 year - month - day
//如果year相同 就比較Month
int yearMinus = year - o.getYear();
if (yearMinus != 0) {
return yearMinus;
}
//如果year相同 就比較month
int monthMinus = month - o.getMonth();
if (monthMinus != 0) {
return monthMinus;
}
return day - o.getDay();
}
}
自定義泛型
自定義泛型類
·基本語法
class 類名 <T,R> {
成員
}
·細節
普通成員可以使用泛型 屬性 方法
使用泛型的數組 不能初始化
靜態方法中不能使用類的泛型
泛型類的類型 是在創建對象時確定的 因為創建對象時 需要指定類型
如果在創建對象時 沒有指定類型 默認為Object
自定義泛型介面
·基本語法
interface 介面名 <T,R> {
}
·細節
介面中 成員是靜態性質的 所以不能使用泛型
泛型介面的類型 在繼承介面 或者 實現介面 是確定
沒有指定類型 默認為Object
自定義泛型方法
·細節
泛型方法 可以定義在普通類 也可以定義在泛型類中
當泛型方法被調用時 類型會確定
public void eat(E e) 修飾符後沒有 <T,R> eat 方法不是 泛型方法 而是使用了泛型