Java中的集合(下)

### 11.3.3 List實(shí)現(xiàn)類(lèi)的區(qū)別

#### 1、Vector和ArrayList的區(qū)別

核心類(lèi)庫(kù)中List接口有兩個(gè)實(shí)現(xiàn)類(lèi):Vector和ArrayList。它們的底層物理結(jié)構(gòu)都是數(shù)組,我們稱為動(dòng)態(tài)數(shù)組。

(1)ArrayList是新版的動(dòng)態(tài)數(shù)組,線程不安全,效率高,Vector是舊版的動(dòng)態(tài)數(shù)組,線程安全,效率低。

(2)動(dòng)態(tài)數(shù)組的擴(kuò)容機(jī)制不同,ArrayList擴(kuò)容為原來(lái)的1.5倍,Vector擴(kuò)容增加為原來(lái)的2倍。

(3)數(shù)組的初始化容量,如果在構(gòu)建ArrayList與Vector的集合對(duì)象時(shí),沒(méi)有顯式指定初始化容量,那么Vector的內(nèi)部數(shù)組的初始容量默認(rèn)為10,而ArrayList在JDK1.6及之前的版本也是10,JDK1.7之后的版本ArrayList初始化為長(zhǎng)度為0的空數(shù)組,之后在添加第一個(gè)元素時(shí),再創(chuàng)建長(zhǎng)度為10的數(shù)組。

ArrayList在第1次添加時(shí)再創(chuàng)建數(shù)組是為了避免浪費(fèi)。因?yàn)楹芏喾椒ǖ姆祷刂凳茿rrayList類(lèi)型,需要返回一個(gè)ArrayList的對(duì)象,例如:后期從數(shù)據(jù)庫(kù)查詢對(duì)象的方法,返回值很多就是ArrayList。有可能你要查詢的數(shù)據(jù)不存在,要么返回null,要么返回一個(gè)沒(méi)有元素的ArrayList對(duì)象。

#### 2、ArrayList與LinkedList的區(qū)別

動(dòng)態(tài)數(shù)組底層的物理結(jié)構(gòu)是數(shù)組,因此根據(jù)索引訪問(wèn)的效率非常高。但是==非末尾==位置的插入和刪除效率不高,因?yàn)樯婕暗揭苿?dòng)元素。==末尾位置==的插入和刪除不涉及移動(dòng)元素。另外添加操作時(shí)涉及到擴(kuò)容問(wèn)題,就會(huì)增加時(shí)空消耗。

鏈表底層的物理結(jié)構(gòu)是鏈表,因此根據(jù)索引訪問(wèn)的效率不高,但是插入和刪除不需要移動(dòng)元素,只需要修改前后元素的指向關(guān)系即可,而且鏈表的添加不會(huì)涉及到擴(kuò)容問(wèn)題。

 

## 11.4 棧和隊(duì)列

### 11.4.1 棧

堆棧是一種先進(jìn)后出(FILO:first in last out)或后進(jìn)先出(LIFO:last in first out)的結(jié)構(gòu)。

棧只是邏輯結(jié)構(gòu),其物理結(jié)構(gòu)可以是數(shù)組,也可以是鏈表,即棧結(jié)構(gòu)分為順序棧和鏈?zhǔn)綏!?/p>

核心類(lèi)庫(kù)中的棧結(jié)構(gòu)有Stack和LinkdeList。Stack就是順序棧,它是Vector的子類(lèi)。LinkedList是鏈?zhǔn)綏!?/p>

體現(xiàn)棧結(jié)構(gòu)的操作方法:

*  peek()方法:查看棧頂元素,不彈出
*  pop()方法:彈出棧
*  push(E e)方法:壓入棧

```java
package com.atguigu.list;
import org.junit.Test;
import java.util.LinkedList;
import java.util.Stack;
public class TestStack {
   @Test
   public void test1(){
       Stack<Integer> list = new Stack<>();
       list.push(1);
       list.push(2);
       list.push(3);
       System.out.println("list = " + list);
       System.out.println("list.peek()=" + list.peek());
       System.out.println("list.peek()=" + list.peek());
       System.out.println("list.peek()=" + list.peek());
/*
        System.out.println("list.pop() =" + list.pop());
        System.out.println("list.pop() =" + list.pop());
        System.out.println("list.pop() =" + list.pop());
        System.out.println("list.pop() =" + list.pop());//java.util.NoSuchElementException
*/
        while(!list.empty()){
           System.out.println("list.pop() =" + list.pop());
       }
   }
   @Test
   public void test2(){
       LinkedList<Integer> list = new LinkedList<>();
       list.push(1);
       list.push(2);
       list.push(3);
       System.out.println("list = " + list);
       System.out.println("list.peek()=" + list.peek());
       System.out.println("list.peek()=" + list.peek());
       System.out.println("list.peek()=" + list.peek());
/*
        System.out.println("list.pop() =" + list.pop());
        System.out.println("list.pop() =" + list.pop());
        System.out.println("list.pop() =" + list.pop());
        System.out.println("list.pop() =" + list.pop());//java.util.NoSuchElementException
*/
       while(!list.isEmpty()){
           System.out.println("list.pop() =" + list.pop());
       }
   }
}
```

### 11.4.2 隊(duì)列

隊(duì)列(Queue)是一種(但并非一定)先進(jìn)先出(FIFO)的結(jié)構(gòu)。

隊(duì)列是邏輯結(jié)構(gòu),其物理結(jié)構(gòu)可以是數(shù)組,也可以是鏈表。隊(duì)列有普通隊(duì)列、雙端隊(duì)列、并發(fā)隊(duì)列等等,核心類(lèi)庫(kù)中的隊(duì)列實(shí)現(xiàn)類(lèi)有很多(后面會(huì)學(xué)到很多),LinkdeList是雙端隊(duì)列的實(shí)現(xiàn)類(lèi)。

==Queue==除了基本的 `Collection`操作外,==隊(duì)列==還提供其他的插入、提取和檢查操作。每個(gè)方法都存在兩種形式:一種拋出異常(操作失敗時(shí)),另一種返回一個(gè)特殊值(`null` 或  `false`,具體取決于操作)。`Queue` 實(shí)現(xiàn)通常不允許插入  元素,盡管某些實(shí)現(xiàn)(如 )并不禁止插入  。即使在允許 null 的實(shí)現(xiàn)中,也不應(yīng)該將  插入到  中,因?yàn)? 也用作  方法的一個(gè)特殊返回值,表明隊(duì)列不包含元素。

|      | *拋出異常* | *返回特殊值* |
| ---- | ---------- | ------------ |
| 插入 | add(e)     | offer(e)     |
| 移除 | remove()   | poll()       |
| 檢查 | element()  | peek()       |

==Deque==,名稱 *deque* 是“double ended queue==(雙端隊(duì)列)==”的縮寫(xiě),通常讀為“deck”。此接口定義在雙端隊(duì)列兩端訪問(wèn)元素的方法。提供插入、移除和檢查元素的方法。每種方法都存在兩種形式:一種形式在操作失敗時(shí)拋出異常,另一種形式返回一個(gè)特殊值(`null`  或 `false`,具體取決于操作)。Deque接口的實(shí)現(xiàn)類(lèi)有ArrayDeque和LinkedList,它們一個(gè)底層是使用數(shù)組實(shí)現(xiàn),一個(gè)使用雙向鏈表實(shí)現(xiàn)。

|          | **第一個(gè)元素(頭部)** |               | **最后一個(gè)元素(尾部)** |              |
| -------- | ---------------------- | ------------- | ------------------------ | ------------ |
|          | *拋出異常*             | *特殊值*      | *拋出異常*               | *特殊值*     |
| **插入** | addFirst(e)            | offerFirst(e) | addLast(e)               | offerLast(e) |
| **移除** | removeFirst()          | pollFirst()   | removeLast()             | pollLast()   |
| **檢查** | getFirst()             | peekFirst()   | getLast()                | peekLast()   |

此接口擴(kuò)展了 `Queue`接口。在將雙端隊(duì)列用作隊(duì)列時(shí),將得到  FIFO(先進(jìn)先出)行為。將元素添加到雙端隊(duì)列的末尾,從雙端隊(duì)列的開(kāi)頭移除元素。從 `Queue` 接口繼承的方法完全等效于  `Deque` 方法,如下表所示:  

| **`Queue` 方法** | **等效 `Deque` 方法** |
| ---------------- | --------------------- |
| add(e)           | addLast(e)            |
| offer(e)         | offerLast(e)          |
| remove()         | removeFirst()         |
| poll()           | pollFirst()           |
| element()        | getFirst()            |
| peek()           | peekFirst()           |

雙端隊(duì)列也可用作 LIFO(后進(jìn)先出)堆棧。應(yīng)優(yōu)先使用此接口而不是遺留 `Stack` 類(lèi)。在將雙端隊(duì)列用作堆棧時(shí),元素被推入雙端隊(duì)列的開(kāi)頭并從雙端隊(duì)列開(kāi)頭彈出。堆棧方法完全等效于 `Deque` 方法,如下表所示:  

| **堆棧方法** | **等效 `Deque` 方法** |
| ------------ | --------------------- |
| push(e)      | addFirst(e)           |
| pop()        | removeFirst()         |
| peek()       | peekFirst()           |

結(jié)論:Deque接口的實(shí)現(xiàn)類(lèi)既可以用作FILO堆棧使用,又可以用作FIFO隊(duì)列使用。

```java
package com.atguigu.queue;
import java.util.LinkedList;
public class TestQueue {
   public static void main(String[] args) {
       LinkedList<String> list = new LinkedList<>();
       list.addLast("張三");
       list.addLast("李四");
       list.addLast("王五");
       list.addLast("趙六");
       while (!list.isEmpty()){
           System.out.println("list.removeFirst()=" + list.removeFirst());
       }
   }
}
```

 

## 11.5 Map

### 11.5.1 概述

現(xiàn)實(shí)生活中,我們常會(huì)看到這樣的一種集合:IP地址與主機(jī)名,身份證號(hào)與個(gè)人,系統(tǒng)用戶名與系統(tǒng)用戶對(duì)象等,這種一一對(duì)應(yīng)的關(guān)系,就叫做映射。Java提供了專門(mén)的集合類(lèi)用來(lái)存放這種對(duì)象關(guān)系的對(duì)象,即`java.util.Map<K,V>`接口。Map接口的常用實(shí)現(xiàn)類(lèi):HashMap、TreeMap、LinkedHashMap和Properties。其中HashMap是 Map 接口使用頻率最高的實(shí)現(xiàn)類(lèi)。

我們通過(guò)查看`Map`接口描述,發(fā)現(xiàn)`Map<K,V>`接口下的集合與`Collection<E>`接口下的集合,它們存儲(chǔ)數(shù)據(jù)的形式不同。

* `Collection`中的集合,元素是孤立存在的(理解為單身),向集合中存儲(chǔ)元素采用一個(gè)個(gè)元素的方式存儲(chǔ)。
* `Map`中的集合,元素是成對(duì)存在的(理解為夫妻)。每個(gè)元素由鍵與值兩部分組成,通過(guò)鍵可以找對(duì)所對(duì)應(yīng)的值。
* `Collection`中的集合稱為單列集合,`Map`中的集合稱為雙列集合。
* 需要注意的是,`Map`中的集合不能包含重復(fù)的鍵,值可以重復(fù);每個(gè)鍵只能對(duì)應(yīng)一個(gè)值(這個(gè)值可以是單個(gè)值,也可以是個(gè)數(shù)組或集合值)。

### 11.5.2 Map常用方法

1、添加操作

* V put(K key,V value):添加一對(duì)鍵值對(duì)
* void putAll(Map<? extends K,? extends V> m):添加一組鍵值對(duì)

2、刪除

* void clear():清空map
* V remove(Object key):根據(jù)key刪除一對(duì)鍵值對(duì)
* default boolean remove(Object key,Object value):刪除匹配的(key,value)

3、修改value(JDK1.8新增)

- default V replace(K key, V value):找到目標(biāo)key,替換value
- default boolean replace(K key,V oldValue,V newValue):找到目標(biāo)(key,value),替換value
- default void replaceAll(BiFunction<? super K,? super V,? extends V> function):按照指定要求替換value

4、元素查詢的操作

* V get(Object key):根據(jù)key返回value
* boolean containsKey(Object key):判斷key是否存在
* boolean containsValue(Object value):判斷value是否存在
* boolean isEmpty():判斷map是否為空
* int size():獲取鍵值對(duì)的數(shù)量

5、遍歷

Map的遍歷,不能支持foreach,因?yàn)镸ap接口沒(méi)有繼承java.lang.Iterable<T>接口。只能用如下方式遍歷:

(1)分開(kāi)遍歷:

* 單獨(dú)遍歷所有key:Set<K> keySet()
* 單獨(dú)遍歷所有value:Collection<V> values()


(2)成對(duì)遍歷:

* 遍歷所有鍵值對(duì):Set<Map.Entry<K,V>> entrySet()
* 遍歷的是映射關(guān)系Map.Entry類(lèi)型的對(duì)象,Map.Entry是Map接口的內(nèi)部接口。每一種Map內(nèi)部有自己的Map.Entry的實(shí)現(xiàn)類(lèi)。在Map中存儲(chǔ)數(shù)據(jù),實(shí)際上是將Key---->value的數(shù)據(jù)存儲(chǔ)在Map.Entry接口的實(shí)例中,再在Map集合中插入Map.Entry的實(shí)例化對(duì)象,如圖示:

1563725601891.png

(3)調(diào)用forEach方法遍歷

- default void forEach(BiConsumer<? super K,? super V> action)  

```java
package com.atguigu.map;
import org.junit.Test;
import java.util.HashMap;
import java.util.function.BiFunction;
public class TestMapMethod {
   @Test
   public void test1(){
       //創(chuàng)建 map對(duì)象
       HashMap<String, String> map = new HashMap<String, String>();
       //添加元素到集合
       map.put("黃曉明", "楊穎");
       map.put("文章", "馬伊琍");
       map.put("文章", "姚笛");
       map.put("鄧超", "孫儷");
       System.out.println(map.size());
       System.out.println(map);
   }
   @Test
   public void test2(){
       HashMap<String, String> map = new HashMap<String, String>();
       //添加模范夫妻
       map.put("黃曉明", "楊穎");
       map.put("文章", "馬伊琍");
       map.put("鄧超", "孫儷");
       System.out.println(map);
       //刪除鍵值對(duì)
       map.remove("文章");
       System.out.println(map);
       map.remove("黃曉明","楊穎");
       System.out.println(map);
   }
   @Test
   public void test3(){
       HashMap<String, String> map = new HashMap<String, String>();
       //添加夫妻
       map.put("黃曉明", "楊穎");
       map.put("文章", "馬伊琍");
       map.put("鄧超", "孫儷");
       map.put("張三", null);
       System.out.println(map);
       //修改value
       map.replace("文章","姚笛");
       System.out.println(map);
       map.replace("黃曉明","楊穎", "angelababy");
       System.out.println(map);
       map.replaceAll(new BiFunction<String, String, String>() {
           @Override
           public String apply(String key, String value) {
               return value == null ? "如花" : value;
           }
       });
       System.out.println(map);
   }
   @Test
   public void test4(){
       HashMap<String, String> map = new HashMap<String, String>();
       //添加元素到集合
       map.put("黃曉明", "楊穎");
       map.put("文章", "馬伊琍");
       map.put("鄧超", "孫儷");
       // 想要查看 誰(shuí)的媳婦 是誰(shuí)
       System.out.println(map.get("黃曉明"));
       System.out.println(map.get("鄧超"));
   }
   
   @Test
   public void test4(){
       HashMap<String, String> map = new HashMap<String, String>();
       //添加元素到集合
       map.put("黃曉明", "楊穎");
       map.put("文章", "馬伊琍");
       map.put("鄧超", "孫儷");
       //遍歷所有key
       System.out.println("所有key:");
       Set<String> keySet = map.keySet();
       for (String key : keySet) {
           System.out.println(key);
       }
       //遍歷所有value
       System.out.println("所有value:");
       Collection<String> values = map.values();
       for (String value : values) {
           System.out.println(value);
       }
       //遍歷所有鍵值對(duì)
       System.out.println("所有鍵值對(duì):");
       Set<Map.Entry<String, String>> entries = map.entrySet();
       for (Map.Entry<String, String> entry : entries) {
           System.out.println(entry);
       }
       //調(diào)用forEach方法遍歷所有鍵值對(duì)
       System.out.println("所有鍵值對(duì):");
       BiConsumer<String,String> c = new BiConsumer<>() {
           @Override
           public void accept(String key, String value) {
               System.out.println(key+" vs "+value);
           }
       };
       map.forEach(c);
   }
   
}
```

### 11.5.3 Map接口的實(shí)現(xiàn)類(lèi)們

#### **1、HashMap和Hashtable**

HashMap和Hashtable都是哈希表。HashMap和Hashtable判斷兩個(gè) key 相等的標(biāo)準(zhǔn)是:兩個(gè) key 的hashCode 值相等,并且 equals() 方法也返回 true。因此,為了成功地在哈希表中存儲(chǔ)和獲取對(duì)象,用作鍵的對(duì)象必須實(shí)現(xiàn) hashCode 方法和 equals 方法。


* Hashtable是線程安全的,任何非 null 對(duì)象都可以用作鍵或值。

* HashMap是線程不安全的,并允許使用 null 值和 null 鍵。


示例代碼:添加員工姓名為key,薪資為value

```java
package com.atguigu.map;
import org.junit.Test;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
public class TestHashMap {
   @Test
   public void test01(){
       HashMap<String,Double> map = new HashMap<>();
       map.put("張三", 10000.0);
       //key相同,新的value會(huì)覆蓋原來(lái)的value
       //因?yàn)镾tring重寫(xiě)了hashCode和equals方法
       map.put("張三", 12000.0);
       map.put("李四", 14000.0);
       //HashMap支持key和value為null值
       String name = null;
       Double salary = null;
       map.put(name, salary);
       System.out.println(map);
     
   }
   @Test
   public void test02(){
       Hashtable<String,Double> map = new Hashtable<>();
       map.put("張三", 10000.0);
       //key相同,新的value會(huì)覆蓋原來(lái)的value
       //因?yàn)镾tring重寫(xiě)了hashCode和equals方法
       map.put("張三", 12000.0);
       map.put("李四", 14000.0);
       //Hashtable不支持key和value為null值
       /*String name = null;
       Double salary = null;
       map.put(name, salary);*/
       System.out.println(map);
   }
}
```

#### **2、LinkedHashMap**

LinkedHashMap 是 HashMap 的子類(lèi)。此實(shí)現(xiàn)與 HashMap 的不同之處在于,后者維護(hù)著一個(gè)運(yùn)行于所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,該迭代順序通常就是將鍵插入到映射中的順序(插入順序)。

示例代碼:添加員工姓名為key,薪資為value

```java
package com.atguigu.map;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class TestLinkedHashMap {
   public static void main(String[] args) {
       LinkedHashMap<String,Double> map = new LinkedHashMap<>();
       map.put("張三", 10000.0);
       //key相同,新的value會(huì)覆蓋原來(lái)的value
       //因?yàn)镾tring重寫(xiě)了hashCode和equals方法
       map.put("張三", 12000.0);
       map.put("李四", 14000.0);
       //HashMap支持key和value為null值
       String name = null;
       Double salary = null;
       map.put(name, salary);
       System.out.println(map);
   }
}
```

#### **3、TreeMap**

基于紅黑樹(shù)(Red-Black tree)的 NavigableMap 實(shí)現(xiàn)。該映射根據(jù)其鍵的自然順序進(jìn)行排序,或者根據(jù)創(chuàng)建映射時(shí)提供的 Comparator 進(jìn)行排序,具體取決于使用的構(gòu)造方法。

代碼示例:添加員工姓名為key,薪資為value

```java
package com.atguigu.map;
import java.util.Comparator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import org.junit.Test;
public class TestTreeMap {
    @Test
    public void test1() {
        TreeMap<String,Integer> map = new TreeMap<>();
        map.put("Jack", 11000);
        map.put("Alice", 12000);
        map.put("zhangsan", 13000);
        map.put("baitao", 14000);
        map.put("Lucy", 15000);
        
        //String實(shí)現(xiàn)了Comparable接口,默認(rèn)按照Unicode編碼值排序
        System.out.println(map);
    }
    @Test
    public void test2() {
        //指定定制比較器Comparator,按照Unicode編碼值排序,但是忽略大小寫(xiě)
        TreeMap<String,Integer> map = new TreeMap<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareToIgnoreCase(o2);
            }
        });
        map.put("Jack", 11000);
        map.put("Alice", 12000);
        map.put("zhangsan", 13000);
        map.put("baitao", 14000);
        map.put("Lucy", 15000);
        
        System.out.println(map);
    }
}
```

#### **4、Properties**

Properties 類(lèi)是 Hashtable 的子類(lèi),Properties 可保存在流中或從流中加載。屬性列表中每個(gè)鍵及其對(duì)應(yīng)值都是一個(gè)字符串。

存取數(shù)據(jù)時(shí),建議使用setProperty(String key,String value)方法和getProperty(String key)方法。

代碼示例:

```java
package com.atguigu.map;
import org.junit.Test;
import java.util.Properties;
public class TestProperties {
   @Test
   public void test01() {
       Properties properties = System.getProperties();
       String fileEncoding = properties.getProperty("file.encoding");//當(dāng)前源文件字符編碼
       System.out.println("fileEncoding = " + fileEncoding);
   }
   @Test
   public void test02() {
       Properties properties = new Properties();
       properties.setProperty("user","chai");
       properties.setProperty("password","123456");
       System.out.println(properties);
   }
}
```

### 11.5.4 Set集合與Map集合的關(guān)系

Set的內(nèi)部實(shí)現(xiàn)其實(shí)是一個(gè)Map。即HashSet的內(nèi)部實(shí)現(xiàn)是一個(gè)HashMap,TreeSet的內(nèi)部實(shí)現(xiàn)是一個(gè)TreeMap,LinkedHashSet的內(nèi)部實(shí)現(xiàn)是一個(gè)LinkedHashMap。

部分源代碼摘要:

HashSet源碼:

```java
   public HashSet() {
       map = new HashMap<>();
   }
   public HashSet(Collection<? extends E> c) {
       map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
       addAll(c);
   }
   public HashSet(int initialCapacity, float loadFactor) {
       map = new HashMap<>(initialCapacity, loadFactor);
   }
   public HashSet(int initialCapacity) {
       map = new HashMap<>(initialCapacity);
   }
    //這個(gè)構(gòu)造器是給子類(lèi)LinkedHashSet調(diào)用的
   HashSet(int initialCapacity, float loadFactor, boolean dummy) {
       map = new LinkedHashMap<>(initialCapacity, loadFactor);
   }
```

LinkedHashSet源碼:

```java
   public LinkedHashSet(int initialCapacity, float loadFactor) {
       super(initialCapacity, loadFactor, true);//調(diào)用HashSet的某個(gè)構(gòu)造器
   }
   public LinkedHashSet(int initialCapacity) {
       super(initialCapacity, .75f, true);//調(diào)用HashSet的某個(gè)構(gòu)造器
   }
   public LinkedHashSet() {
       super(16, .75f, true);
   }
   public LinkedHashSet(Collection<? extends E> c) {
       super(Math.max(2*c.size(), 11), .75f, true);//調(diào)用HashSet的某個(gè)構(gòu)造器
       addAll(c);
   }
```

TreeSet源碼:

```java
   public TreeSet() {
       this(new TreeMap<E,Object>());
   }
   public TreeSet(Comparator<? super E> comparator) {
       this(new TreeMap<>(comparator));
   }
   public TreeSet(Collection<? extends E> c) {
       this();
       addAll(c);
   }
   public TreeSet(SortedSet<E> s) {
       this(s.comparator());
       addAll(s);
   }
```

但是,咱們存到Set中只有一個(gè)元素,又是怎么變成(key,value)的呢?

以HashSet中的源碼為例:

```java
private static final Object PRESENT = new Object();
public boolean add(E e) {
   return map.put(e, PRESENT)==null;
}
public Iterator<E> iterator() {
   return map.keySet().iterator();
}
```

原來(lái)是,把添加到Set中的元素作為內(nèi)部實(shí)現(xiàn)map的key,然后用一個(gè)常量對(duì)象PRESENT對(duì)象,作為value。

這是因?yàn)镾et的元素不可重復(fù)和Map的key不可重復(fù)有相同特點(diǎn)。Map有一個(gè)方法keySet()可以返回所有key。

### 11.5.5 哈希表的原理分析

#### 1、二叉樹(shù)了解

二叉樹(shù)(Binary tree)是樹(shù)形結(jié)構(gòu)的一個(gè)重要類(lèi)型。二叉樹(shù)特點(diǎn)是每個(gè)結(jié)點(diǎn)最多只能有兩棵子樹(shù),且有左右之分。許多實(shí)際問(wèn)題抽象出來(lái)的數(shù)據(jù)結(jié)構(gòu)往往是二叉樹(shù)形式,二叉樹(shù)的存儲(chǔ)結(jié)構(gòu)及其算法都較為簡(jiǎn)單,因此二叉樹(shù)顯得特別重要。

1574575739236-17028853496111.png

前序遍歷:ABDHIECFG

中序遍歷:HDIBEAFCG

后序遍歷:HIDEBFGCA

以下是幾種經(jīng)典的二叉樹(shù):

1、滿二叉樹(shù): 除最后一層無(wú)任何子節(jié)點(diǎn)外,每一層上的所有結(jié)點(diǎn)都有兩個(gè)子結(jié)點(diǎn)的二叉樹(shù)。 第n層的結(jié)點(diǎn)數(shù)是2的n-1次方,總的結(jié)點(diǎn)個(gè)數(shù)是2的n次方-1

1574575163883-17028853496112.png

2、完全二叉樹(shù): 葉結(jié)點(diǎn)只能出現(xiàn)在最底層的兩層,且最底層葉結(jié)點(diǎn)均處于次底層葉結(jié)點(diǎn)的左側(cè)。

1574575180247-17028853496113.png

3、平衡二叉樹(shù):平衡二叉樹(shù)(Self-balancing binary search tree)又被稱為AVL樹(shù)(有別于AVL算法),且具有以下性質(zhì):它是一 棵空樹(shù)或它的左右兩個(gè)子樹(shù)的高度差的絕對(duì)值不超過(guò)1,并且左右兩個(gè)子樹(shù)都是一棵平衡二叉樹(shù), 但不要求非葉節(jié)點(diǎn)都有兩個(gè)子結(jié)點(diǎn) 。平衡二叉樹(shù)的常用實(shí)現(xiàn)方法有紅黑樹(shù)、AVL、替罪羊樹(shù)、Treap、伸展樹(shù)等。例如紅黑樹(shù)的要求:

- 節(jié)點(diǎn)是紅色或者黑色
- 根節(jié)點(diǎn)是黑色

- 每個(gè)葉子的節(jié)點(diǎn)都是黑色的空節(jié)點(diǎn)(NULL)

- 每個(gè)紅色節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色的。

- 從任意節(jié)點(diǎn)到其每個(gè)葉子的所有路徑都包含相同的黑色節(jié)點(diǎn)數(shù)量。


當(dāng)我們插入或刪除節(jié)點(diǎn)時(shí),可能會(huì)破壞已有的紅黑樹(shù),使得它不滿足以上5個(gè)要求,那么此時(shí)就需要進(jìn)行處理:

1、recolor :將某個(gè)節(jié)點(diǎn)變紅或變黑

2、rotation :將紅黑樹(shù)某些結(jié)點(diǎn)分支進(jìn)行旋轉(zhuǎn)(左旋或右旋)

使得它繼續(xù)滿足以上的5個(gè)要求。

紅黑樹(shù)-17028853496114.jpeg

例如:插入了結(jié)點(diǎn)21之后,紅黑樹(shù)處理成:

紅黑樹(shù)2-17028853496115.jpeg

 

#### 2、 哈希表的數(shù)據(jù)結(jié)構(gòu)

請(qǐng)看PPT

## 11.6 集合框架

image-20240718161133558.png

 

## 11.7 Collections工具類(lèi)

參考操作數(shù)組的工具類(lèi):Arrays。

Collections 是一個(gè)操作 Set、List 和 Map 等集合的工具類(lèi)。Collections 中提供了一系列靜態(tài)的方法對(duì)集合元素進(jìn)行排序、查詢和修改等操作,還提供了對(duì)集合對(duì)象設(shè)置不可變、對(duì)集合對(duì)象實(shí)現(xiàn)同步控制等方法:

* public static <T> boolean addAll(Collection<? super T> c,T... elements)將所有指定元素添加到指定 collection 中。
* public static <T> int binarySearch(List<? extends Comparable<? super T>> list,T key)在List集合中查找某個(gè)元素的下標(biāo),但是List的元素必須是T或T的子類(lèi)對(duì)象,而且必須是可比較大小的,即支持自然排序的。而且集合也事先必須是有序的,否則結(jié)果不確定。
* public static <T> int binarySearch(List<? extends T> list,T key,Comparator<? super T> c)在List集合中查找某個(gè)元素的下標(biāo),但是List的元素必須是T或T的子類(lèi)對(duì)象,而且集合也事先必須是按照c比較器規(guī)則進(jìn)行排序過(guò)的,否則結(jié)果不確定。
* public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)在coll集合中找出最大的元素,集合中的對(duì)象必須是T或T的子類(lèi)對(duì)象,而且支持自然排序
* public static <T> T max(Collection<? extends T> coll,Comparator<? super T> comp)在coll集合中找出最大的元素,集合中的對(duì)象必須是T或T的子類(lèi)對(duì)象,按照比較器comp找出最大者
* public static void reverse(List<?> list)反轉(zhuǎn)指定列表List中元素的順序。
* public static void shuffle(List<?> list) List 集合元素進(jìn)行隨機(jī)排序,類(lèi)似洗牌
* public static <T extends Comparable<? super T>> void sort(List<T> list)根據(jù)元素的自然順序?qū)χ付?List 集合元素按升序排序
* public static <T> void sort(List<T> list,Comparator<? super T> c)根據(jù)指定的 Comparator 產(chǎn)生的順序?qū)?List 集合元素進(jìn)行排序
* public static void swap(List<?> list,int i,int j)將指定 list 集合中的 i 處元素和 j 處元素進(jìn)行交換
* public static int frequency(Collection<?> c,Object o)返回指定集合中指定元素的出現(xiàn)次數(shù)
* public static <T> void copy(List<? super T> dest,List<? extends T> src)將src中的內(nèi)容復(fù)制到dest中
* public static <T> boolean replaceAll(List<T> list,T oldVal,T newVal):使用新值替換 List 對(duì)象的所有舊值
* Collections 類(lèi)中提供了多個(gè) synchronizedXxx() 方法,該方法可使將指定集合包裝成線程同步的集合,從而可以解決多線程并發(fā)訪問(wèn)集合時(shí)的線程安全問(wèn)題
* Collections類(lèi)中提供了多個(gè)unmodifiableXxx()方法,該方法返回指定 Xxx的不可修改的視圖。

```java
package com.atguigu.collections;
import org.junit.Test;
import java.text.Collator;
import java.util.*;
public class TestCollections {
   @Test
   public void test11(){
       /*
       public static <T> boolean replaceAll(List<T> list,T oldVal,T newVal):使用新值替換 List 對(duì)象的所有舊值
        */
       List<String> list = new ArrayList<>();
       Collections.addAll(list,"hello","java","world","hello","hello");
       Collections.replaceAll(list, "hello","chai");
       System.out.println(list);
   }
   @Test
   public void test10(){
       List<Integer> list = new ArrayList<>();
       for(int i=1; i<=5; i++){//1-5
           list.add(i);
       }
       List<Integer> list2 = new ArrayList<>();
       for(int i=11; i<=13; i++){//11-13
           list2.add(i);
       }
       list.addAll(list2);
       System.out.println(list);//[1, 2, 3, 4, 5, 11, 12, 13]
   }
   @Test
   public void test09(){
       /*
        * public static <T> void copy(List<? super T> dest,List<? extends T> src)將src中的內(nèi)容復(fù)制到dest中
        */
       List<Integer> list = new ArrayList<>();
       for(int i=1; i<=5; i++){//1-5
           list.add(i);
       }
       List<Integer> list2 = new ArrayList<>();
       for(int i=11; i<=13; i++){//11-13
           list2.add(i);
       }
       Collections.copy(list, list2);
       System.out.println(list);
       List<Integer> list3 = new ArrayList<>();
       for(int i=11; i<=20; i++){//11-20
           list3.add(i);
       }
       Collections.copy(list, list3);//java.lang.IndexOutOfBoundsException: Source does not fit in dest
       System.out.println(list);
   }
   @Test
   public void test08(){
       /*
       public static int frequency(Collection<?> c,Object o)返回指定集合中指定元素的出現(xiàn)次數(shù)
        */
       List<String> list = new ArrayList<>();
       Collections.addAll(list,"hello","java","world","hello","hello");
       int count = Collections.frequency(list, "hello");
       System.out.println("count = " + count);
   }
   @Test
   public void test07(){
       /*
       public static void swap(List<?> list,int i,int j)將指定 list 集合中的 i 處元素和 j 處元素進(jìn)行交換
        */
       List<String> list = new ArrayList<>();
       Collections.addAll(list,"hello","java","world");
       Collections.swap(list,0,2);
       System.out.println(list);
   }
   @Test
   public void test06() {
       /*
        * public static <T extends Comparable<? super T>> void sort(List<T> list)根據(jù)元素的自然順序?qū)χ付?List 集合元素按升序排序
        * public static <T> void sort(List<T> list,Comparator<? super T> c)根據(jù)指定的 Comparator 產(chǎn)生的順序?qū)?List 集合元素進(jìn)行排序
        */
       List<Man> list = new ArrayList<>();
       list.add(new Man("張三",23));
       list.add(new Man("李四",24));
       list.add(new Man("王五",25));
       Collections.sort(list);
       System.out.println(list);
       Collections.sort(list, new Comparator<Man>() {
           @Override
           public int compare(Man o1, Man o2) {
               return Collator.getInstance(Locale.CHINA).compare(o1.getName(),o2.getName());
           }
       });
       System.out.println(list);
   }
   @Test
   public void test05(){
       /*
       public static void shuffle(List<?> list) List 集合元素進(jìn)行隨機(jī)排序,類(lèi)似洗牌,打亂順序
        */
       List<String> list = new ArrayList<>();
       Collections.addAll(list,"hello","java","world");
       Collections.shuffle(list);
       System.out.println(list);
   }
   @Test
   public void test04(){
       /*
       public static void reverse(List<?> list)反轉(zhuǎn)指定列表List中元素的順序。
        */
       List<String> list = new ArrayList<>();
       Collections.addAll(list,"hello","java","world");
       System.out.println(list);
       Collections.reverse(list);
       System.out.println(list);
   }
   @Test
   public void test03(){
       /*
        * public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
        *      <T extends Object & Comparable<? super T>>:要求T必須繼承Object,又實(shí)現(xiàn)Comparable接口,或者T的父類(lèi)實(shí)現(xiàn)Comparable接口
        *      在coll集合中找出最大的元素,集合中的對(duì)象必須是T或T的子類(lèi)對(duì)象,而且支持自然排序
        * public static <T> T max(Collection<? extends T> coll,Comparator<? super T> comp)
        *      在coll集合中找出最大的元素,集合中的對(duì)象必須是T或T的子類(lèi)對(duì)象,按照比較器comp找出最大者
        *
        */
       List<Man> list = new ArrayList<>();
       list.add(new Man("張三",23));
       list.add(new Man("李四",24));
       list.add(new Man("王五",25));
       /*Man max = Collections.max(list);//要求Man實(shí)現(xiàn)Comparable接口,或者父類(lèi)實(shí)現(xiàn)
       System.out.println(max);*/
       Man max = Collections.max(list, new Comparator<Man>() {
           @Override
           public int compare(Man o1, Man o2) {
               return o2.getAge()-o2.getAge();
           }
       });
       System.out.println(max);
   }
   @Test
   public void test02(){
       /*
        * public static <T> int binarySearch(List<? extends Comparable<? super T>> list,T key)
        *      要求List集合的元素類(lèi)型 實(shí)現(xiàn)了 Comparable接口,這個(gè)實(shí)現(xiàn)可以是T類(lèi)型本身也可以T的父類(lèi)實(shí)現(xiàn)這個(gè)接口。
        *      說(shuō)明List中的元素支持自然排序功能。
        *      在List集合中查找某個(gè)元素的下標(biāo),但是List的元素必須是T或T的子類(lèi)對(duì)象,而且必須是可比較大小的,即支持自然排序的。而且集合也事先必須是有序的,否則結(jié)果不確定。
        * public static <T> int binarySearch(List<? extends T> list,T key,Comparator<? super T> c)
        *      說(shuō)明List集合中元素的類(lèi)型<=T,Comparator<? super T>說(shuō)明要傳入一個(gè)Comparator接口的實(shí)現(xiàn)類(lèi)對(duì)象,實(shí)現(xiàn)類(lèi)泛型的指定要求>=T
        *      例如:List中存儲(chǔ)的是Man(男)對(duì)象,T可以是Person類(lèi)型,實(shí)現(xiàn)Comparator的時(shí)候可以是 Comparator<Person>
        *      例如:List中存儲(chǔ)的是Man(男)對(duì)象,T可以是Man類(lèi)型,實(shí)現(xiàn)Comparator的時(shí)候可以是 Comparator<Person>
        *     在List集合中查找某個(gè)元素的下標(biāo),但是List的元素必須是T或T的子類(lèi)對(duì)象,而且集合也事先必須是按照c比較器規(guī)則進(jìn)行排序過(guò)的,否則結(jié)果不確定。
        *
        * 二分查找要求數(shù)組或List必須是“有大小順序”。
        * 二分查找的思路: 和[mid]元素比較,如果相同,就找到了,不相同要看大小關(guān)系,決定去左邊還是右邊繼續(xù)查找。
        */
       List<Man> list = new ArrayList<>();
       list.add(new Man("張三",23));
       list.add(new Man("李四",24));
       list.add(new Man("王五",25));
//        int index = Collections.binarySearch(list, new Man("王五", 25));//要求實(shí)現(xiàn)Comparable接口
//        System.out.println(index);
       int index = Collections.binarySearch(list, new Man("王五", 25), new Comparator<Person>() {
           @Override
           public int compare(Person o1, Person o2) {
               return o1.getAge() - o2.getAge();
           }
       });
       System.out.println(index);
   }
   @Test
   public void test01(){
       /*
       public static <T> boolean addAll(Collection<? super T> c,T... elements)將所有指定元素添加到指定 collection 中。
       Collection的集合的元素類(lèi)型必須>=T類(lèi)型
        */
       Collection<Object> coll = new ArrayList<>();
       Collections.addAll(coll, "hello","java");
       Collections.addAll(coll, 1,2,3,4);
       Collection<String> coll2 = new ArrayList<>();
       Collections.addAll(coll2, "hello","java");
//        Collections.addAll(coll2, 1,2,3,4);//String和Integer之間沒(méi)有父子類(lèi)關(guān)系
   }
}
```

## 11.8 Arrays工具類(lèi)

public static <T> List<T> asList(T... a):將指定元素添加到一個(gè)固定大小的列表中,并返回列表。

```java
@Test
   public void test(){
       List<String> list = Arrays.asList("hello", "java", "world");
       System.out.println(list);
       try {
           list.add("chai");
       } catch (Exception e) {
           e.printStackTrace();
       }
       try {
           list.remove("hello");
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
```