Java集合框架詳細總結


一: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不久,語言文字描述、技術等各方面還不是很好,如果文章有任何錯誤和建議,請各位大佬盡情評論留言!如果這篇文章對你有些許幫助,希望可愛親切的您點個贊推薦一手,非常感謝啦

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM