nacos_jar包啟動命令(新增名稱空間)
陣列的不足:
-
長度固定,且一旦確定,無法更改
-
儲存的必須為同一型別
-
使用陣列進行增刪元素比較麻煩
集合
-
可以動態儲存任意多個物件,使用方便
-
提供了增刪改查的方法:add、remove、set、get
-
使用集合新增刪除等都比較簡潔
框架體系圖!
map 和 collection沒有關係
//1. 集合主要是兩組 (單列集合,雙列集合)
//2. Collection介面 有兩個重要子介面 List Set ,他們的實現都是單列集合
//3. Map介面 實現的子類是雙列集合 Key-Value
Collection介面
-
Collection實現子類,可以存放多個元素,每個元素可以是Object
-
Collection介面 沒有直接的實現子類,是通過 List 和 Set 實現的
-
常用方法:
import java.util.ArrayList;
import java.util.List;
public class collection_01 {
-
Collection介面遍歷元素方式:使用 Iterator迭代器
-
Iterator 物件稱為迭代器
-
所有實現了 Collection 介面的集合類都有一個 iterator() 方法,用以返回一個實現了Iterator介面的物件
-
Iterator 僅用於遍歷集合,Iterator 本身不存放物件
-
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Collection02 {
-
增強for 遍歷
import java.util.ArrayList;
import java.util.List;
public class CollectionFor {
-
List 介面
List 介面是Collection 介面 的子介面
-
List集合類中 元素有序(新增順序和取出順序一致)、且可重複
-
List集合中的每個元素都有其對應的順序索引 (類似於陣列)
-
List 介面的實現類特別多
-
常用方法:
//subList 返回的區間為 fromIndex <= subList < toList
只要是List介面的實現類,
遍歷都有三種方式: 迭代器、增強for、普通for
//迭代器
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//增強for
for (Object o : list) {
System.out.println(o);
}
//普通for
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
ArrayList 注意事項
-
ArrayList 可以放任何Object 物件 ,包括 null
-
ArrayList 是由陣列實現資料儲存的
-
它基本等同於Vector 但是由於它是執行緒不安全的(執行效率高)。
在多執行緒情況下,不建議使用ArrayList
ArrayList 原始碼分析(含debug看原始碼設定)
先說結論:
-
ArrayList 中維護了一個Object型別的陣列 elementData
transient Object[] elementData;//transient (轉瞬即逝的) 表示該屬性不會被序列化
-
當首次建立ArrayList 物件時,如果使用的是無參構造器,則初始的elementData容量為0,第1次新增,則擴容 elementData 為10,如果再次擴容,則擴容 elementData 為1.5倍
-
如果使用的是指定大小的構造器,則初始大小為指定大小,如果再次擴容,則擴容 elementData 為1.5倍
檢視原始碼
自行debug
debug的一些設定:
使用 F7、shift + F8、F9快捷鍵,進行debug。 看原始碼
Vector
-
底層與 ArrayList 一樣 也是用的物件陣列,protected Object[] elementData;
-
是執行緒同步的,即執行緒安全,因為它的方法都有 synchronized 修飾
LinkedList
說明:
-
底層實現了雙向連結串列和雙端佇列的特點
-
可以新增任意元素,元素可以重複, 包括null
-
執行緒不安全,沒有實現執行緒同步
底層機制:
-
底層其實是維護了一個雙向連結串列
-
first指向首節點,last指向尾節點,中間有prev和next指標。
連結串列雖然不支援隨機存取,但是新增刪除效率更高。結構如圖:
//自己Debug
//看原始碼
import java.util.LinkedList;
public class LinkedListDebug {
Set 介面
基本介紹:
-
無序,沒有索引
-
不允許重複元素,最多包含一個null
-
JDK API中Set介面的實現類
-
常用方法與 Collection介面的一致
-
遍歷方式:1. 迭代器 iterator 2. 增強for 注意不能使用索引獲取,所以普通for迴圈不行
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
HashSet
-
實現了 Set 介面
-
底層其實是HaspMap
-
可以存放null,但只能存放一個(Set介面實現類物件都是這樣的,元素不能重複)
-
存取順序不一致(Set實現物件共有)
-
HashSet底層是用 HashMap實現的,是(陣列+連結串列+紅黑樹)結構
看 add 的底層機制,深刻理解不能加入相同元素
//預設建立時,其他欄位都是預設的(如int型別的變數就是0...),只有載入因子賦值了
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
//add
//1.
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//2.
public V put(K key, V value) {
//hash 的計算是使資料雜湊存貯的關鍵
return putVal(hash(key), key, value, false, true);
}
//2.1
static final int hash(Object key) {
int h;
//無符號右移16位 ^ 是異或運算 (同為0,異為1)
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//3.
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
}
//3.1 ( resize )擴容
final Node<K,V>[] resize() {
}
針對hash值的思考:
-
(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 這一步獲得一個很大的hash值
-
將上面很大的一個hash值 跟 此時table的(length-1)進行與(&)運算,就能將這個巨大的hash值,雜湊到table上。
針對擴容的思考:
-
預設載入因子( loadFactor ) 大小為 0.75f
-
根據原始碼,每次新增元素都會 ++size , 所以擴容時,不管此時 table 陣列 有沒有滿,只要size達到 閾值, 都會擴容。
-
根據原始碼,擴容時,是看size是否達到閾值。
//putVal 部分原始碼
if (++size > threshold)
resize();
針對樹化連結串列結點的思考:
//putVal 部分原始碼
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
//treeifyBin 部分原始碼
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
1. TREEIFY_THRESHOLD 的值是8,因此,當某一table中的某一連結串列達到8個時,將會進行變化的樹化
1. 但樹化有先決條件,(見treeifyBin 部分原始碼),若此時table中某一連結串列結點數大於等於8時,但 table的長度小於 MIN_TREEIFY_CAPACITY(值為64)時,就無法進行樹化,而是將需要新增的結點,繼續掛在原來的連結串列上。同時,resize() table,將table擴容。
針對新增結點情況的思考:
//putVal 部分原始碼
//情況一:table 是新建的,預設為null,則先進行擴容
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//情況二:此時table 已存在,且根據hash算出來的 table 的索引處暫無結點,直接新增
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
//情況三:table的索引處已有結點
// 1. 新新增的Node 與 已有的結點不同。主要看:(原始碼:)
// p.hash == hash &&
// ((k = p.key) == key || (key != null && key.equals(k))) )
// 判斷是否是同一結點 ( 判斷關鍵 -> equals !)
// 2. 如果是該索引處的連結串列中,已有同樣的結點,則不再新增
// 3. 若沒有,則掛在連結串列尾。(注意樹化)
// 4. 如果此時已經是紅黑樹,則不是掛在連結串列尾而是(原始碼)
// e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else
LinkedHashSet
-
HashSet 的子類
-
底層是 LinkedHashMap 維護了一個數組 + 雙向連結串列
-
LinkedHashMap 根據hashCode 值決定存放位置,同時使用雙向連結串列維護元素次序,所以看上去元素的取出順序和插入順序相同。
-
不允許新增重複元素
結構解讀:
-
LinkedHashSet 中維護了hashtable 和 一個雙向連結串列(LinkedHashSet 有head 和 tail)
-
每個節點有 before 和 after 用於形成雙向連結串列
-
新增時,與HashSet類似,先求hash值,然後呼叫map.put -> putVal ;如果有重複的元素則不新增。同時 LinkedHashMap 重寫了 HashMap 的 newNode 方法,保證新新增的結點,是滿足雙向連結串列的。
-
遍歷時通過 head -> afer -> after -> ... -> tail 遍歷,保證與插入時相同。
其實底層都是HashMap
LinkedHashMap 重寫了 HashMap 的 newNode 方法,保證新新增的結點,是滿足雙向連結串列的。
TreeSet
底層其實是TreeMap
使用預設構造器的話,那麼TreeSet也是無序的
(方法都與 HashSet 相同)唯一區別:通過構造器可以使加入的元素有序。
注意:重寫compare後,判斷元素是否相同就要根據compare方法來決定,若該方法返回0,則不能新增相 應的新元素。
//底層其實是TreeMap
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
//使用構造器讓元素有序 例子
//根據重寫的compare方法,來確定順序
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//通過String 的 compareTo 方法,返回一個int
return ((String)o1).compareTo((String)o2);
}
});
add 的 底層也是TreeMap
與HashSet 相同,新增時,呼叫put方法,
public boolean add(E e){
return m.put(e, PRESENT)==null;//PRESENT 是TreeSet的常量。
}
Map介面
實現類的通用特點:
-
Map用於儲存具有對映關係的 Key-Value (雙列集合)
-
Map中的 Key和 Value 可以是任何引用型別的資料,會封裝到HashMap$Node中
-
Map的Key 不允許重複,(因為用Key 算出hash值,儲存在table中)
-
Map的Value 可以重複
-
Map中的 Key 可以為null,Value 也可以為null。(因為key不能重複, 所以null 的key只能由一個)
-
新增時使用 put(...) 方法。當新新增的key 已存在,但 value 不同時,會覆蓋已存在的該key對應的value
//map為Map介面的實現物件(實現類的物件)
map.put("n1","jack"); -
key與value 存在單向的一對一關係,即可以通過key 找到對應的value
map.get("n1");//傳入一個key,返回對應的value
-
常用 String 類作為key (但別的資料型別也可以作為key)
-
結構: 除了 table 以外,還有一個EntrySet,為了方便遍歷。
-
如圖:
-
EntrySet是為了遍歷而存在的一個Node的副本,其中的k - v都是指向table表中的k和v的引用
-
因為 Node 實現了 Entry 介面,所以EntrySet中,可以存放Node
class Node<K,V> implements Map.Entry<K,V>
-
為了方便單獨遍歷k 和 v ,還另外提供了兩個內部類 KeySet 和 Values (分別實現了Set介面 和 Collection 介面)
-
Entry是Map的內部介面
-
Map的結構:有table 、entrySet、KeySet、Values
-
-
關於map的結構的思考:
反正記住他們的作用,都是通過方法獲得,然後再遍歷。
-
entrySet是HashMap的一個Set 集合屬性,用於遍歷k-v
2. KeySet 是HashMap的內部類,用於遍歷 k
2. Valuse 是HashMap的內部類,用於遍歷 v
常用方法:
-
put(" k "," v "); 新增
-
remove(" k "); 根據鍵值刪除
-
get(" k "); 根據鍵值獲取值
-
size(); 獲取元素個數
-
isEmpty(); 判空
-
clear(); 清空map
-
containsKey(" k "); 查詢鍵值是否存在,返回鍵值所對應的值
遍歷:
package com.lxly.hsp.chapter14.map_;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;