一文看懂Java集合(詳細)
一、陣列與集合
-
集合、陣列都是對多個資料進行儲存操作的結構,簡稱Java容器。
說明:此時的儲存,主要指的是記憶體層面的儲存,不涉及到持久化的儲存(.txt,.jpg,.avi,資料庫中) -
陣列在儲存多個資料方面
-
特點:
一旦初始化以後,其長度就確定了。
陣列一旦定義好,其元素的型別也就確定了。我們也就只能操作指定型別的資料了。比如 :String[] arr;int[] arr1;Object[] arr2; -
缺點
一旦初始化以後,其長度就不可修改。
陣列中提供的方法非常有限,對於新增、刪除、插入資料等操作,非常不便,同時效率不高。
獲取陣列中實際元素的個數的需求,陣列沒有現成的屬性或方法可用
陣列儲存資料的特點:有序、可重複。對於無序、不可重複的需求,不能滿足。
-
-
集合儲存的優點:解決陣列儲存資料方面的弊端。
二、Collection介面
-
單例集合框架
Collection介面:單列集合,用來儲存一個一個的物件-
List介面:儲存有序的、可重複的資料。---->“動態”陣列
常用實現類:ArrayList、LinkedList、Vector -
Set介面:儲存無序的、不可重複的資料。 ---->數學中的“集合”
常用實現類:HashSet、LinkedHashSet、TreeSet
對應圖示:
-
-
Collection介面中常用方法
-
add(Object e):將元素e新增到集合coll中
-
addAll(Collection coll1):將coll1集合中的元素新增到當前的集合中
-
size():獲取新增的元素的個數
-
clear():清空集合元素
-
isEmpty():判斷當前集合是否為空
@Test public void test1() { Collection coll = new ArrayList(); //1. add(Object e):將元素e新增到集合coll中 coll.add("AA"); coll.add("BB"); coll.add(123);//自動裝箱 coll.add(new Date()); //2. size():獲取新增的元素的個數 System.out.println(coll.size());//4 //3. addAll(Collection coll1):將coll1集合中的元素新增到當前的集合中 Collection coll1 = new ArrayList(); coll1.add(456); coll1.add("CC"); coll.addAll(coll1); System.out.println(coll.size());//6 System.out.println(coll);//[AA, BB, 123, Fri Dec 04 21:10:12 CST 2020, 456, CC] //4. clear():清空集合元素 coll.clear(); //5. isEmpty():判斷當前集合是否為空 System.out.println(coll.isEmpty());//true }
-
contains(Object obj):判斷當前集合中是否包含obj,我們在判斷是會呼叫obj物件所在類的equals()
-
containsAll(Collection coll1):判斷形參coll1中的所有元素是否都存在於當前集合中
@Test public void test2(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(false); coll.add(new Person("Jerry",20)); //6. contains(Object obj):判斷當前集合中是否包含obj,我們在判斷是會呼叫obj物件所在類的equals() boolean contains = coll.contains(123); System.out.println(contains);//true System.out.println(coll.contains(new String("Tom")));//true System.out.println(coll.contains(new Person("Jerry",20)));//false,要使它變成true,在Person中重寫equals()方法 //7. containsAll(Collection coll1):判斷形參coll1中的所有元素是否都存在於當前集合中 Collection coll1 = Arrays.asList(123,456); System.out.println(coll.containsAll(coll1));//true }
-
remove(Object obj):從當前集合中移除obj元素。移除成功返回true,移除失敗返回false。
-
removeAll(Collection coll1):差集:從當前集合中移除coll1中所有的元素
@Test public void test3(){ //8. remove(Object obj):從當前集合中移除obj元素。移除成功返回true,移除失敗返回false。 Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(false); coll.add(new Person("Jerry",20)); coll.remove(1234); System.out.println(coll);//[123, 456, Tom, false, Person{name='Jerry', age=20}] coll.remove(new Person("Jerry",20)); System.out.println(coll);//[123, 456, Tom, false] //9. removeAll(Collection coll1):差集:從當前集合中移除coll1中所有的元素 Collection coll1 = Arrays.asList(123,4567); coll.removeAll(coll1); System.out.println(coll);//[456, Tom, false] }
-
retainAll(Collection coll1):交集:獲取當前集合和coll1集合的交集,並返回給當前集合
-
equals(Object obj):要想返回true,需要當前集合和形參集合的元素都相同
@Test public void test4(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(false); coll.add(new Person("Jerry",20)); //10. retainAll(Collection coll1):交集:獲取當前集合和coll1集合的交集,並返回給當前集合 // Collection coll1 = Arrays.asList(123,456,789); // coll.retainAll(coll1); // System.out.println(coll);//[123, 456] //11. equals(Object obj):要想返回true,需要當前集合和形參集合的元素都相同 Collection coll1 = new ArrayList(); coll1.add(123); coll1.add(456); coll1.add(new String("Tom")); coll1.add(false); coll1.add(new Person("Jerry",20)); System.out.println(coll.equals(coll1));//true }
-
hashCode():返回當前物件的雜湊值
-
集合 —> 陣列:toArray()
擴充:陣列 —>集合:呼叫Arrays類的靜態方法asList(T … t),程式碼如下 -
iterator():返回Iterator介面的例項,用於遍歷集合元素
@Test public void test5(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(false); coll.add(new Person("Jerry",20)); //12. hashCode():返回當前物件的雜湊值 System.out.println(coll.hashCode());//1560300821 //13. 集合 ---> 陣列:toArray() Object[] arr = coll.toArray(); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); // 123 // 456 // Tom // false // Person{name='Jerry', age=20} } //擴充:陣列 ---> 集合:呼叫Arrays類的靜態方法asList(T ... t) List<String> strings = Arrays.asList("AA", "BB", "CC"); System.out.println(strings);//[AA, BB, CC] //注意細節 List arr1 = Arrays.asList(new int[]{123, 456}); System.out.println(arr1.size());//1 List arr2 = Arrays.asList(new Integer[]{123, 456}); System.out.println(arr2.size());//2 //14. iterator():返回Iterator介面的例項,用於遍歷集合元素。放在IteratorTest中測試 }
-
注: 使用Collection集合儲存物件,要求物件所屬的類滿足:向Collection介面的實現類的物件中新增資料obj時,要求obj所在類要重寫equals()
三、Iterator介面和foreach迴圈
-
遍歷Collection的兩種方式:
① 使用迭代器Iterator
② foreach迴圈(或增強for迴圈) -
java.utils包下定義的迭代器介面:Iterator
-
作用:遍歷集合Collectiton元素
-
內部的方法:hasNext() 和 nest()
-
如何讓獲取例項:coll.iterator()返回一個迭代器例項
-
遍歷程式碼實現:
Iterator iterator = coll.iterator(); //hasNext():判斷是否還有下一個元素 while (iter.hasNext()) { //next():①指標下移 ②將下移以後集合位置上的元素返回 System.out.println(iter.next()); }
-
集合物件每次呼叫iterator()方法都得到一個全新的迭代器物件,預設遊標都在集合的第一個元素之前,圖示如下
-
內部定義了remove(),可以在遍歷時,刪除集合中的元素。此方法不同於集合直接呼叫remove()
//測試Iterator中的remove() //如果還未呼叫next()或在上一次呼叫 next 方法之後已經呼叫了 remove 方法,再呼叫remove都會報IllegalStateException。 //內部定義了remove(),可以在遍歷的時候,刪除集合中的元素。此方法不同於集合直接呼叫remove() @Test public void test3(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person("Jerry",20)); coll.add(new String("Tom")); coll.add(false); //刪除集合中"Tom" Iterator iterator = coll.iterator(); while (iterator.hasNext()){ // iterator.remove(); Object obj = iterator.next(); if("Tom".equals(obj)){ iterator.remove(); // iterator.remove(); } } //遍歷集合 iterator = coll.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } }
-
-
jdk5.0新特性–增強for迴圈:(foreach迴圈)
- 遍歷集合舉例:內部仍然使用了迭代器
@Test public void test1(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person("Jerry",20)); coll.add(new String("Tom")); coll.add(false); //for(集合元素的型別 區域性變數 : 集合物件) for(Object obj : coll){ System.out.println(obj); } }
- 遍歷陣列舉例:
@Test public void test2(){ int[] arr = new int[]{1,2,3,4,5,6}; //for(陣列元素的型別 區域性變數 : 陣列物件) for(int i : arr){ System.out.println(i); } }
- 遍歷集合舉例:內部仍然使用了迭代器
四、Collection子介面:List介面
-
儲存的資料特點:儲存有序的、可重複的資料。
-
常用實現類:
- ArrayList:作為List介面的主要實現類;執行緒不安全的,效率高;底層使用Object[] elementData儲存
- LinkedList:對於頻繁的插入、刪除操作,使用此類效率比ArrayList高;底層使用雙向連結串列儲存
- Vector:作為List介面的古老實現類;執行緒安全的,效率低;底層使用Object[] elementData儲存
-
原始碼分析(難點)
-
ArrayList的原始碼分析:
-
jdk 7情況下
ArrayList list = new ArrayList();//底層建立了長度是10的Object[]陣列elementData
list.add(123);//elementData[0] = new Integer(123);
…
list.add(11);//如果此次的新增導致底層elementData陣列容量不夠,則擴容。
預設情況下,擴容為原來容量的1.5倍,同時需要將原有陣列中的資料複製到新陣列中
結論:建議開發中使用帶參的構造器:ArrayList list = new ArrayList(int capacity) -
jdk 8中ArrayList的變化:
ArrayList list = new ArrayList();//底層Object[] elementData初始化為{}.並沒建立長度為 10的陣列
list.add(123);//第一次呼叫add()時,底層才建立了長度10的陣列,並將資料123新增到elementData[0]
…
後續的新增和擴容操作與jdk 7 無異。 -
小結:jdk7中的ArrayList的物件的建立類似於單例的餓漢式,而jdk8中的ArrayList的物件的建立類似於單例的懶漢式,延遲了陣列的建立,節省記憶體。
-
-
LinkedList的原始碼分析:
LinkedList list = new LinkedList();//內部宣告瞭Node型別的first和last屬性,預設值為null
list.add(123);//將123封裝到Node中,建立了Node物件
其中,Node定義為:體現了LinkedList雙向連結串列的說法private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
-
Vector的原始碼分析:
jdk7和jdk8中通過Vector()構造器建立物件時,底層都建立了長度為10的陣列。
在擴容方面,預設擴容為原來的陣列長度的2倍。
-
-
List介面中的常用方法
-
void add(int index, Object ele):在index位置插入ele元素
ArrayList list = new ArrayList(); list.add(123); list.add(456); list.add("AA"); list.add(new Person("Tom",12)); list.add(456); System.out.println(list);//[123, 456, AA, Person{name='Tom', age=12}, 456] //void add(int index, Object ele):在index位置插入ele元素 list.add(1,"BB"); System.out.println(list);//[123, BB, 456, AA, Person{name='Tom', age=12}, 456]
-
boolean addAll(int index, Collection eles):從index位置開始將eles中的所有元素新增進來
//boolean addAll(int index, Collection eles):從index位置開始將eles中的所有元素新增進來 List list1 = Arrays.asList(1,2,3); list.addAll(list1); System.out.println(list.size());//9 System.out.println(list);//[123, BB, 456, AA, Person{name='Tom', age=12}, 456, 1, 2, 3]
-
Object get(int index):獲取指定index位置的元素
//Object get(int index):獲取指定index位置的元素 System.out.println(list.get(3));//3
-
int indexOf(Object obj):返回obj在集合中首次出現的位置
//int indexOf(Object obj):返回obj在集合中首次出現的位置,如果不存在返回-1 System.out.println(list.indexOf(456));//2 System.out.println(list.indexOf(789));//-1
-
int lastIndexOf(Object obj):返回obj在當前集合中末次出現的位置
//int lastIndexOf(Object obj):返回obj在當前集合中末次出現的位置 System.out.println(list.lastIndexOf(456));//5
-
Object remove(int index):移除指定index位置的元素,並返回此元素
// Object remove(int index):移除指定index位置的元素,並返回此元素 Object re = list.remove(1); System.out.println(re);//BB System.out.println(list);//[123, 456, AA, Person{name='Tom', age=12}, 456, 1, 2, 3]
-
Object set(int index, Object ele):設定指定index位置的元素為ele
// Object set(int index, Object ele):設定指定index位置的元素為ele.返回被替換的元素 Object re1 = list.set(1, "CC"); System.out.println(re1);//456 System.out.println(list);//[123, CC, AA, Person{name='Tom', age=12}, 456, 1, 2, 3]
-
List subList(int fromIndex, int toIndex):返回從fromIndex到toIndex位置的子集合
//List subList(int fromIndex, int toIndex):返回從fromIndex到toIndex位置的子集合, // 左閉右開,不包含toIndex位置,不會改變原有集合; System.out.println(list.subList(3, 5));//[Person{name='Tom', age=12}, 456] System.out.println(list);//[123, CC, AA, Person{name='Tom', age=12}, 456, 1, 2, 3]
總結:常用方法
增:add(Object obj)
刪:remove(int index) / remove(Object obj)
改:set(int index,Object obj)
查:get(int index)
插:add(int index,Object obj),add(int index,Collection coll)
長度:size()
遍歷:① Iterator迭代器方式
② 增強for迴圈:foreach
③ 普通的迴圈@Test public void test2(){ ArrayList list = new ArrayList(); list.add(123); list.add(456); list.add("AA"); //方式一:Iterator迭代器方式 Iterator iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } System.out.println("*****************************"); //方式二:增強for迴圈 for (Object o: list) { System.out.println(o); } System.out.println("*****************************"); //方式三:普通for迴圈 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } }
[面試題] * 面試題:ArrayList、LinkedList、Vector者的異同? * 同:三個類都是實現了List介面,儲存資料的特點相同:儲存序的、可重複的資料 * 不同:見上(第2部分+第3部分)
-
五、Collection子介面:Set介面
-
儲存的資料特點:無序的、不可重複的元素
以HashSet為例說明
1. 無序的:不等於隨機性。儲存的資料在底層陣列中並非按照陣列索引的順序新增,而是根據資料的雜湊值新增
2. 不可重複的:保證新增的元素按照equals()判斷時,不能返回true。即:相同的元素只能新增一個 -
元素新增過程: (以HashSet為例)
我們向HashSet中新增元素a,首先呼叫元素a所在類的hashCode()方法,計算元素a的雜湊值, 此雜湊值通過某種雜湊函式計算出在HashSet底層陣列中的存放位置(即為:索引位置),判斷 陣列此位置上是否已經有元素: 如果此位置上沒有其他元素,則元素a新增成功。---->情況1 如果此位置上有其他元素b(或以連結串列形式存在的多個元素),則比較元素a與元素b的hash值: 如果hash值不同,則元素a新增成功。---->情況2 如果hash值相同,進而需要呼叫元素a所在類的equals()方法: equals()返回true,元素a新增失敗 equals()返回false,則元素a新增成功。---->情況3 對於新增成功的情況2和情況3而言:元素a與已經存在指定索引位置上資料一連結串列的方式儲存。 jak 7:新新增的元素放到陣列中,指向原來的元素 jdk 8:原來的元素在陣列中,指向新新增的元素 HashSet底層:陣列+連結串列的結構(前提:jdk7)
-
常用方法
Set介面中沒額外定義新的方法,使用的都是Collection中宣告過的方法。 -
常用實現類:
-
HashSet:作為Set介面的主要實現類:執行緒不安全的;可以儲存null值
@Test public void test1(){ Set set = new HashSet(); set.add(456); set.add(123); set.add("AA"); set.add("CC"); set.add(new User("Tom",12)); set.add(new User("Tom",12));//體現不可重複性 set.add(129); Iterator iterator = set.iterator(); while (iterator.hasNext()){ System.out.print(iterator.next() + " ");//AA CC 129 456 123 User{name='Tom', age=12} } }
LinkedHashSet:作為HashSet的子類;遍歷其內部資料時,可以按照新增的順序遍歷。
在新增資料的同時,每個資料還維護了兩個引用,記錄此資料的前一個資料和後一個資料
對於頻繁的遍歷操作,LinkedHashSet效率高於HashSet@Test public void test2(){ Set set = new LinkedHashSet(); set.add(456); set.add(123); set.add("AA"); set.add("CC"); set.add(new User("Tom",12)); set.add(new User("Tom",12));//體現不可重複性 set.add(129); Iterator iterator = set.iterator(); while (iterator.hasNext()){ System.out.print(iterator.next() + " ");//456 123 AA CC User{name='Tom', age=12} 129 } }
要求:
- 向Set中新增的資料,其所在類一定要重寫hashCode()和equals()
- 重寫的hashCode()和equals()儘可能保持一致性:相等的物件必須具有相同的雜湊碼
重寫兩個方法的小技巧:物件中用作equals()方法比較的Filed,都應該用來計算hashCode值
-
TreeSet: 可以按照新增物件的指定屬性,進行排序
使用說明:-
向TreeSet中新增資料,要求是相同類的物件
-
兩種排序方式:
自然排序(實現Comparable介面) 中,比較兩個物件是否相同的標準為:compareTo()返回0,不再是equals()@Test public void test1(){ TreeSet set = new TreeSet(); //失敗:不能新增不同類的物件 // set.add(123); // set.add(456); // set.add("AA"); // set.add(new User("Tom",12)); //舉例一: // set.add(34); // set.add(-34); // set.add(43); // set.add(11); // set.add(8); //舉例二: set.add(new User("Tom",12)); set.add(new User("Jerry",32)); set.add(new User("Jim",2)); set.add(new User("Mike",65)); set.add(new User("Jack",33)); set.add(new User("Jack",56)); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } }
User.java
package com.zhkey.Learn1; /** * @author zhkey * @create 2020-12-08 */ public class User implements Comparable { private String name; private int age; public User() { } public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (age != user.age) return false; return name != null ? name.equals(user.name) : user.name == null; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; } //按照姓名從小到大排列,年齡從小到大排列 @Override public int compareTo(Object o) { if(o instanceof User){ User user = (User)o; // return this.name.compareTo(user.name); int compare = this.name.compareTo(user.name); if(compare != 0){ return compare; }else{ return Integer.compare(this.age,user.age); } }else{ throw new RuntimeException("輸入的型別不匹配"); } } }
定製排序中,比較兩個物件是否相同的標準為:compare()返回0,不再是equals()
@Test public void test2(){ Comparator com = new Comparator() { //照年齡從小到大排列 @Override public int compare(Object o1, Object o2) { if(o1 instanceof User && o2 instanceof User){ User u1 = (User)o1; User u2 = (User)o2; return Integer.compare(u1.getAge(),u2.getAge()); }else{ throw new RuntimeException("輸入的資料型別不匹配"); } } }; TreeSet treeSet = new TreeSet(com); treeSet.add(new User("Tom",12)); treeSet.add(new User("Jerry",18)); treeSet.add(new User("Leon",20)); treeSet.add(new User("Messi",15)); treeSet.add(new User("Jack",33)); treeSet.add(new User("Jack",55)); treeSet.add(new User("Marry",55)); Iterator iterator = treeSet.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } }
-
-
相關文章
- Java集合詳解8:Java集合類細節精講,細節決定成敗Java
- Java集合詳解2:一文讀懂Queue和LinkedListJava
- 一文看懂java io系統Java
- 一文看懂Java鎖機制Java
- java集合梳理【10】— Vector超級詳細原始碼分析Java原始碼
- Java集合詳解(一):全面理解Java集合Java
- vue3保證你看懂watch和watchEffect的詳細詳細使用Vue
- Java集合詳解(二)Java
- Java集合詳解(三)Java
- 一文快速瞭解Java集合框架Java框架
- java基礎詳解-集合Java
- 一文看懂 GithubGithub
- 【Java集合】單列集合Collection常用方法詳解Java
- 超詳細的Java面試題總結(三)之Java集合篇常見問題Java面試題
- 一文搞懂所有Java集合面試題Java面試題
- Java集合詳解7:一文搞清楚HashSet,TreeSet與LinkedHashSet的異同Java
- 從原始碼中學習Java集合中的List集合,詳細而透徹,一步到位原始碼Java
- Java IO流(詳細)Java
- Java中的併發集合詳解Java
- java集合學習(一):詳解ArrayListJava
- Java集合詳解3:一文讀懂Iterator,fail-fast機制與比較器JavaAIAST
- Java 集合系列1、細思極恐之ArrayListJava
- java運算子(超詳細!!!)Java
- java中cookie操作詳細JavaCookie
- 一文看懂 PHP 7.3 更新PHP
- ? 一文看懂 JS 繼承JS繼承
- 一文看懂個稅改革
- 一文搞定資訊打點——超詳細
- 一文了解Python反射機制(很詳細)Python反射
- Java 集合詳解 | 一篇文章解決Java 三大集合Java
- 一文看懂虛擬機器中Java物件的生死判別虛擬機Java物件
- Java集合詳解(五):Hashtable原理解析Java
- Java集合詳解(二):ArrayList原理解析Java
- Java集合詳解(三):HashMap原理解析JavaHashMap
- Java內功心法,Set集合的詳解Java
- Java集合詳解(三):LinkedList原理解析Java
- Java異常詳細介紹Java
- 一文看懂——SimSolid的優勢Solid