java集合超詳細(最新推薦)
1 - 概述
所有的集合類和集合接口都在java.util包下。
在內存中申請一塊空間用來存儲數據,在Java中集合就是替換掉定長的數組的一種引用數據類型。
2 - 集合與數組的區(qū)別
長度區(qū)別
數組長度固定,定義長了造成內存空間的浪費,定義短了不夠用。
集合大小可以變,用多少空間拿多少空間。
內容區(qū)別
數組可以存儲基本數據類型和引用數據類型
集合中能存儲引用數據類型(存儲的為對象的內存地址)
list.add(100);//為自動裝箱,100為Integer包裝的
元素區(qū)別
數組中只能存儲同一種類型成員
集合中可以存儲不同類型數據(一般情況下也只存儲同一種類型的數據)
集合結構
在java中每一個不同的集合,底層會對應不同的數據結構。往不同的集合中
存儲元素,等于將數據放到了不同的數據結構當中。什么是數據結構?數據存儲的
結構就是數據結構。不同的數據結構,數據存儲方式不同。
- 單列集合 Collection
- List可以重復:ArrayList/LinkedList
- Set不可重復:HashSet/TreeSet
(大量文字插入會導致圖片不清,所以在此進行更詳細的描述)
- List特點:此處順序并不是大小順序,而是存入數據的先后順序。有序因為List集合都有下標,下標從0開始,以遞增。
- Set特點:取出順序不一定為存入順序,另外Set集合沒有下標。
- ArrayList是非線程安全的。
- HashSet集合在new的時候,底層實際上new了一個HashMap集合。向HashSet集合中存儲元素,實際上是存儲到了HashMap的key中了。HashMap集合是一個Hash表數據結構。
- SortedSet集合存儲元素的特點:由于繼承了Set集合,所以他的特點也是無序不可重復,但是放在SortedSet集合中的元素可以自動排序。放到該集合中的元素是自動按照大小順序排序的。
- TreeSet集合底層實際上是TreeMap。TreeSet集合在new的時候,底層實際上new了一個TreeMap集合。向TreeSet集合中存儲元素,實際上是存儲到了TreeMap的key中了。TreeMap集合是一個二叉樹數據結構。
雙列集合Map:HashMap/TreeMap
粗體是接口 斜體是實現類
3- Collection集合
3.1 - 概述
單列集合的頂層接口,既然是接口就不能直接使用,需要通過實現類!~
3.2 - Collection集合的的常用方法
方法名 | 說明 |
---|---|
boolean add(E e) | 添加元素到集合的末尾(追加) |
boolean remove(Object o) | 刪除指定的元素,成功則返回true(底層調用equles) |
void clear() | 清空集合 |
boolean contains(Object o) | 判斷元素在集合中是否存在,存在則返回true(底層調用equles) |
boolean isEmpty() | 判斷集合是否為空,空則返回true |
int size() | 返回集合中元素個數 |
import java.util.ArrayList; import java.util.Collection; /** * @author Mr.樂 * @Description */ public class Collection_01 { public static void main(String[] args) { //父類的引用指向子類的對象,形成多態(tài) Collection<String> con = new ArrayList<>(); //追加的方式添加元素 con.add("東邪"); con.add("西毒"); con.add("南帝"); con.add("北丐"); con.add("中神通"); //刪除,通過元素名稱刪除元素 System.out.println(con.remove("西毒")); //判斷集合中是否包含指定參數元素 System.out.println(con.contains("西毒")); //false System.out.println(con.contains("東邪")); //true //獲取集合中元素個數 System.out.println(con.size()); //判斷是否為空 System.out.println(con.isEmpty());//false //清空集合 con.clear(); //判斷是否為空 System.out.println(con.isEmpty());//true System.out.println(con);//打印集合的元素 } }
3.3 - Collection集合的遍歷
以下迭代方式,是所有Collection通用的一種方式。在Map集合中不能使用,在所有的Collection以及子類中使用。
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; /** * @author Mr.樂 * @Description Collection 集合的遍歷 */ public class Connection_02 { public static void main(String[] args) { //多態(tài) Collection<String> con = new ArrayList<>(); //添加元素 con.add("abc"); con.add("def"); con.add("100"); con.add("444"); //Collection集合的遍歷方式 //因為沒有索引的概念,所以Collection集合不能使用fori進行遍歷 //增強版for循環(huán),其實底層使用的也是迭代器,在字節(jié)碼文件中查看 for (String str : con) { System.out.print(str + "\t"); } System.out.println();//換行 //迭代器,集合專屬的遍歷工具 Iterator<String> it = con.iterator();//創(chuàng)建迭代器對象 while (it.hasNext()){//判斷下一個位置是否有元素 System.out.print(it.next() + "\t");//獲取到下一個位置的元素 } } }
3.4-Iterator的remove
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; /** * @author Mr.樂 * @Description */ public class Connection_remove { public static void main(String[] args) { // 創(chuàng)建集合 Collection c = new ArrayList(); // 注意:此時獲取的迭代器,指向的是那是集合中沒有元素狀態(tài)下的迭代器。 // 一定要注意:集合結構只要發(fā)生改變,迭代器必須重新獲取。 // 當集合結構發(fā)生了改變,迭代器沒有重新獲取時,調用next()方法時:java.util.ConcurrentModificationException Iterator it = c.iterator(); // 添加元素 c.add(1); // Integer類型 c.add(2); c.add(3); // 獲取迭代器 //Iterator it = c.iterator(); /*while(it.hasNext()){ // 編寫代碼時next()方法返回值類型必須是Object。 // Integer i = it.next(); Object obj = it.next(); System.out.println(obj); }*/ Collection c2 = new ArrayList(); c2.add("abc"); c2.add("def"); c2.add("xyz"); Iterator it2 = c2.iterator(); while(it2.hasNext()){ Object o = it2.next(); // 刪除元素 // 刪除元素之后,集合的結構發(fā)生了變化,應該重新去獲取迭代器 // 但是,循環(huán)下一次的時候并沒有重新獲取迭代器,所以會出現異常:java.util.ConcurrentModificationException // 出異常根本原因是:集合中元素刪除了,但是沒有更新迭代器(迭代器不知道集合變化了) //c2.remove(o); // 直接通過集合去刪除元素,沒有通知迭代器。(導致迭代器的快照和原集合狀態(tài)不同。) // 使用迭代器來刪除可以嗎? // 迭代器去刪除時,會自動更新迭代器,并且更新集合(刪除集合中的元素)。 it2.remove(); // 刪除的一定是迭代器指向的當前元素。 System.out.println(o); } System.out.println(c2.size()); //0 } }
4-List
原型ArrayList<E>
- ArrayList是一個List接口的實現類,底層使用的是一個可以調整大小的數組實現的。
<E>
:是一種特殊的數據類型(引用數據類型) -- 泛型- ArrayList<String> 或者 ArrayList<Integer> 或者 ArrayList<Student>
4.1 - ArrayList構造和添加方法
方法名 | 說明 |
---|---|
public ArrayList<E>() | 創(chuàng)建一個空集合 |
public boolean add(E e) | 將指定的參數元素追加到集合的末尾 |
public void add(int index ,E e) | 在集合的指定位置添加指定的元素(插入元素) |
public void addAll(E object) | 用于將指定集合中所有元素添加到當前集合中 |
/** * @author Mr.樂 * @Description ArrayList構造和添加方法 */ public class ArrayList_01 { public static void main(String[] args) { //創(chuàng)建空集合 ArrayList<String> list = new ArrayList<>();//泛型定義為String //采用默認追加的方式添加元素 System.out.println(list.add("劉德華")); System.out.println(list.add("張學友")); System.out.println(list.add("郭富城")); System.out.println(list.add("黎明")); //插入的方式添加元素 // list.add(10,"譚詠麟");//插入元素方法索引值不能大于集合中元素個數 // list.add(4,"譚詠麟");//表示在集合中最后位置插入元素,與追加相同 list.add(1,"譚詠麟");//指定位置插入元素,索引位置之后的元素會自動向后進行移動 ArrayList<String> newList = new ArrayList<>();//創(chuàng)建新的集合 newList.add("小沈陽"); newList.add("宋小寶"); newList.add("趙四"); newList.add("劉能"); //查看集合中的元素 System.out.println("原集合內部元素:" + list); System.out.println("新集合內部元素:" + newList); list.addAll(newList); //將新集合全部元素添加到原集合中 System.out.println("原集合內部元素:" + list); } }
4.2 - ArrayList集合常用方法
方法名 | 說明 |
---|---|
public boolean remove(Object o) | 刪除指定的元素,成功則返回true |
public E remove(int index) | 刪除指定索引位置的元素,返回被刪除的元素 |
public E set(int index,E e) | 修改指定索引位置的元素,返回修改前的元素 |
public E get(int index) | 獲取指定索引對應的元素 |
public int size() | 獲取結合中元素個數 |
import java.util.ArrayList; import java.util.Iterator; /** * @author Mr.樂 * @Description ArrayList集合常用方法 */ public class ArrayList_02 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); //追加方式添加元素 list.add("東邪"); list.add("西毒"); list.add("南帝"); list.add("北丐"); list.add("中神通"); //刪除 System.out.println(list.remove("西毒"));//通過元素名稱刪除,返回boolean System.out.println(list.remove(1));//通過索引刪除元素,返回被刪除元素名 //修改 System.out.println(list.set(1,"西毒"));//指定索引位置修改元素,并返回被修改元素 System.out.println("原集合中元素有:" + list); //獲取方法 System.out.println(list.get(1));//通過指定索引位置獲取集合元素 //獲取集合元素個數 System.out.println(list.size()); //集合的遍歷,普通for循環(huán) for (int i = 0; i < list.size(); i++) { System.out.print(list.get(i) + "\t"); } System.out.println(); //增強版for循環(huán) for (String name : list) { System.out.print(name+ "\t"); } System.out.println(); //迭代器 Iterator<String> it = list.iterator();//創(chuàng)建迭代器 while (it.hasNext()){//判斷下一個位置是否有元素 System.out.print(it.next() + "\t"); //next方法表示獲取下一個位置的元素 } System.out.println(); //Stream流 list.stream().forEach(System.out::println); } }
4.3 -ArrayList實現原理
底層代碼:
屬性:
DEFAULT_CAPACITY = 10 默認長度,初始化容量為10Object[] EMPTY_ELEMENTDATA = {} //有參構造所創(chuàng)建Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {} //無參構造所創(chuàng)建的Object[] elementData;底層為Object類型的數組,存儲的元素都在此。int size 實際存放的個數
構造方法 :
//一個參數的構造 public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } //參數如果大于零,則為創(chuàng)建數組的長度; //參數如果等于零,EMPTY_ELEMENTDATA; //參數如果小于0,拋出異常。 //無參構造 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //DEFAULTCAPACITY_EMPTY_ELEMENTDATA new對象時默認為0 當添加第一個元素的時候,數組擴容至10
add方法源碼:(jdk1.8與之不同,此處為jdk16)
//源碼 public boolean add(E e) { modCount++;//操作次數 add(e, elementData, size); //e 操作對象; elementData 底層操作的數組;size 默認大小0 return true; } ------------------------------------------------ private void add(E e, Object[] elementData, int s) { if (s == elementData.length)//ture elementData = grow(); elementData[s] = e; //存數據 size = s + 1; //最小需要長度 } ---------------------------------------------------------- private Object[] grow() { return grow(size + 1); } ----------------------------------------------------- private Object[] grow(int minCapacity) { //初始傳入為size+1 為1 int oldCapacity = elementData.length; //初始為0 if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //if條件為初始數組長度>0或者數組不是無參構造構建的 int newCapacity = ArraysSupport.newLength(oldCapacity, //舊數組的長度 minCapacity - oldCapacity, /* minimum growth */ //最小需要長度-舊數組的長度 大于0代表空間不足 oldCapacity >> 1 /* preferred growth */); //二進制位右移1位 位舊數組長度/2 return elementData = Arrays.copyOf(elementData, newCapacity); 將數據放入新數組中 } else { return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)]; //數組長度 DEFAULT_CAPACITY為10 此處代表無參構造默認長度為10 } } ---------------------------------------------------- public static int newLength(int oldLength, int minGrowth, int prefGrowth) { // assert oldLength >= 0 // assert minGrowth > 0 int newLength = Math.max(minGrowth, prefGrowth) + oldLength; //如果prefGrowth>minGrowth 擴容1.5倍 minGrowth>prefGrowth為需要多少給多少 if (newLength - MAX_ARRAY_LENGTH <= 0) { //MAX_ARRAY_LENGTH為int最大值 表示新數組長度如果小于int的最大值 return newLength; } return hugeLength(oldLength, minGrowth); //返回int最大值 }
ArrayList集合底層是數組,怎么優(yōu)化?
盡可能少的擴容。因為數組擴容效率比較低,建議在使用ArrayList集合 的時候預估計元素的個數,給定一個初始化容量。數組優(yōu)點:
檢索效率比較高。(每個元素占用空間大小相同,內存地址是連續(xù)的,知道首元素內存地址,
然后知道下標,通過數學表達式計算出元素的內存地址,所以檢索效率最高。)數組缺點:
隨機增刪元素效率比較低。
另外數組無法存儲大數據量。(很難找到一塊非常巨大的連續(xù)的內存空間。)向數組末尾添加元素,效率很高,不受影響。
4.4 -LinkedList實現原理
底層代碼
屬性:
transient int size = 0;//初始長度 transient Node<E> first;//頭節(jié)點 transient Node<E> last;//尾節(jié)點
add方法源碼:(jdk1.8與之不同,此處為jdk16)
public boolean add(E e) { linkLast(e); return true; } -------------------------------------- void linkLast(E e) { final Node<E> l = last; //初始為null final Node<E> newNode = new Node<>(l, e, null); //參數1:位上一個節(jié)點的內存地址,參數2:e為插入的數據,參數3:下一個節(jié)點的內存地址 last = newNode; // 最后節(jié)點為新節(jié)點 if (l == null) //如果newNode的前一個節(jié)點為null,則將新節(jié)點賦給first first = newNode; else l.next = newNode; //尾節(jié)點下一個節(jié)點為新節(jié)點 size++;//大小 modCount++;//操作數 }
4.5-LinkedList和ArrayList
LinkedList和ArrayList方法一樣,只是底層實現不一樣。ArrayList底層為數組存儲,LinkedList是以雙向鏈表存儲。LinkedList集合沒有初始化容量。最初這個鏈表中沒有任何元素。first和last引用都是null。
鏈表的優(yōu)點:
由于鏈表上的元素在空間存儲上內存地址不連續(xù)。
所以隨機增刪元素的時候不會有大量元素位移,因此隨機增刪效率較高。
在以后的開發(fā)中,如果遇到隨機增刪集合中元素的業(yè)務比較多時,建議
使用LinkedList。
鏈表的缺點:
不能通過數學表達式計算被查找元素的內存地址,每一次查找都是從頭
節(jié)點開始遍歷,直到找到為止。所以LinkedList集合檢索/查找的效率
較低。
ArrayList:把檢索發(fā)揮到極致。(末尾添加元素效率還是很高的。)
LinkedList:把隨機增刪發(fā)揮到極致。
加元素都是往末尾添加,所以ArrayList用的比LinkedList多。
4.6 -Vector
1、底層也是一個數組。
2、初始化容量:10
3、怎么擴容的?
擴容之后是原容量的2倍。
10--> 20 --> 40 --> 80
4、Vector中所有的方法都是線程同步的,都帶有synchronized關鍵字,
是線程安全的。效率比較低,使用較少了。
5、怎么將一個線程不安全的ArrayList集合轉換成線程安全的呢?
使用集合工具類:
java.util.Collections;
java.util.Collection 是集合接口。
java.util.Collections 是集合工具類。
Collections.synchronizedList();//將及格轉換為線程安全的。
5-Set
5.1 -概述
- Set集合也是一個接口,繼承自Collection,與List類似,都需要通過實現類來進行操作。
- 特點
- 不允許包含重復的值
- 沒有索引(就不能使用普通的for循環(huán)進行遍歷)
import java.util.HashSet; import java.util.Set; /** * @author Mr.樂 * @Description Set集合 */ public class Demo01 { public static void main(String[] args) { //使用多態(tài),父類的引用指向子類對象 Set<String> set = new HashSet<>(); //添加元素 set.add("黃固"); set.add("歐陽鋒"); set.add("段智興"); set.add("洪七公"); set.add("段智興"); System.out.println(set);//打印集合 //[洪七公, 黃固, 歐陽鋒, 段智興] //HashSet集合對于元素的讀寫順序不做保證 //相同的元素,多次存儲,只能保留一個,并且不會報錯 //List集合可以存儲重復元素,Set集合不行 } }
例:雙色球
import java.util.Random; import java.util.TreeSet; /** * @author Mr.樂 * @Description 雙色球 -Set版 */ public class Demo02 { public static void main(String[] args) { Random ran = new Random();//創(chuàng)建隨機類對象 int blueBall = ran.nextInt(16) + 1; // HashSet<Integer> redBalls = new HashSet<>();//創(chuàng)建集合用來存儲紅球 TreeSet<Object> redBalls = new TreeSet<>();//TreeSet集合自帶排序規(guī)則 while (redBalls.size() < 6){ redBalls.add(ran.nextInt(33) + 1);//將當前生成的紅球直接存進集合中 //因為Set集合不能存儲重復的元素,所以去重的操作可以省略不做。 } System.out.println("紅球:" + redBalls + "籃球 [" + blueBall + "]"); } }
5.2 -哈希值
Set集合的去重原理使用的是哈希值。
哈希值就是JDK根據對象地址 或者 字符串 或者數值 通過自己內部的計算出來的一個整數類型數據
public int hashCode()
- 用來獲取哈希值,來自于Object頂層類- 對象的哈希值特點
- 同一個對象多次調用
hashCode()
方法,得到的結果是相同的。- 默認情況下,不同的對象的哈希值也是不同的(特殊情況除外)
/** * @author Mr.樂 * @Description 哈希值 */ public class Demo03 { public static void main(String[] args) { //相同對象哈希值相同 System.out.println("張三".hashCode());//774889 System.out.println("張三".hashCode());//774889 //不同對象哈希值不同 System.out.println(new Object().hashCode()); System.out.println(new Object().hashCode()); //不同的對象的哈希值也有可能相同,例外情況 System.out.println("輅鵝".hashCode());//1179395 System.out.println("較鴉".hashCode());//1179395 System.out.println("輒鸇".hashCode());//1179395 System.out.println("輔鷨".hashCode());//1179395 } }
5.3 -HashSet去重原理
- HashSet集合的特點
- 底層結構是“哈希表”
- 集合對于讀寫順序不做保證
- 沒有索引
- Set集合中的內容不能重復
/** * @author Mr.樂 * @Description HashSet去重原理 */ public class Demo04 { public static void main(String[] args) { HashSet<Student> set = new HashSet<>(); //添加元素 set.add(new Student("黃固",28)); set.add(new Student("歐陽鋒",38)); set.add(new Student("段智興",48)); set.add(new Student("洪七公",40)); set.add(new Student("段智興",48)); //從程序的角度來考慮,兩個段智興不是同一個對象,都有自己的存儲空間,所以哈希值也不一樣。 for (Student stu : set) { System.out.println(stu); } /* 重寫hashcode和equals Student{name='段智興', age=48} Student{name='歐陽鋒', age=38} Student{name='洪七公', age=40} Student{name='黃固', age=28} */ } }
5.4 -LinkedHashSet
- 特點
- LinkedHashSet是哈希表和鏈表實現的Set接口,具有可預測的讀寫順序。
- 有鏈表來保證元素有序
- 有哈希表來保證元素的唯一性
/** * @author Mr.樂 * @Description LinkedHashSet */ public class Demo05 { public static void main(String[] args) { LinkedHashSet<String> set = new LinkedHashSet<>(); //添加元素 set.add("黃固"); set.add("歐陽鋒"); set.add("段智興"); set.add("洪七公"); set.add("段智興");//重復的元素不能存進去 System.out.println(set);//打印集合 [黃固, 歐陽鋒, 段智興, 洪七公] } }
5.5 -TreeSet
1、TreeSet集合底層實際上是一個TreeMap
2、TreeMap集合底層是一個二叉樹。
3、放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。
4、TreeSet集合中的元素:無序不可重復,但是可以按照元素的大小順序自動排序。
import java.util.TreeSet; public class TreeSetTest02 { public static void main(String[] args) { // 創(chuàng)建一個TreeSet集合 TreeSet<String> ts = new TreeSet<>(); // 添加String ts.add("zhangsan"); ts.add("lisi"); ts.add("wangwu"); ts.add("zhangsi"); ts.add("wangliu"); // 遍歷 for(String s : ts){ // 按照字典順序,升序! System.out.println(s); } /* lisi wangliu wangwu zhangsan zhangsi */ TreeSet<Integer> ts2 = new TreeSet<>(); ts2.add(100); ts2.add(200); ts2.add(900); ts2.add(800); ts2.add(600); ts2.add(10); for(Integer elt : ts2){ // 升序! System.out.println(elt); } } }
5.5.1 -自定義排序規(guī)則
對于自定義的類無法排序,因為類中對象之間沒有比較規(guī)則,不知道誰大誰小。
/** * @author Mr.樂 * @Description 自定義比較器 */ import java.util.TreeSet; public class TreeSetTest04 { public static void main(String[] args) { Customer c1 = new Customer(32); Customer c2 = new Customer(20); Customer c3 = new Customer(30); Customer c4 = new Customer(25); // 創(chuàng)建TreeSet集合 TreeSet<Customer> customers = new TreeSet<>(); // 添加元素 customers.add(c1); customers.add(c2); customers.add(c3); customers.add(c4); // 遍歷 for (Customer c : customers){ System.out.println(c); } } } // 放在TreeSet集合中的元素需要實現java.lang.Comparable接口。 // 并且實現compareTo方法。equals可以不寫。 class Customer implements Comparable<Customer>{ int age; public Customer(int age){ this.age = age; } // 需要在這個方法中編寫比較的邏輯,或者說比較的規(guī)則,按照什么進行比較! // k.compareTo(t.key) // 拿著參數k和集合中的每一個k進行比較,返回值可能是>0 <0 =0 // 比較規(guī)則最終還是由程序員指定的:例如按照年齡升序。或者按照年齡降序。 @Override public int compareTo(Customer c) { // c1.compareTo(c2); return c.age - this.age; } public String toString(){ return "Customer[age="+age+"]"; } }
匿名內部類方式
public class TreeSetTest05 { public static void main(String[] args) { // TreeSet<Student> ts = new TreeSet<>();//默認排序規(guī)則 TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { int res = o1.getAge() - o2.getAge(); return 0 == res ? o1.getName().compareTo(o2.getName()) : res; //三目運算符 等于零用姓名排序 } });//默認排序規(guī)則 //添加元素 ts.add(new Student("Andy",19)); ts.add(new Student("Jack",18)); ts.add(new Student("Tom",21)); ts.add(new Student("Lucy",17)); ts.add(new Student("Bob",21)); //當年齡相同時,按照姓名的字典順序排序 for (Student stu : ts) { System.out.println(stu); } } }
Comparable和Comparator怎么選擇呢?
當比較規(guī)則不會發(fā)生改變的時候,或者說當比較規(guī)則只有1個的時候,建議實現Comparable接口。
如果比較規(guī)則有多個,并且需要多個比較規(guī)則之間頻繁切換,建議使用Comparator接口。
6 -Map
6.1 -概述
- 雙列集合:用來存儲鍵值對的集合。
interface Map<K,V>
: K(key)鍵 ,V(value)值- 將鍵映射到值的對象,不能出現重復的鍵,每個鍵最多可以映射到一個值
1、Map和Collection沒有繼承關系。
2、Map集合以key和value的方式存儲數據:鍵值對
key和value都是引用數據類型。
key和value都是存儲對象的內存地址。
key起到主導的地位,value是key的一個附屬品。
例子:
學號(Key) | 姓名(Value) |
---|---|
STU001 | 張三 |
STU002 | 李四 |
STU003 | 張三 |
6.2 -Map的基本方法
學號(Key) | 姓名(Value) |
---|---|
STU001 | 張三 |
STU002 | 李四 |
STU003 | 張三 |
import java.util.HashMap; import java.util.Map; /** * @author Mr.樂 * @Description 集合的基本方法 */ public class Map01 { public static void main(String[] args) { Map<String,String> map = new HashMap<>(); map.put("STU001","Andy"); map.put("STU002","Jack"); map.put("STU003","Tom"); map.put("STU004","Bob"); map.put("STU004","Smith");//設置(修改) //如果鍵不存在,則表示添加元素。如果鍵存在,則表示設置值。 //刪除 System.out.println(map.remove("STU003")); //Tom //判斷是否包含 System.out.println(map.containsKey("STU003")); //false System.out.println(map.containsKey("STU004")); //true System.out.println("-----------------------"); System.out.println(map.containsValue("Tom")); //false System.out.println(map.containsValue("Smith")); //true System.out.println("-----------------------"); System.out.println(map.isEmpty());//判斷集合是否為空 false map.clear();//清空集合 System.out.println(map.isEmpty()); //true System.out.println(map); //{} } }
6.3 -Map集合的獲取功能
import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @author Mr.樂 * @Description */ public class map_get { public static void main(String[] args) { Map<String,String> map = new HashMap<>(); map.put("STU001","Andy"); map.put("STU002","Jack"); map.put("STU003","Tom"); map.put("STU004","Bob"); //get通過鍵獲取值 System.out.println(map.get("STU003")); System.out.println("------------------"); //keySet 獲取所有鍵的Set集合 Set<String> keySet = map.keySet(); System.out.println(keySet); //values 獲取所有值的Collection集合 Collection<String> values = map.values(); System.out.println(values); //entrySet 獲取所有鍵值對對象的Set集合 Set<Map.Entry<String, String>> es = map.entrySet(); //Map集合通過entrySet()方法轉換成的這個Set集合,Set集合中元素的類型是 Map.Entry<K,V> //Map.Entry和String一樣,都是一種類型的名字,只不過:Map.Entry是靜態(tài)內部類,是Map中的靜態(tài)內部類 System.out.println(es); //[STU001=Andy, STU003=Tom, STU002=Jack, STU004=Bob] for (Map.Entry<String, String> entry:es){ System.out.println("key:"+entry.getKey()+" "+"value:"+entry.getValue()); } /* key:STU001 value:Andy key:STU003 value:Tom key:STU002 value:Jack key:STU004 value:Bob */ } }
6.4 -哈希表
通過 數組 + 鏈表 實現的一種數據結構
哈希表的構造方法的參數是一個長度為16個元素的數組,通過哈希值 % 16 的值,作為頭節(jié)點在數組中選擇對應的位置,就形成了哈希表。
注:圖轉自動力節(jié)點。
6.5 -HashMap
6.5.1 -底層源碼
public class HashMap{ // HashMap底層實際上就是一個數組。(一維數組) Node<K,V>[] table; // 靜態(tài)的內部類HashMap.Node static class Node<K,V> { final int hash; // 哈希值(哈希值是key的hashCode()方法的執(zhí)行結果。hash值通過哈希函數/算法,可以轉換存儲成數組的下標。) final K key; // 存儲到Map集合中的那個key V value; // 存儲到Map集合中的那個value Node<K,V> next; // 下一個節(jié)點的內存地址。 } }
6.5.2 -特點
1、無序,不可重復。
為什么無序? 因為不一定掛到哪個單向鏈表上。
不可重復是怎么保證的? equals方法來保證HashMap集合的key不可重復。
如果key重復了,value會覆蓋。
2、放在HashMap集合key部分的元素其實就是放到HashSet集合中了。
所以HashSet集合中的元素也需要同時重寫hashCode()+equals()方法。
3、HashMap集合的默認初始化容量是16,默認加載因子是0.75
這個默認加載因子是當HashMap集合底層數組的容量達到75%的時候,數組以二叉樹開始擴容。
重點,記?。篐ashMap集合初始化容量必須是2的倍數,這也是官方推薦的,
這是因為達到散列均勻,為了提高HashMap集合的存取效率,所必須的。
6.5.3 -注意
1.向Map集合中存,以及從Map集合中取,都是先調用key的hashCode方法,然后再調用equals方法!
equals方法有可能調用,也有可能不調用。
拿put(k,v)舉例,什么時候equals不會調用? k.hashCode()方法返回哈希值, 哈希值經過哈希算法轉換成數組下標。 數組下標位置上如果是null,equals不需要執(zhí)行。 拿get(k)舉例,什么時候equals不會調用? k.hashCode()方法返回哈希值, 哈希值經過哈希算法轉換成數組下標。 數組下標位置上如果是null,equals不需要執(zhí)行。
4.假設將所有的hashCode()方法返回值固定為某個值,那么會導致底層哈希表變成了 純單向鏈表。
這種情況我們成為:散列分布不均勻。
什么是散列分布均勻?
假設有100個元素,10個單向鏈表,那么每個單向鏈表上有10個節(jié)點,這是最好的, 是散列分布均勻的。假設將所有的hashCode()方法返回值都設定為不一樣的值,可以嗎,有什么問題? 不行,因為這樣的話導致底層哈希表就成為一維數組了,沒有鏈表的概念了。 也是散列分布不均勻。散列分布均勻需要你重寫hashCode()方法時有一定的技巧。
7 -Properties
Properties是一個Map集合,繼承Hashtable,Properties的key和value都是String類型。 Properties被稱為屬性類對象。 Properties是線程安全的。
7.1 -方法
import java.io.IOException; import java.util.Properties; import java.util.Set; /** * @author Mr.樂 * @Description Properties特有方法 */ public class Properties01 { public static void main(String[] args) throws IOException { Properties prop = new Properties(); final String SRC = "./myConf.ini";//定義配置信息存儲路徑 // mySave(prop,SRC);//存儲配置文件 myLoad(prop,SRC);//加載配置文件信息 //PASSWORD<--->123456 //DATABASE<--->YX2115 //PORT<--->3306 //USERNAME<--->root } private static void myLoad(Properties prop, String src) throws IOException { FileReader fr = new FileReader(src); prop.load(fr);//通過流,加載指定路徑的配置文件 fr.close(); //遍歷 Set<String> keySet = prop.stringPropertyNames();//獲取對象鍵的Set集合 for (String key : keySet) { System.out.println(key + "<--->" + prop.getProperty(key));//通過鍵拿到值 } } private static void mySave(Properties prop, String src) throws IOException { //將配置信息存儲到對象中 prop.setProperty("USERNAME","root"); prop.setProperty("PASSWORD","123456"); prop.setProperty("DATABASE","YX2115"); prop.setProperty("PORT","3306"); //寫入文件 FileWriter fw = new FileWriter(src);//創(chuàng)建輸出流對象 prop.store(fw,"MyDataBase Configure!~"); fw.close(); } }
8 -總結
本篇文章介紹了集合的常用方法以及個別集合的底層是如何實現的。介紹了集合的繼承與實現結構。各個集合的擴容方式及擴容大小以及各個集合的優(yōu)點和用途。希望大家可以根據本篇文章可以更加深刻的理解java中的集合。
到此這篇關于java集合超詳細的文章就介紹到這了,更多相關java集合內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Redis Lettuce連接redis集群實現過程詳細講解
這篇文章主要介紹了Redis Lettuce連接redis集群實現過程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧2023-01-01Springboot整合nacos報錯無法連接nacos的解決
這篇文章主要介紹了Springboot整合nacos報錯無法連接nacos的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06使用ScheduledThreadPoolExecutor踩過最痛的坑
這篇文章主要介紹了使用ScheduledThreadPoolExecutor踩過最痛的坑及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08解決Mybatis-plus和pagehelper依賴沖突的方法示例
這篇文章主要介紹了解決Mybatis-plus和pagehelper依賴沖突的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04