一:Collection集合

呼~,歷過好幾天的奮戰終於把集合框架肝完了,b站某馬老師講的是真的非常詳細而且動聽,原理給你分析得明明白白的,此前也找了許多關於集合這一大章節的視訊,發現更多的是針對於使用,原理講的並不是很多,這就導致我在練習或者回顧時還是一知半解。以下是我結合視訊以及個人的一些理解和體會做的筆記總結。路漫漫其修遠兮,吾將上下而求索,希望這篇總結對你有些許幫助,嘻嘻!

1.1集合概述:

集合:Java中提供的一種容器,可以用來儲存多個數據。java集合大致可以分為Set,List,Queue和Map四種體系。

陣列和集合的區別:

  • 陣列的長度是固定的。集合的長度是可變的。

  • 陣列儲存的是同一型別的資料,可以儲存基本資料型別值。容器能儲存物件,而且儲存物件的型別可以不一致。在開發中當物件多的時候,使用容器進行儲存。

1.2集合架構

單列集合體繫結構:

Collection介面是所有單列集合的父介面,因此在單列集合中定義的List和set通用的一些方法,這些方法可以操作所有的單列集合。方法如下:

1.3Collection集合常用方法

  • public boolean add(E e); 向集合中新增元素

  • public boolean remove(E e); 刪除集合中的某個元素

  • public void clear(); 清空集合中所有的元素

  • public boolean contains(); 判斷集合中是否含有xxx元素

  • publicboolean isEmpty(); 判斷集合是否為空

  • publicint size(); 計算集合的長度

  • public Object[] toArray(); 將集合轉成一個數組

【參考程式碼】

 package Collection;
 ​
 import java.util.ArrayList;
 import java.util.Collection;
 ​
 /*
 Collection集合常用方法
 boolean add(E e);                   向集合中新增元素
 boolean remove(E e);                 刪除集合中的某個元素
 void clear();                       清空集合中所有的元素
 boolean contains();                       判斷集合中是否含有xxx元素
 boolean isEmpty();                   判斷集合是否為空
 int size();                           計算集合的長度
 Object[] toArray();                       將集合轉成一個數組
  */
 public class Test {
     public static void main(String[] args) {
         //建立集合物件 , 可以多型使用
         Collection<String>col = new ArrayList<>();
 //     Collection<String>col = new HashSet<>();   下面的功能照樣能實現:共性方法
         col.add("小明"); // 新增元素
         col.add("小紅");
         col.add("小藍");
         col.add("小綠");
         System.out.println(col); //[小明, 小紅, 小藍, 小綠]
 ​
         //boolean remove(E e);     刪除集合中的某個元素
 //       boolean ans = col.remove("小明");
 //       System.out.println(ans);//true
 //       System.out.println(col);//[小紅, 小藍, 小綠]
 ​
         //void clear();             清空集合中所有的元素
 //       col.clear();
 //       System.out.println(col);//[]
 ​
         //boolean contains();       判斷集合中是否含有xxx元素
 //       boolean result = col.contains("小明");
 //       System.out.println(result);//true
 ​
         //boolean isEmpty();         判斷集合是否為空
 //       boolean result = col.isEmpty();
 //       System.out.println(result);// 不為空false
 ​
         //int size();             計算集合的長度
 //       int len = col.size();
 //       System.out.println(len);// 4
 ​
         //Object[] toArray();       將集合轉成一個數組
         Object[] arr = col.toArray();
         // 遍歷陣列
 //       for (int i = 0; i < arr.length; i++) {
 //           System.out.println(arr[i]);
 //       }
    }
 }

二:迭代器Iterator

引入:由於集合有多種,每種集合儲存跟讀取的方式都不一樣,好比衣櫃、水瓶、藥瓶,你存和取的方式肯定不一樣。如果每種集合都定義一種遍歷方式那將十分的繁瑣。

迭代器(Iterator):它不是一個容器而是介面,它是一種用於訪問容器的方法,可用於迭代 List、Set和Map等容器。

迭代:即Collection集合的通用獲取方式。再獲取元素之前先要判斷集合中是否有元素,如果有就將這個元素去取出來,繼續再判斷,直到集合所有元素被取出來為止。即:一個一個的往外拿。

作用:幫我們遍歷或者拿到容器裡邊的資料。

2.1Iterator介面

迭代器常用操作:

  1. next() 下一個

  2. hasNext() 判斷是否存在下一個元素

  3. remove() 刪除元素

迭代器的使用步驟:

  1. 使用集合中的的方法iterator()獲取迭代器的實現類物件,使用Iterator介面接收(多型)

  2. 使用Tterator介面中的方法hashnext()判斷還有沒有下一個元素

  3. 使用Tterator介面中的方法next()取出集合的下一個元素

【參考程式碼】

 package Iterator;
 ​
 import javax.swing.text.html.parser.Entity;
 import java.util.*;
 ​
 public class Test {
     public static void main(String[] args) {
         //建立一個集合物件
         Collection<String>col = new ArrayList();
         //新增元素
         col.add("小明");
         col.add("小紅");
         col.add("小藍");
         col.add("小綠");
 ​
         /*
         1.使用集合的方法iterator()獲取迭代器的實現類物件,使用Iterator介面接收(多型)
         注意:
             Iterator介面也是有泛型的,迭代器的泛型跟集合走,集合是什麼泛型,迭代器就是什麼泛型
          */
         // 多型 介面             實現類物件
         Iterator<String>it = col.iterator();
 ​
         // 2.使用 Iterator介面中的hashNext方法判斷是否還有下一個元素
         while(it.hasNext());{
         // 3.使用 Iterator介面中的next方法取出集合的下一個元素
         String str = it.next();
         System.out.println(str);        
        }
 ​
    }
 }

2.2Iterator的實現原理:

2.3增強for()

增強for迴圈(for each迴圈)是JDk1.5之後的一個高迴圈,專門用來遍歷陣列和集合的,所有的陣列跟單列集合都可以使用。它的內部原理就是一個迭代器Iterator,所以在遍歷過程中,不能對集合元素進行增刪操作。

語法:

 for(型別 變數 : 陣列/集合){// 陣列或者集合裡的每一項賦值給這個變數
  // 迴圈體
 }

【參考程式碼】

         String[] student = {"小明","小紅","小藍"};
 //       // 傳統遍歷方式
 //       for (int i = 0; i < student.length; i++) {
 //           System.out.println(student[i]);
 //       }
 ​
         // 增強for
         for(String c : student){
             System.out.println(c);
        }
 ​
  --------------------------------
         List<Integer>list = new ArrayList<Integer>();
         list.add(123);
         list.add(234);
         list.add(456);
         for(Integer n : list){
             System.out.println(n);
        }

注:增強for必須有被遍歷的目標。目標只能是陣列或者Collection,而它僅僅作為遍歷操作實現

2.4迭代器注意事項

  • 迭代器是一次性物件。我們不能重置迭代器,它不能被重用。

  • 要再次遍歷同一集合的元素,請通過呼叫集合的iterator()方法來建立一個新的Iterator。

三:泛型

3.1泛型概述

在前面學習集合時,我們知道集合時可以存放任意物件的,只要把物件儲存集合後,它們都會被向上轉型提升為Object型別。當我們要取出這些物件時必須進行型別強制轉換,由Object型別變為原來的型別。

3.2泛型的優缺點

不使用泛型:

 - 好處:集合預設型別是Object類,可以儲存任意型別的資料
 - 弊端:不安全,會引發異常,需要強轉。
 public static void main(String[] args) {
         List list = new ArrayList();
         list.add("小明");
         list.add("小紅");
         for (int i = 0; i < list.size(); i++) {
         String s= (String)list.get(i) // 強轉
             System.out.println(s);
        }
    }
 ​

使用泛型:

  • 好處:避免了型別強制轉化的麻煩,存的什麼型別,取出來的也是什麼型別;程式碼執行之後才會丟擲異常,寫程式碼時不會報錯

  • 弊端:泛型是什麼型別只能儲存什麼型別的資料。

 public static void main(String[] args) {
         List<String> list = new ArrayList();// 規範了資料型別,只能放字串!
         list.add("小明");
         list.add("小紅");
         //stringList.add(123);// 除了字串以外的型別不能加,報錯!
         for (int i = 0; i < list.size(); i++) {
        String s = list.get(i); // 不用再強轉了
             System.out.println(s);
        }
    }
 ​

在上述的例項中,我們只能新增String型別的資料,否則編譯器會報錯。

3.3泛型的定義與使用

泛型類

定義格式:

 修飾符 class 類名<泛型變數>{
 ​
 }
 // 注:泛型變數建議使用E、T、K、V

例如:

 public class Box<T> {
 
   private T t;
 
   public void add(T t) {
     this.t = t;
  }
 
   public T get() {
     return t;
  }

參考示例:

注:在建立物件時確定泛型的型別

泛型方法

定義格式:

 修飾符 <泛型變數> 返回值的型別 方法名稱(形參列表){
  //方法體
 }

注:含有泛型的方法,在呼叫的時候確定泛型的資料型別

傳遞什麼型別的引數,泛型就是什麼型別

參考示例:

泛型介面

定義格式:

 public interface 介面名<泛型型別> {
     
 }

使用方式1:定義介面的實現類,實現介面,並且指定介面的泛型

使用方式2:介面使用什麼泛型,實現類就使用什麼泛型,類跟著介面走。

就相當於定義了一個含有泛型的類,建立物件的時候確定泛型的型別。

下圖介面同上圖介面

3.4泛型的萬用字元

當使用泛型類或介面時,傳遞資料中,泛型型別不確定,可以通過萬用字元表示<?>表示。但一旦使用泛型的萬用字元後,只能使用Object類中的共性方法,集合中元素自身方法無法使用。

萬用字元的基本使用

泛型的萬用字元:不知道使用什麼型別來接收的時候,此時可以使用 ? ,?表示未知萬用字元

此時只能接收資料,不能往集合中儲存資料。

【參考程式碼】

 package FanXing;
 ​
 import javax.swing.text.html.HTMLDocument;
 import java.util.ArrayList;
 import java.util.Iterator;
 /*
     泛型的萬用字元:
         ?:代表資料型別
     使用方式:
         不能在建立物件時使用
         只能作為方法的傳遞引數使用
  */
 public class Generic {
     public static void main(String[] args) {
 ​
         ArrayList<Integer>list01 = new ArrayList<>();
         list01.add(123);
         list01.add(456);
 ​
         ArrayList<String>list02 = new ArrayList<>();
         list02.add("小明");
         list02.add("小紅");
 // ......還有很多其它型別
         printArray(list01);
         printArray(list02);
         /*
             定義一個方法,能遍歷所有型別的ArrayList集合
             這時候我們不知道ArrayList集合使用的是什麼型別,可以使用泛型的萬用字元:?來代表資料型別
             注意:泛型沒有繼承的概念
          */
    }
 ​
     public static void printArray(ArrayList<?>list){
         // 使用迭代器遍歷集合
        Iterator<?> it = list.iterator();
        while(it.hasNext()){
            Object obj = it.next();//it.next()取出的元素是Object類。Object類 可以接收任意的資料型別
            System.out.println(obj);
        }
 ​
    }
 }

萬用字元高階使用-----受限泛型

之前設定泛型的時候,實際上是可以可以任意設定的,只要是類就可以設定。但在Java的泛型中可以指定一個泛型的上限和下限。

泛型的上限:

  • 格式:型別名稱<? extends E >物件名稱 代表的泛型只能是E型別的子類/本身

  • 意義:只能接收該型別及其子集

泛型的下限:

  • 格式:型別名稱<? super E >物件名稱 代表的泛型只能是E型別的父類/本身

  • 意義:只能接收該型別及其父類

比如:Object類、String類、Number類、Integer類,其中Number類是Integer的父類。

四:Java常見資料結構

集合是基於資料結構做出來的,不同的集合底層採用不同的資料結構。不同的資料結構,功能和作用是不一樣的。

資料結構是指資料以什麼方式組織在一起。不同的資料結構,增刪查的效能是不一樣的。

41棧

棧:stack,又稱堆疊,它是運算受限的線性表,只能在棧的受限一端進行插入和刪除操作。

特點:先進後出

4.2佇列

佇列:queue,簡稱隊,它同棧由於也是運算受限的線性表,只能在表的一端進行插入操作,而在表的另一端進行刪除操作。

特點:先進先出

4.3陣列

陣列:Array,是個有序的元素序列,陣列在記憶體中開闢一段連續的空間。

特點:

  • 查詢快:隨機存取,通過索引可以快速訪問元素

  • 增刪慢:靜態分配記憶體,陣列的長度是固定,存在空間閒置或者溢位現象;不適合進行插入和刪除操作,需要移動大量元素。

4.4連結串列

連結串列:linked list,由一系列結點node組成,結點可以在執行時動態產生。每個節點包含兩個部分:資料域(data)和指向下一個節點的指標域(next)。連結串列包括單鏈表和雙向連結串列。

  • 單鏈表:連結串列中只有一條鏈子,不能保證元素的順序(儲存和取出的順序可能不一致)

  • 雙向連結串列:連結串列中只有兩條鏈子,有一條鏈子專門記錄元素的順序,是一個有序的集合。

特點:

  • 查詢慢:連結串列的地址不是連續的,每次查詢都要從頭到尾進行遍歷。

  • 增刪快:動態分派記憶體,增/刪一個節點對於連結串列整體結構沒有影響,增刪操作效率高。

4.5紅黑樹

紅黑樹:R-B Tree,全稱是Red-Black Tree,又稱為“紅黑樹”,它一種特殊的二叉查詢樹。紅黑樹的每個節點上都有儲存位表示節點的顏色,可以是紅(Red)或黑(Black),它是一種弱平衡二叉樹(Weak AVL)。

特點:

(1)每個節點或者是黑色,或者是紅色。 (2)根節點是黑色。 (3)每個葉子節點(NIL)是黑色。 [注意:這裡葉子節點,是指為空(NIL或NULL)的葉子節點!] (4)如果一個節點是紅色的,則它的子節點必須是黑色的。 (5)從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。

注:以上資料結構可以結合所學過c語言資料結構

五:List集合體系

5.1List概述

List集合體系:新增元素,是有序,可重複,有索引的,大小可變。實際開發中常用的是ArrayList集合。List集合體系包括以下幾種:

  • ArrayList——新增元素,是有序,可重複,有索引的。

  • LinkedList——新增元素,是有序,可重複,有索引的。

  • Vector——查詢快,增刪慢;執行效率慢、執行緒安全

List集合繼承了Collection集合的全部功能,同時因為List集合系列有索引,所以多了很多按照索引操作元素的方法:

 add(int index, E element) 根據索引新增元素
 get(int index) 根據索引獲取元素
 remove(int index) 根據索引刪除元素
 set(int index, E element) 根據索引修改該位置上的元素
 contains(E element)判斷容器是否含有XXX東西
 clear() 清空集合中的元素
 size()計算集合的大小

【參考程式碼】

 package Collection;
 ​
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 ​
 public class TestList {
     public static void main(String[] args) {
         List<String>list = new ArrayList();
         // 換成Linkedist 下面的操作都能一樣實現
 ​
         list.add("小明");
         list.add("小紅");
         list.add("小藍");
         list.add("小綠");
         list.add("小明");
 ​
 ​
 //       // 在某個索引位置往集合中新增元素
 //       list.add(2,"哈哈哈哈");
 //       System.out.println(list);
 ​
 //       // 刪除集合中某個元素
 //       list.remove("小藍");
 ​
 ​
 //       // 根據索引獲取元素
 //       System.out.println(list.get(0));
 ​
 //       // 修改索引位置處的元素
 //       list.set(0,"小明很明白!");
 //       System.out.println(list.get(0));//小明很明白!
 ​
 //       // 計算列表的大小(長度):
 //       System.out.println(list.size());
 ​
 //       //判斷列表中是否有xxx false
 //       System.out.println(list.contains("小藍"));
 ​
    }
 }

5.2List遍歷方式

  1. for迴圈

     // 遍歷列表
     for (int i = 0; i < list.size(); i++) {
         String str = list.get(i);
         System.out.println(str);
     ​
     }
  2. 迭代器

     Iterator<String>it = list.iterator(); // 建立一個List的迭代器
     ​
     while(it.hasNext()){// 判斷有沒有下一個元素
      String s = it.next();
      System.out.println(s);
     }
  3. 增強for

     List<String>list = new ArrayList<>();
     ​
         for(String s : list){
             System.out.println(s);
        }
  4. Lambda表示式(瞭解)

     list.foreach(s -> {
           System.out.println(s);
     });

5.3ArrayList集合

ArrayList集合儲存的結構是陣列結構,元素增刪慢,查詢快。最常用。

5.4LinkedList集合

LinkedList集合儲存的結構是連結串列結構,方便元素的新增、刪除操作。LinkedList是一個雙向連結串列

LinkedList的特點:

  1. 底層是一個連結串列結構:查詢慢,增刪快

  2. 裡邊含有大量操作首尾元素的方法

  3. 注:使用LinkedList集合特有的方法,不能使用多型,命名要注意了!

實際開發中對一個集合的新增、刪除操作經常涉及首尾操作,LinkedList提供了很多操作首尾元素方法

 public void addFirst(E e); 將指定的元素插到列表開頭。
 public void addLat(E e); 將指定的元素插到列表結尾。 此方法等效於add()方法
 public void push(E e); 將元素推入此列表所示的堆疊。 此方法等效於addFirst()方法
 ​
 public E getFirst(); 返回此列表的第一個元素
 public E getLast(); 返回此列表的最後一個元素
 ​
 public E removeFirst(); 移除並返回此列表的第一個元素
 public E removeLast(); 移除並返回此列表的最後一個元素
 public E pop(E e); 入此列表所示的堆疊中彈出一個元素。
 ​
 public boolean isEmpty(); 如果列表為空 返回true

【參考程式碼】

 package Collection;
 /*
 public void addFirst(E e); 將指定的元素插到列表開頭。
 public void addLast(E e); 將指定的元素插到列表結尾。
 public void push(E e); 將元素推入此列表所示的堆疊。
 ​
 public E getFrist(); 返回此列表的第一個元素
 public E getLast(); 返回此列表的最後一個元素
 ​
 public E removeFrist(); 移除並返回此列表的第一個元素
 public E removeLast(); 移除並返回此列表的最後一個元素
 public E pop(E e); 入此列表所示的堆疊中彈出一個元素。
 ​
 public boolean isEmpty(); 如果列表為空 返回true
  */
 ​
 import java.util.LinkedList;
 import java.util.List;
 ​
 public class TestLinkedList {
     public static void main(String[] args) {
     show01();
     show02();
     show03();
    }
 ​
     /*
     public void addFirst(E e); 將指定的元素插到列表開頭。
     public void addLast(E e); 將指定的元素插到列表結尾。
     public void push(E e); 將元素推入此列表所示的堆疊
      */
     public static void show01(){
 //       注:LinkedList特有的方法不能使用多型!
 //       List<String> list = new LinkedList<>(); 是不對的
         LinkedList<String>list = new LinkedList<>();
 ​
         // add()新增元素
         list.add("a");
         list.add("b");
         list.add("c");
         System.out.println(list);//[a, b, c]
 ​
         list.addFirst("hhh");
         //public void push(E e); 將元素推入此列表所示的堆疊。 等效於addFirst()
         list.push("hhh");
         System.out.println(list);
 ​
         //public void lastFrist(E e); 將指定的元素插到列表結尾。 等效於add()
         list.addLast("com");
         System.out.println(list);
 ​
    }
     /*
     public E getFrist(); 返回此列表的第一個元素
     public E getLast(); 返回此列表的最後一個元素
      */
     public static void show02(){
         LinkedList<String>list = new LinkedList<>();
         // add()新增元素
         list.add("a");
         list.add("b");
         list.add("c");
 //       list.clear(); // 清空集合中所有元素
         if(! list.isEmpty()){
             System.out.println(list.getFirst());//a
             System.out.println(list.getLast());//c
        }
    }
     /*
     public E removeFrist(); 移除並返回此列表的第一個元素
     public E removeLast(); 移除並返回此列表的最後一個元素
     public E pop(E e); 入此列表所示的堆疊中彈出一個元素。
      */
     public static void show03(){
         LinkedList<String>list = new LinkedList<>();
         // add()新增元素
         list.add("a");
         list.add("b");
         list.add("c");
         System.out.println(list.pop());
         //public E pop(E e); 入此列表所示的堆疊中彈出一個元素。 等效於 removefirst()
         //System.out.println(list.pop());  
         System.out.println(list.removeFirst());//a
         System.out.println(list.removeLast());//c
         System.out.println(list);//[b]
    }
 }

注:使用LinkedList集合特有的方法,不能使用多型。

5.5Vector集合(瞭解)

陣列結構實現,查詢快,增刪慢;

JDK1.0版本,執行效率慢、執行緒安全

【參考程式碼】

 package Collection;
 ​
 import javax.swing.text.html.HTMLDocument;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.Vector;
 ​
 /*
     Vector集合的使用
         儲存結構:陣列
  */
 public class VectorTest {
     public static void main(String[] args) {
         // 建立集合
         Vector<String>vector = new Vector<>();
         // 新增元素
         vector.add("小明");
         vector.add("小紅");
         vector.add("小藍");
         System.out.println("元素個數"+ vector.size());
         // 判斷
         System.out.println(vector.contains("小明"));
         System.out.println(vector.isEmpty());
         //刪除
         vector.remove("小紅");
         System.out.println(vector);
         //清空 clear(); vector.clear();
         // 遍歷
         Iterator<String> it = vector.iterator();
         while (it.hasNext()){
             String str = it.next();
             System.out.println(str);
        }
         //vector獨有的遍歷 使用列舉器
 //       Enumeration<String>en = vector.elements();
 //       while (en.hasMoreElements()){
 //           String s = en.nextElement();
 //           System.out.println(s);
 //       }
    }
 }

六:Set系列集合

6.1概述

Set系列集合:新增的元素,是無序的,不重複的,無索引的(索引的操作不能用)。

——HashSet:新增的元素,是無序的,不重複的,無索引的。

——LinkedHashSet:新增的元素,是有序的,不重複的,無索引的。

——TreeSet:不重複,無索引,按照大小預設升序排序!!(可排序集合)

遍歷方式:由於Set集合五索引,故沒有for迴圈遍歷,只有三種遍歷。

6.2Set集合儲存元素不重複的原理

注:儲存的字串,Integer等型別的資料,它們是Java已經定義好了類,它們都重寫了hashCode方法和equals方法,保證了元素的唯一性!

HashSet 保證元素唯一性的原理

我們使用 Set 集合都是需要去掉重複元素的, 如果在儲存的時候逐個 equals() 比較, 效率較低,雜湊演算法提高了去重複的效率, 降低了使用 equals() 方法的次數。

當 HashSet 呼叫 add() 方法儲存物件的時候, 先呼叫物件的 hashCode() 方法得到一個雜湊值, 然後在集合中查詢是否有雜湊值相同的物件,如果沒有雜湊值相同的物件就直接存入集合。如果有雜湊值相同的物件, 就和雜湊值相同的物件逐個進行 equals() 比較,比較結果為 false 就存入, true 則不存。儲存元素必需要重寫HashCode方法和equals方法

6.3HashSet儲存自定義型別元素

給HashSet中存放自定義的型別時,必需要重寫HashCode方法和equals方法,建立自己的比較方式,才能保證HashSet集合中物件的唯一性!

【參考程式碼】

Person類:

 package Collection;
 ​
 import java.util.Objects;
 ​
 public class Person {
     private String name;
     private int age;
 ​
     public Person(String name, int age) {
         this.name = name;
         this.age = age;
    }
 ​
     // 用於列印
     @Override
     public String toString() {
         return "Person{" +
                 "name='" + name + '\'' +
                 ", age=" + age +
                 '}';
    }
  // 重寫hashCode方法和equals方法
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         Person person = (Person) o;
         return age == person.age && Objects.equals(name, person.name);
    }
 ​
     @Override
     public int hashCode() {
         return Objects.hash(name, age);
    }
 ​
     public String getName() {
         return name;
    }
 ​
     public void setName(String name) {
         this.name = name;
    }
 ​
     public int getAge() {
         return age;
    }
 ​
     public void setAge(int age) {
         this.age = age;
    }
 }
 // 主控制檯
 package Collection;
 ​
 import java.util.HashSet;
 import java.util.Set;
 /*
     HashSet儲存自定義型別的元素
 ​
     Set集合保證元素唯一性:
         儲存的元素(String Integer,...Student,Person...) 必須重寫hashCode方法和equals方法
     要求:
         同名且同年齡視為同一個人噢
  */
 public class TestHaxhSet {
     public static void main(String[] args) {
         // 建立hashSet集合儲存Person
         Set<Person>set = new HashSet<>();
        //集合類存放物件的!
         // 建立物件(人)
 /*
        // 在沒有重寫hashCode方法和equals方法前,它們的雜湊值都是不一樣的,equals也為false 故沒有重複
         Person p1 = new Person("小明",18);
         Person p2 = new Person("小明",19);
         Person p3 = new Person("小明",18);
         System.out.println(p1.hashCode());// 460141958
         System.out.println(p2.hashCode());// 1163157884
         System.out.println(p3.hashCode());// 1956725890
         System.out.println(p1.equals(p2));// false
         set.add(p1);
         set.add(p2);
         set.add(p3);
         System.out.println(set);// [Person{name='小明', age=18}, Person{name='小明', age=19}, Person{name='小明', age=18}]
 */
         // 重寫hashCode方法和equals方法之後set物件就唯一性了
         Person p1 = new Person("小明",18);
         Person p2 = new Person("小明",19);
         Person p3 = new Person("小明",18);
         set.add(p1);
         set.add(p2);
         set.add(p3);
         System.out.println(set);// [Person{name='小明', age=19}, Person{name='小明', age=18}]
 ​
    }
 }

6.4LinkedHashSet集合

我們知道HashSet保證元素的唯一性,但存放進去的元素是無序的,那我們要保證有序,該怎麼辦好呢?

在HashSet下面的一個子類Java.util.LinkedHashSet。它是連結串列和雜湊表組合的一個數據結構。

LinkedHashSet集合的特點:

底層是一個雜湊表(陣列+連結串列/紅黑樹)+連結串列:多了一條連結串列(記錄元素的儲存順序),保證元素有序

具有可預知迭代順序的 Set 介面的雜湊表和連結列表實現,即按照將元素插入到 set 中的順序(插入順序)進行迭代

HashSet與LinkedHashSet的區別:

【參考程式碼】

 package Collection;
 ​
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Set;
 ​
 public class TestHashSet {
     public static void main(String[] args) {
         Set<String>set = new HashSet<>();
         set.add("kkk");
         set.add("abc");
         set.add("abc");
         set.add("afterglow");
         System.out.println(set);//[afterglow, abc, kkk] 無序,不重複
 ​
         Set<String>Linkset = new LinkedHashSet<>();
         Linkset.add("kkk");
         Linkset.add("abc");
         Linkset.add("abc");
         Linkset.add("afterglow");
         System.out.println(Linkset);//[kkk, abc, afterglow] 有序,不重複
    }
 ​
 }

6.5可變引數

使用前提:

如果我們定義一個方法需要接收多個引數,並且多個引數型別一致,我們可以對其做如下格式的簡化:

修飾符 返回值型別 方法名(引數型別... 形參名){ }

這個寫法完全等價於:

修飾符 返回值型別 方法名(引數型別[] 形參名){ } ,

後者在呼叫時必須傳遞陣列,而前者可以直接傳遞資料型別。

可變引數原理:

可變引數底層是一個數組,根據引數個數不同,會建立不同長度的陣列來儲存這些引數。傳遞引數的個數,可以是0~n個

【參考程式碼】

 package Collection;
 ​
 public class KeBiancanShu {
     public static void main(String[] args) {
         int i =add(1,2,3,4);
         System.out.println(i);
    }
 ​
 //   // 兩個數的和
 //   public static int add(int a, int b){
 //       return a + b;
 //   }
 //   // 三個數的和,要是多個一直往下寫,很麻煩!
 //   public static int add(int a, int b, int c){
 //       return a + b +c;
 //   }
 ​
     /*    
         求0~n個整數的和
         資料型別已經確定:int
         引數個數不確定,可以使用可變引數
 ​
      */
     public static int add(int...arr){
 //       System.out.println(arr);// [I@1b6d3586 底層是一個數組
 //       System.out.println(arr.length);// 可變陣列的長度,卻決於你新增的個數
         int sum = 0;
         for (int i : arr){
            sum += i;
        }
         return sum;
    }
 }

注意事項:

  • 一個方法的引數列表,只能有一個可變引數

  • 如果方法的引數有多個,那麼可變引數必須寫在引數列表的末尾!

【示例程式碼】

     /*
     可變引數注意事項:
         一個方法的引數列表,只能有一個可變引數
         如果方法的引數有多個,那麼可變引數必須寫在引數列表的末尾!
      */
     //一個方法的引數列表,只能有一個可變引數
 //   public static void method01(int...a,String...b){ 報錯!
 //         }
     
     //如果方法的引數有多個,那麼可變引數必須寫在引數列表的末尾!
     public static void method02(String b, double c, int...a){
         
    }

七:Collections工具類

7.1常用方法:

  • Java.utils.collections 是集合工具類,用於對集合進行操作。常用功能如下:

  • max() / min() :求集合的最大 / 小值

  • public static <T> boolenan addAll(Collect<T> c , T. . . elements) :往集合中新增一些元素

  • public static void shuffle(List<?> list) :打亂集合順序

  • public static void sort(List<T> list) :將集合按照預設規則(升序)進行排序

  • public static void sort(List<T> list , Comparator<? super T >) ;將集合按照指定的規則進行排序

【參考程式碼】

 public class Test {
     public static void main(String[] args) {
         List<Integer>list = new ArrayList<Integer>();
         list.add(120);
         list.add(20);
         list.add(220);
 ​
         // 求最值
         Integer max = Collections.max(list);
         System.out.println(max);
         Integer min = Collections.min(list);
         System.out.println(min);
         // 排序
         Collections.sort(list);
         System.out.println(list);
         // 打亂順序
         Collections.shuffle(list); // 鬥地主發牌
         System.out.println(list);
         // 不定引數新增
         Collections.addAll(list,456,789);
         System.out.println(list);//[220, 20, 120, 456, 789]
         
    }
 }

sort(List < T > list)使用

注意:

sort(List<T> list)使用前提:

排序的集合裡邊儲存的元素,必須實現Comparable介面,重寫介面中的方法compareTo定義排序的規則。在Java中Integer、String等等資料型別已經幫我們實現Comparable介面並重寫介面中的方法compareTo了。如果要對自己定義的類進行排序,我們就要自己實現介面並重寫compareTo然後進行自定義排序規則。

Comparable介面的排序規則:

自己(this) - 引數:升序,反之降序

【示例參考】:比較自定義型別

輸出結果:

[Student{name='小明', age=18}, Student{name='小紅', age=20}, Student{name='小藍', age=19}] [Student{name='小明', age=18}, Student{name='小藍', age=19}, Student{name='小紅', age=20}]

sort(List< T > list , Comparator<? super T >)

sort(List< T > list , Comparator<? super T >)的使用:

Comparator:相當於找一個第三放的裁判,按照Comparator比較器裡面重寫的compare方法對元素進行排序比較

Comparator的比較規則:

o1 - o2 升序

【參考程式碼】

 public class TestComparator {
     public static void main(String[] args) {
         List<Integer> list = new ArrayList<>();
         list.add(2);
         list.add(1);
         list.add(3);
 ​
         Collections.sort(list, new Comparator<Integer>() {
             @Override
             public int compare(Integer o1, Integer o2) {
                 return o1 - o2;// 升序
 //               return o2 - o1;// 降序
            }
        });
 ​
         System.out.println(list);// [1, 2, 3]
    }
 }

【示例參考】:比較自定義型別

Comparator和Comparable的區別

  • Comparable:自己(this)和別人(引數)比較,自己需要實現Comparable介面,重寫比較規則compareTo方法

  • Comparator:相當於找一個第三放的裁判,按照Comparator比較器裡面重寫的compare方法對元素進行排序比較

八:Map集合

8.1概述

Map集合的特點

  1. Map是一個雙列集合,其中每個元素表示一個鍵值對作為<key,value> ;

  2. Map集合中的元素,key和value的資料型別可以相同,也可以不同

  3. Map集合中的元素,key不允許出現重複,value可以重複

  4. Map集合中的元素,key和value是一一對應的(對映)

注:對映由Map<K,V>介面的例項表示,它不是繼承自Collection介面。

8.2Map集合常見子類

Map系列集合,常用子類的包括:

——HashMap

——LinkedHashMap

【HashMap集合】

java.util.HashMap<k , v >集合implements Map<k , v>介面.

HashMap集合的特點:

  1. HashMap底層是雜湊表:查詢速度特別快

    JDK1.8之前:陣列 + 單項鍊表

    JDK1.8之後:陣列 + 單項鍊表/紅黑樹(連結串列長度超過8):提高查詢速

  2. HashMap集合是一個無序的集合,儲存元素和取出元素的順序有可能不一致

【LinkedHashMap集合】

java.util.LinkedHashMap<k , v >集合extends HashMap<k , v>集合。

LinkedHashMap集合的特點:

  1. LinkedHashMap底層是雜湊表 + 連結串列(保證迭代的順序)

  2. HashMap集合是一個有序的集合,儲存元素和取出元素的順序是一致的

8.3Map介面中的常用方法

Map介面中定義了很多方法,常見如下:

  • public V put(K key , V value):把指定的鍵(key)和指定的值(value)新增到Map集合中

  • public V remove(Object key):把指定的key所對應的value從Map集合中刪除,返回被刪元素的值

  • public V get(Object key):在集合中獲取指定key對應value的元素

  • boolean containsKey(Object key):判斷集合中是否含有xxxkey

  • boolean containsValue(Object key):判斷集合中是否含有xxxvalue

  • public Set<K> KeySet():把Map中所有的key打包成(儲存到)set集合返回

  • public Set< Map.Entry<K,V> > entrySet():獲取Map中所有key和value物件的集合(Entry)儲存在集合Set中

【參考程式碼】

 package Map;
 ​
 import java.util.HashMap;
 import java.util.Map;
 ​
 public class Test {
     public static void main(String[] args) {
  // 建立Map集合物件,多型
         Map<Integer,String>map = new HashMap();
         map.put(11,"小明");
         map.put(22,"小紅");
         map.put(33,"小藍");
         map.put(44,"小綠");
         System.out.println(map);// {33=小藍, 22=小紅, 11=小明, 44=小綠} HashMap無序的
 ​
         map.remove(44);// 刪除
         System.out.println(map);// {33=小藍, 22=小紅, 11=小明}
 ​
         System.out.println(map.size()); //大小 3
 ​
         System.out.println(map.containsKey(33)); //true
         System.out.println(map.containsValue("小藍")); //true
 ​
         map.put(22,"小芳"); // {33=小藍, 22=小芳, 11=小明} 若出現重複的key原來的資料會被頂替
         System.out.println(map);
 //       map.put(55,"小明");
 //       System.out.println(map);//是否被頂替卻決於key,key對映value,而不是value對映key {33=小藍, 22=小芳, 55=小明, 11=小明}
 ​
         System.out.println(map.keySet()); // [33, 22, 11] 把map中的key打包成Set集合的形式
 ​
         System.out.println(map.get(33));// 小藍 通過key查詢value
    }
 }
 ​

8.4Map的遍歷

方法一:通過鍵找值的方式

  1. 使用Map集合中的方法keySet(),把Map集合裡所有的key取出來,存放到一個Set集合中

  2. 遍歷set集合,獲取Map集合中的每一個key

  3. 通過Map集合中的get(key)方法,找到value

【參考程式碼】

 package Map;
 ​
 import javax.swing.text.html.HTMLDocument;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 ​
 public class Test {
     public static void main(String[] args) {
         // 建立Map集合物件
         Map<String,Integer>map = new HashMap<>();
         map.put("小明",18);
         map.put("小紅",18);
         map.put("小藍",19);
         map.put("小綠",20);
 ​
         //1. 使用Map集合中的方法keySet(),把Map集合裡所有的key取出來,存放到一個Set集合中\
         Set<String> set = map.keySet();
         
         //2.遍歷set集合,獲取Map集合中的每一個key
         /* 使用while遍歷 */
         Iterator <String> it = set.iterator();
         while (it.hasNext()){
             String key = it.next();
             
             //3.通過Map集合中的get(key)方法,找到value
             Integer value = map.get(key);
             System.out.println(key+"="+value);
        }
         System.out.println("-----------------------");
         /* 使用增強for遍歷 */
         for(String key : set){
             //3.通過Map集合中的get(key)方法,找到value
             Integer value = map.get(key);
             
             System.out.println(key+"="+value);
        }
 ​
    }
 }

【總結】:

while——迭代器遍歷:

     Set<String> set = map.keySet();
     Iterator <String> it = set.iterator();
     
     while (it.hasNext()){
         String key = it.next();
         Integer value = map.get(key);
         System.out.println(key+"="+value);
    }

增強for遍歷:

     Set<String> set = map.keySet();
     
     for(String key : set){
         //3.通過Map集合中的get(key)方法,找到value
         Integer value = map.get(key);
         System.out.println(key+"="+value);
    }

方法二:鍵值對的方式遍歷(更加面向物件)

把鍵值對當成一個整體遍歷,增強for無法遍歷,這個整體不是型別,因此Java提供了方法:

Map集合通過程式碼Set<Map.Entry<K,V>> ,將鍵值對元素轉成了一個實體型別,此時得到的是一個Entry物件,型別是:Map.Entry<K,V>

  1. 通過Map集合中的entrySet()方法,把Map集合中的多個Entry物件取出來,儲存到一個Set集合中

  2. 此時鍵值對元素的型別就確定了,型別是鍵值對實體型別:Map.Entry<K,V>

  3. 接下來就可以用增強for遍歷了

【參考程式碼】

 package Map;
 ​
 import javax.swing.text.html.HTMLDocument;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 ​
 public class Test {
     public static void main(String[] args) {
         // 建立Map集合物件
         Map<String,Integer>map = new HashMap<>();
         map.put("小明",18);
         map.put("小紅",18);
         map.put("小藍",19);
         map.put("小綠",20);
 ​
         //1.通過Map集合中的entrySet()方法,把Map集合中的多個Entry物件取出來,儲存到一個Set集合中
         Set<Map.Entry<String,Integer>> set = map.entrySet();
 ​
         //遍歷set集合,獲取每一個Entry物件
         //使用迭代器遍歷set集合
         Iterator <Map.Entry<String,Integer>> it = set.iterator();
         while (it.hasNext()){
             Map.Entry<String,Integer>entry = it.next();
             // 使用Entry物件中的getKey()和getValue()方法獲取鍵和值
             String key = entry.getKey();
             Integer value = entry.getValue();
             System.out.println(key+"="+value);
        }
         System.out.println("-----------");
 ​
         //增強for
         for(Map.Entry<String,Integer> entry : set){
             // 使用Entry物件中的getKey()和getValue()方法獲取鍵和值
             String key = entry.getKey();
             Integer value = entry.getValue();
             System.out.println(key+"="+value);
        }
    }
 }

總結】:

while——迭代器遍歷:

         Set<Map.Entry<String,Integer>> set = map.entrySet();
 ​
         //遍歷set集合,獲取每一個Entry物件
         //使用迭代器遍歷set集合
         Iterator <Map.Entry<String,Integer>> it = set.iterator();
         while (it.hasNext()){
             Map.Entry<String,Integer>entry = it.next();
             
             // 使用Entry物件中的getKey()和getValue()方法獲取鍵和值
             String key = entry.getKey();
             Integer value = entry.getValue();
             System.out.println(key+"="+value);
        }

增強for遍歷:

      //增強for
         for(Map.Entry<String,Integer> entry : set){
             // 使用Entry物件中的getKey()和getValue()方法獲取鍵和值
             String key = entry.getKey();
             Integer value = entry.getValue();
             System.out.println(key+"="+value);
        }

Entry:表示一個key和value,它提供了獲取對應key和value的方法:

public K getKey():獲取Entry中的key

public V getValue():獲取Entry中的value

方法二圖解:

8.5HashMap儲存自定義型別鍵值

練習:每位學生(姓名,年齡)都有自己的家庭住址。那麼,既然有對應關係,則將學生物件和家庭住址儲存到map集合中,學生作為鍵,地址為值。

注:學生姓名、年齡相同則視為同一人

 package Map;
 /*
     hashMap儲存自定義型別鍵值:
     Map集合保證key是唯一的:
         作為key元素,必須重寫hashMap方法和equals方法,以保證key唯一
  */
 ​
 import java.util.HashMap;
 import java.util.Set;
 ​
 public class HashMapSavePerson {
     public static void main(String[] args) {
         show01();
         /*
             上海-->HashMapSavePerson{name='小藍', age=18}
             深圳-->HashMapSavePerson{name='小綠', age=18}
             北京-->HashMapSavePerson{name='小紅', age=18}
             
             key唯一
          */
    }
     
         /*
            hashMap儲存自定義型別鍵值:
              key:String型別
                String類重寫hashCode方法和equals方法,可以保證key唯一
             value:Person型別
                value可以重複(同名同年齡視為重複)
          */
 ​
 ​
     public static void show01(){
 ​
         // 創造HashMap集合
         HashMap<String,Person> map = new HashMap<>();
         //往集合中新增元素
         map.put("深圳",new Person("小明",18));
         map.put("上海",new Person("小藍",18));
         map.put("北京",new Person("小紅",18));
         map.put("深圳",new Person("小綠",18));
         // 使用keySet()增強for遍歷map集合
         Set<String> set = map.keySet();
         for(String key:set){
             Person value = map.get(key);
             System.out.println(key+"-->"+value);
             // 因為字串類(Java幫我們的)重寫了hashCode方法和equals方法,所以鍵(key)是不能重複的
        }
         
    }
     
 }

Person類:

下面這個是我們自己定義的key的型別,Person類,上面例子的是String類:

 package Map;
 /*
     hashMap儲存自定義型別鍵值:
     Map集合保證key是唯一的:
         作為key元素,必須重寫hashMap方法和equals方法,以保證key唯一
  */
 ​
 import javax.swing.text.html.HTMLDocument;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 ​
 public class HashMapSavePerson {
     public static void main(String[] args) {
         
         show02();
    }
           /*
            hashMap儲存自定義型別鍵值:
              key:Person型別
                Person就必須類重寫hashCode方法和equals方法,來保證key唯一
             value:String型別
                value可以重複(同名同年齡視為重複)
          */
 ​
     public static void show02(){
         // 創造HashMap集合
         HashMap<Person,String> map02 = new HashMap<>();
         // 往集合中新增元素
         map02.put(new Person("張三",18),"法外狂徒");
         map02.put(new Person("黃老闆",18),"英國");
         map02.put(new Person("陳奕迅",18),"中國");
         map02.put(new Person("張三",18),"法外狂徒");
 ​
        // 使用迭代器遍歷set集合中的Entry物件
         Set<Map.Entry<Person,String>> set = map02.entrySet();
         Iterator<Map.Entry<Person,String>> it = set.iterator();
         while(it.hasNext()){
 ​
             Map.Entry<Person,String> entry = it.next();
 ​
             Person key = entry.getKey();
             String value = entry.getValue();
             System.out.println(key+"--->"+value);
        }
    }
 ​
 }

這裡再介紹一下本例中Entry物件遍歷的圖解,再次加深印象:

8.6LinkedHashMap集合

我們知道HashMap保證key唯一,並且查詢速度快,可是成對元素存放進去是沒有順序的(存和取的順序可能不一致),那我們要如何保證順序呢?

在HashMap下面有個LinkedHashMap(繼承關係),它是連結串列(記錄元素的順序)和雜湊表組合的一個數據儲存結構,是個有序的集合

【參考程式碼】

 package Map;
 ​
 import javax.swing.text.html.HTMLDocument;
 import java.util.*;
 ​
 public class Test {
     public static void main(String[] args) {
 ​
         HashMap<String,String> map = new LinkedHashMap<>();
         map.put("a","a");
         map.put("c","c");
         map.put("b","b");
         map.put("d","d");
         System.out.println(map);//{a=a, c=c, b=b, d=d}
    }
 }
 ​

輸出結果:(儲存和取出的順序是一樣的)

{a=a, c=c, b=b, d=d}

總結

看到這裡,相信各位小夥伴們對Java集合這一章節的知識有了進一步的理解,尤其是一些在之前學習時可能沒有注意到的知識或者原理,沒關係,這次都幫你總結在一起了。最後,感謝看到這裡的你!願你韶華不負,青春無悔!

注: 由於自己剛剛開始學習Java不久,語言文字描述、技術等各方面還不是很好,如果文章有任何錯誤和建議,請各位大佬盡情評論留言!如果這篇文章對你有些許幫助,希望可愛親切的您點個贊推薦一手,非常感謝啦