1. 程式人生 > >JAVA進階(04)容器

JAVA進階(04)容器

一 泛型

  • 泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。

1、泛型方法

(1)泛型方法的定義規則

  • <E> 在方法返回值之前。
  • 多個型別引數(<E>)間用逗號隔開。
  • 返回值型別可以用泛型宣告,方法內,泛型可以當作正常的類來使用。
  • 泛型只能代表引用型型別,不能是基本型別。 
public class GenericMethodTest
{
   // 泛型方法 printArray                         
   public static < E > void printArray( E[] inputArray )
   {
      // 輸出陣列元素            
         for ( E element : inputArray ){        
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }
 
    public static void main( String args[] )
    {
        // 建立不同型別陣列: Integer, Double 和 Character
        Integer[] intArray = { 1, 2, 3, 4, 5 };
        Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
 
        System.out.println( "整型陣列元素為:" );
        printArray( intArray  ); // 傳遞一個整型陣列
 
        System.out.println( "\n雙精度型陣列元素為:" );
        printArray( doubleArray ); // 傳遞一個雙精度型陣列
 
        System.out.println( "\n字元型陣列元素為:" );
        printArray( charArray ); // 傳遞一個字元型陣列
    } 
}

(2)有界的型別引數

  • 上界 <? extends Fruit> ,表示所有繼承Fruit的子類,包含Fruit本身
  • 下界 <? super Apple>,表示Apple的所有父類,包含Apple本身
  • 其中?號為型別萬用字元

2、 泛型類

  • 類名後新增型別引數宣告
  • 泛型介面使用基本和泛型類差不多

public class Box<T> {
   
  private T t;
 
  public void add(T t) {
    this.t = t;
  }
 
  public T get() {
    return t;
  }
 
  public static void main(String[] args) {
    Box<Integer> integerBox = new Box<Integer>();
    Box<String> stringBox = new Box<String>();
 
    integerBox.add(new Integer(10));
    stringBox.add(new String("菜鳥教程"));
 
    System.out.printf("整型值為 :%d\n\n", integerBox.get());
    System.out.printf("字串為 :%s\n", stringBox.get());
  }
}

3、 偽泛型的概念

  • 泛型只在原始碼中存在,在編譯後的位元組碼中就已經替換為原來的原生型別了,並且在相應的地方插入了強制型別轉換的程式碼,Java的泛型實現方法稱為型別擦除,基於這種方法實現的泛型稱為偽泛型

編譯前

反編譯

二、容器(集合)概述

1、Java集合框架的體系結構:

 

(1)說明:

  • 所有的集合框架體系都包含:介面、實現類和演算法 
  • collection 介面所包含的方法:

 

三、list

1、特點

  • 有序:有索引
  • 可重複:允許兩個元素 equal

2、常用方法

(1)兩個 list 之間的元素操作 

  • list.containsAll(list2)-------list是否包含list2中所有元素
  • list.addAll(list2)--------將list2中所有元素都新增到list中
  • list.removeAll(list2)--------從list中刪除同時在list和list2中存在的元素
  • list.retainAll(list2)-------取list和list2的交集

3、ArrayList

  • 特點:查詢效率高,增刪效率低,執行緒不安全
  • 原理:ArrayList底層使用Object陣列來儲存元素資料,超出陣列預設長度重新搞一個更大的陣列,然後將已有內容複製過去

4、LinkedList

  • 特點:查詢效率低,增刪效率高,執行緒不安全
  • 原理:每個資料節點中都有兩個指標,分別指向前一個節點和後一個節點

   å¾9-8 LinkedListçå­å¨ç»æå¾.png

5、Vector

  • 和 arraylist 相比,執行緒安全,效率低,幾乎不用了

6、List應用總結

  • 需要執行緒安全時,用Vector
  • 不存線上程安全問題時,並且查詢較多用ArrayList(一般使用它)
  • 不存線上程安全問題時,增加或刪除元素較多用LinkedList

四、map

1、概述

  •  Map就是用來儲存 鍵(key)-值(value) 對 的,儲存的 鍵值對 通過鍵來標識,所以鍵不能重複
  •  Map 介面的實現類有 HashMap、TreeMap、HashTable、Properties 等

2、map介面方法

   è¡¨9-3 Mapæ¥å£ä¸­å¸¸ç¨çæ¹æ³.png

3、HashMap和HashTable

(1)原理:HashMap採用雜湊演算法實現,底層採用了雜湊表儲存資料,鍵不能重複,如果發生重複,新的會替換舊的

(2)特點:HashMap在查詢、刪除、修改方面都有非常高的效率

(3)二者的區別

  • HashMap: 執行緒不安全,效率高。允許key或value為null
  • HashTable: 執行緒安全,效率低。不允許key或value為null

4、HashMap底層實現詳解

(1)資料結構中由陣列和連結串列來實現對資料的儲存,二者特點如下:

  • 陣列:佔用空間連續。 定址容易,查詢速度快。但是,增加和刪除效率非常低。
  • 連結串列:佔用空間不連續。 定址困難,查詢速度慢。但是,增加和刪除效率非常高。
  • 雜湊表的本質就是 陣列+連結串列,集合二者優點。

(2)HashMap的結構

å¾9-15 Entryæ°ç»å­å¨ç»æå¾.png

   說明:

  • Entry[] table 就是HashMap的核心陣列結構,稱之為 位桶陣列;
  •  一個Entry物件儲存了: key物件 ;value物件 ;下一個節點next ;鍵的hash值;

(3)存資料過程

  • 首先呼叫key物件的hashcode()方法,獲得hashcode;
  • 根據hashcode計算出hash值,要求轉化後的hash值儘量均勻地分佈在[0,陣列長度-1]這個區間,減少hash衝突;
  • 生成Entry物件,將Entry物件放到table陣列中:如果本Entry物件對應的陣列索引位置還沒有放Entry物件,則直接將Entry物件儲存進陣列,如果對應索引位置已經有Entry物件,則將已有Entry物件的next指向本Entry物件,形成連結串列;
  • 補充:hash演算法採用 hashcode與(length-1)的位與運算;連結串列長度大於8時,連結串列就轉換為紅黑樹,提高了查詢效率;

(4)取資料過程

  • 通過 key的hashcode 得到 hash 值,通過這個值找到陣列的位置;
  • 然後在對應的連結串列上呼叫 key的 equals 方法逐一與所有節點的 key比較;
  • 返回比較後為 true 的節點對應的 value 值;
  • Java中規定,兩個內容相同(equals()為true)的物件必須具有相等的hashCode,否則Map取資料的過程存在悖論;

(5)擴容

  • HashMap的位桶陣列,初始大小為16,大小是可變的,如果位桶陣列中的元素達到(0.75*陣列 length), 就重新調整陣列大小變為原來2倍大小;
  • 擴容的本質是定義新的更大的陣列,並將舊陣列內容挨個拷貝到新陣列中,很耗時;

5、TreeMap

(1)二叉樹和紅黑二叉樹

   1、基本二叉樹

   2、排序二叉樹

  • 左子樹上所有節點的值均小於它的根節點的值
  • 右子樹上所有節點的值均大於它的根節點的值

   3、平衡二叉樹

  • 在平衡二叉樹中任何節點的兩個子樹的高度最大差別為1
  • 平衡二叉樹建立在排序二叉樹的基礎上,通過左旋或者右旋的操作來達到平衡

   4、紅黑二叉樹:自平衡的排序二叉樹

(2)TreeMap的底層實現

  • TreeMap是紅黑二叉樹的典型實現;
  • entry 裡面儲存了本身資料、左節點、右節點、父節點、以及節點顏色;

(3)TreeMap的使用

  • HashMap效率高於TreeMap,一般用他;
  • 在需要排序的Map時才選用TreeMap;

五、set

1、概述

  • 方法和Collection保持完全一致;
  • Set容器特點:無序、不可重複(null 值也只能有一個);
  • Set常用的實現類有:HashSet、TreeSet等,一般使用HashSet;

2、HashSet

(1)使用:使用時注意無序和不可重複的特點

(2)底層實現:HashSet本質就是一個簡化版的HashMap,資料存到鍵物件

3、TreeSet

(1)使用:

  • 對應的類需要實現Comparable介面,才能根據compareTo()方法比較物件之間的大小,才能進行內部排序;
  • TreeSet中不能放入null元素;
@Override
    public int compareTo(User o) {
        if (this.id > o.id) {
            return 1;
        } else if (this.id < o.id) {
            return -1;
        } else {
            return 0;
        }
    }

(2)底層實現:一個簡化版的TreeMap,通過key來儲存Set的元素

六、迭代器

1、迭代器

(1)特殊:一般情況集合在遍歷的過程中是不許刪除操作的會報錯,但是使用迭代器可以實現

        Set<String> set = new HashSet<>();
		set.add("haha1");
		set.add("haha2");
		set.add("haha3");
		set.add("haha4");
		Iterator<String> ite = set.iterator();
		while(ite.hasNext()){
			String str = ite.next();
			if(str.endsWith("3")){
				ite.remove();
			}
			System.out.print(str+"\t");
		}
		System.out.println();
		for (String str:set) {
			System.out.print(str+"\t");
		}

(2)常用集合的遍歷:

  • List:普通for,增強for和迭代器
  • set:增強for和迭代器
  • map:增強for和迭代器(keys,values,keyset,entryset

2、Collections工具類

(1)Collections 提供了對Set、List、Map進行排序、填充、查詢元素的輔助方法

  • void sort(List)---------對List容器內的元素排序,排序的規則是按照升序進行排序
  • void shuffle(List)-----------對List容器內的元素進行隨機排列
  • void reverse(List)-----------對List容器內的元素進行逆續排列 
  • void fill(List, Object)-----------用一個特定的物件重寫整個List容器
  • int binarySearch(List, Object)-----------對於順序的List容器,採用折半查詢的方法查詢特定物件

(2)如下情況可能需要重寫 equals/hashCode 方法:

  • 要將我們自定義的物件放入HashSet中處理
  • 要將我們自定義的物件作為HashMap的key處理
  • 放入Collection容器中的自定義物件後,可能會呼叫remove、contains等方法時

---------------------------------------------------------------------------------------------------------------------------------------

補充1(佇列queue)

(1)說明

  • 描述:特殊的線性表,特點是先進先出
  • 常用的方法:offer()來加入元素    poll()來獲取並移出元素   element()或者peek()方法使用前端而不移出該元素
  • 注意:LinkedList類實現了Queue介面,因此我們可以把LinkedList當成Queue來用
public class QueueTest {
    public static void main(String[] args) {
        //add()和remove()方法在失敗的時候會丟擲異常(不推薦)
        Queue<String> queue = new LinkedList<String>();
        //新增元素
        queue.offer("a");
        queue.offer("b");
        queue.offer("c");
        queue.offer("d");
        queue.offer("e");
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("poll="+queue.poll()); //返回第一個元素,並在佇列中刪除
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("element="+queue.element()); //返回第一個元素 
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("peek="+queue.peek()); //返回第一個元素 
        for(String q : queue){
            System.out.println(q);
        }
        
    }
}

補充2(資料結構)

   在Java中的資料結構主要包括以下幾種介面和類:

  • 列舉(Enumeration)
  • 位集合(BitSet)
  • 向量(Vector)
  • 棧(Stack)
  • 字典(Dictionary)
  • 雜湊表(Hashtable)
  • 屬性(Properties)

1、列舉(Enumeration)

  • 類似於迭代器,現在用的少

2、位集合(BitSet)

  • 一個Bitset類建立一種特殊型別的陣列來儲存位值,有許多位操作的方法

3、向量(Vector)

  • 動態陣列,類似於ArrayList,不過是執行緒安全的,現在用的少

4、棧(Stack)

   特點是後進先出,是Vector的一個子類,常用的方法:

  • boolean empty( ) 
  • Object peek( )
  • Object pop( )
  • Object push(Object element)
  • int search(Object element)

5、字典(Dictionary)

  • 儲存鍵值對,類似於Map,已過時

6、雜湊表(Hashtable)

  • 類似於HashMap,是同步的,用得少

7、屬性(Properties)

   HashTable的子類,存鍵值對,鍵和值都是String,常用來配合IO流載入配置檔案,常用方法如下:

  • void load(InputStream streamIn) throws IOException
     從輸入流中讀取屬性列表(鍵和元素對)。
  • void store(OutputStream streamOut, String description)
     將此Properties中的屬性列表(鍵和元素對)寫入輸出流。