Java類集框架詳細彙總-底層分析

xbhog發表於2021-03-31

前言:

Java的類集框架比較多,也十分重要,在這裡給出圖解,可以理解為相應的繼承關係,也可以當作重要知識點回顧;

Collection集合介面

Collection集合介面

繼承自:Iterable

public interface Collection<E> extends Iterable<E>

java.util.Collection是單值集合操作的最大父介面,其中有幾個核心操作方法以及常用操作方法;

Modifier and Type Method(public) Description
boolean add(E e) 確保此集合包含指定的元素(可選操作)。
boolean addAll(Collection<? extends E> c) 將指定集合中的所有元素新增到此集合(可選操作)。
void clear() 從集合中刪除所有元素(可選操作)。
boolean contains(Object o) 如果該集合包含指定的元素,則返回true。
boolean remove(Object o) 如果存在,則從此集合中刪除指定元素的單個例項(可選操作)。
int size() 返回此集合中的元素數。
Object[] toArray() 返回一個包含此集合中所有元素的陣列。
Iterator<E> iterator() 返回對此集合中的元素進行迭代的迭代器。

上面方法中有兩個特殊的方法就是cotainsremove;都需要equals方法的支援才能刪除與查詢資料;否則找不到元素。

後面都是衍生出的子類方法。

List集合

最大特點:允許儲存重複的元素,並在其父介面上擴充了其他的方法;

繼承關係:

public interface List<E> extends Collection<E>
Modifier and Type Method(public) Description
void add(int index, E element) Inserts the specified element at the specified position in this list (optional operation).
boolean add(E e) Appends the specified element to the end of this list (optional operation).
ListIterator<E> listIterator() Returns a list iterator over the elements in this list (in proper sequence).
static <E> List<E> of() Returns an unmodifiable list containing zero elements.
default void forEach(Consumer<? super T> action) Performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception.

例項:

package Java從入門到專案實戰.Java類集框架.List集合;
import java.util.List;
public class 多資料儲存 {
    public static void main(String[] args) {
        List<String> all = List.of("xbhg","Hello","World","welcome");
        Object[] result = all.toArray();
        for (Object t: result) {
            System.out.println(t);
        }
        System.out.println("----------分割線----------");
        all.forEach(System.out::println); //方法引用部分的引用構造方法
    }
}

ArrayList子類

繼承結構如下:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable

例項化:重複元素允許儲存並且按照新增時的順序儲存;

package Java從入門到專案實戰.Java類集框架.List集合;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ArrayList例項化List {
    public static void main(String[] args) {
        List<String> all = new ArrayList<String>();
        all.add("Hello");
        all.add("你好");
        all.add("你好");
        all.add("xbhog");
        System.out.println(all);
        all.forEach(System.out::print);
        //lambda表示式
        all.forEach((str)->{
            System.out.print(str+"、");
        });
    }
}

集合操作方法:

package Java從入門到專案實戰.Java類集框架.List集合;
import java.util.ArrayList;
import java.util.List;

public class ArrayLIst集合相關操作 {
    public static void main(String[] args) {
        List<String> all = new ArrayList<String>();
        System.out.println("集合是否為空?"+all.isEmpty()+"、集合元素個數"+all.size());
        all.add("1");
        all.add("2");
        all.add("3");
        System.out.println("集合是否為空?"+all.isEmpty()+"、集合元素個數"+all.size());
        System.out.println(all.get(1));
        System.out.println(all.remove("3"));
        System.out.println("集合是否為空?"+all.isEmpty()+"、集合元素個數"+all.size());
    }
}

ArrayList原理分析:重點

首先需要明確ArrayList是通過陣列實現的;這樣就出現了ArrayList通過什麼樣的方式進行的擴容操作,以及在什麼情況下才會擴容?

ArrayList類中的陣列是在構造方法中進行的空間開闢的;其對應的有無參和有參構造方法:

無參構造方法:使用空陣列(長度為0)初始化,在第一次使用時會為其開闢空間為(初始化程度為10);

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//預設的開闢空間大小
private static final int DEFAULT_CAPACITY = 10;
/**
     * Shared empty array instance used for empty instances.
     */
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

有參構造方法:長度大於0則以指定長度開闢陣列空間;如果長度為0,則按照無參構造方法進行;如果負數則丟擲ILLegaLArgumentException異常;

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // defend against c.toArray (incorrectly) not returning Object[]
        // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

當陣列擴充後利用陣列複製的形式,將舊陣列中的資料複製到開闢的新陣列中;

其最大程度為:

/**
     * The maximum size of array to allocate (unless necessary).
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

ArrayList儲存自定義類物件:

該操作必然包含了相關的增刪改查;由於contains與remove方法的實現都需要通過物件比較倆完成;所以我們需要覆寫equals方法

package Java從入門到專案實戰.Java類集框架.List集合;

import java.util.ArrayList;
import java.util.List;

class Person{
    private String name;
    private int age;
    public Person(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;
    }
    public boolean equals(Object obj){
        if(this== obj) return true;
        if(obj == null) return false;
        if(!(obj instanceof Person)) return false;
        Person pe = (Person) obj;
        return this.name.equals(pe.name) && this.age == pe.age;
    }

    @Override
    public String toString() {
        return "姓名:"+this.name +"、年齡:"+this.age;
    }
}
public class ArrayList儲存自定義類物件 {
    public static void main(String[] args) {
        List<Person> std = new ArrayList<Person>();
        std.add(new Person("xbhog",1));
        std.add(new Person("小明",2));
        std.add(new Person("小白",3));
        System.out.println("-------刪除前的資料內容----------");
        std.forEach((person)->{
            System.out.println("姓名:"+person.getName()+"、年齡:"+person.getAge());
        });
        System.out.println("-------刪除後的資料內容----------");
        std.remove(new Person("小白",3));
        std.forEach((person)->{
            System.out.println("姓名:"+person.getName()+"、年齡:"+person.getAge());
        });
        System.out.println("-------檢視資料是否存在----------");
        System.out.println(std.contains(new Person("小明",2)));
    }
}

LinkedList子類:

繼承結構如下:基於連結串列形式的實現

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable

實現LinkedList的集合操作:

package Java從入門到專案實戰.Java類集框架.List集合;

import java.util.LinkedList;
import java.util.List;

public class LinkList連結串列操作 {
    public static void main(String[] args) {
        List<String> all = new LinkedList<String>();
        all.add("java");
        all.add("python");
        all.add("Linux");
        System.out.println(all);
        System.out.println(all.get(2));
        System.out.println(all.get(1));
    }
}

Vector子類:

繼承結構如下:

public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable

從繼承結構上可以看出,Vector子類的使用方式與ArrayList的使用方式相同;

package Java從入門到專案實戰.Java類集框架.List集合;
import java.util.List;
import java.util.Vector;
public class Vector子類實現List介面 {
    public static void main(String[] args) {
        List<String> all = new Vector<String>();
        all.add("asda");
        all.add("你好");
        all.add("buhao");
        System.out.println(all);
    }
}

不同點:

以下是vector操作方法,採用的方式是synchronized 同步處理;屬於執行緒安全,但是效率沒有ArrayList高;

在考慮執行緒併發訪問的情況下才能去使用vector子類。

public synchronized void copyInto(Object[] anArray)

Set集合

主要特點是:內部不允許儲存重複元素

繼承結構如下:

public interface Set<E> extends Collection<E>

例項化:

package Java從入門到專案實戰.Java類集框架.Set集合;
import java.util.Set;
public class set的基本使用 {
    public static void main(String[] args) {
        //不能有重複值,如果有的話會報錯
        //Exception in thread "main" java.lang.IllegalArgumentException: duplicate element: 世界
//        Set<String> all = Set.of("你好","xbhog","世界","世界");
        Set<String> all = Set.of("你好","xbhog","世界");
        System.out.println(all);
    }
}

HashSet子類:

特點:雜湊存放且不允許儲存重複元素 即:無序存放

繼承結構如下:

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable

HashSet儲存資料:

package Java從入門到專案實戰.Java類集框架.Set集合;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSet儲存資料 { //資料採用無序的儲存方式,且不允許儲存重複的資料
    public static void main(String[] args) {
        //儲存的型別是String
        Set<String> all = new HashSet<String>();
        all.add("小李");
        all.add("小紅");
        all.add("小1");
        all.add("小2");
        all.add("小花");
        all.add("小花");
        System.out.println(all);
        Iterator<String> iter = all.iterator();
        while(iter.hasNext()){
            String str  = iter.next();
            System.out.print(str+"、");
        }
    }
}

LinkedHashSet子類:JDK1.4加入---解決HashSet無法順序儲存的資料

實現是基於連結串列儲存的資料:增加的順序就是集合的儲存順序,且不會儲存重複的資料。

package Java從入門到專案實戰.Java類集框架.Set集合;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;
public class 連結串列實現LinkHashSet {  //基於連結串列的操作,且儲存的資料為順序儲存
    public static void main(String[] args) {
        Set<String> all = new LinkedHashSet<String>();
        all.add("小李老師");
        all.add("小bai");
        all.add("小明");
        all.add("小黃");
        System.out.println(all);
        Iterator<String> iter = all.iterator();
        while (iter.hasNext()) {
            String str = iter.next();
            System.out.println(str + "、");
        }
    }
}

TreeSet子類:

特點:使得集合中儲存的資料進行有序排列

其繼承結構如下:

public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable

TreeSet子類繼承AbstractSet抽象類並實現了NavigableSet介面(該介面為排序標準介面,是Set的子類)

TreeSet儲存資料:

package Java從入門到專案實戰.Java類集框架.Set集合;
import java.util.*;
public class TreeSet子類的有序排列 {
    //所有儲存的資料按照從大到小的順序(字元按照字母大小順序依次比較)
    public static void main(String[] args) {
        Set<String> all = new TreeSet<String>();
        all.add("11");
        all.add("hello");
        all.add("hello");
        all.add("world");
        all.add("add");
        all.add("部署");
        all.add("啊哈");
        System.out.println(all);
    }
}

優先順序:數字排序>字母排序>漢字排序

TreeSet子類排序分析:

該類子在進行有序資料儲存時依據的是Comparable介面實現排序;需要注意的是在覆寫compareTo()方法時需要進行類中全部屬性的比較;否則出現部分屬性相同時被誤判為同一個物件;導致重複元素判斷失敗;

package Java從入門到專案實戰.Java類集框架.Set集合;
import java.util.Set;
import java.util.TreeSet;
class Member implements Comparable<Member>{
    private  String name;
    private  int age;
    public Member(String name,int age){
        this.name = name;
        this.age = age;
    }
    public String toString(){
        return "姓名:"+this.name +"、年齡:"+this.age;
    }
    @Override
    public int compareTo(Member per){
        if(this.age < per.age) return -1;
        else if(this.age > per.age) return 1;  //年齡相同時比較名字
        else {
            return this.name.compareTo(per.name);
        }
    }
}

public class TreeSet子類排序分析 {
    public static void main(String[] args) {
        Set<Member> all = new TreeSet<Member>();
        all.add(new Member("張三",12));
        all.add(new Member("李四",12));
        all.add(new Member("王五",20));
        all.add(new Member("王五",20));
        all.forEach(System.out::println);
    }
}

關於compareTo的相關描述,可以到原始碼下的註釋中翻譯瞭解。

重複元素消除:(非排序的集合中的重複元素)

依靠兩種方法:

  1. Hash碼:public int Hashcode();
  2. 物件比較:public boolean equals(Object obj);

在進行物件比較的過程中,首先會使用hashCode()方法與集合中已經儲存的程式碼進行匹配比較;如果程式碼相同則再使用equals()方法進行屬性的依次比較;如果全部相同;則為相同元素;

package Java從入門到專案實戰.Java類集框架.Set集合;
import java.util.*;
class Person{
    private String name;
    private int age;
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        if (name != null ? !name.equals(person.name) : person.name != null) return false;

        return true;
    }
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}
public class 重複元素消除 {
    public static void main(String[] args) {
        Set<Person> all = new HashSet<Person>();
        all.add(new Person("張三",19));
        all.add(new Person("李四",19));
        all.add(new Person("王五",20));
        all.add(new Person("王五",20));
        all.add(new Person("魏六",66));
        all.add(new Person("xbhog",10));
        System.out.println("------------第一種輸入方式-----------");
        all.forEach((person -> {
            System.out.println(person.getName()+"----"+person.getAge());
        }));
        System.out.println("------------第二種輸入方式-----------");
        Iterator<Person> iter = all.iterator();
        while(iter.hasNext()){
            Person per = iter.next();
            System.out.println(per.getName() + " "+per.getAge());
        }
    }
}

通過hashSet 儲存了重複元素,再兩個方法的作用實現去重操作。

集合輸出

在類框架中的對於集合的標準輸出為:IteratorListIteratorEnumerationforeach;

Iterator迭代輸出:

迭代輸出:依次判斷每個元素,判斷其是否有內容,如果有內容就輸出。

Iterator介面依靠Iterable介面中的iterate()方法例項化的;

Iterator常用方法:

boolean hasNext() Returns true if the iteration has more elements.
E next() 返回迭代中的下一個元素。
default void remove() 從基礎集合中移除該迭代器返回的最後一個元素(可選操作)。
boolean hasNext() 如果迭代有更多元素則返回true。

例項:

package Java從入門到專案實戰.Java類集框架.集合輸出;
import java.util.Iterator;
import java.util.Set;
public class Iterator輸出Set集合 {
    public static void main(String[] args) {
        Set<String> all = Set.of("Hello","World","xbhog");
        Iterator<String> iter = all.iterator();
        while(iter.hasNext()){
            String str = iter.next();
            System.out.print(str+"、");
        }       
    }
}

關於資料刪除的問題:

在Collection中與Iterator都有remove方法;那麼應該選擇什麼呢:

Collection不管三七二十一,就給你刪除了,這樣會造成Java.util.ConcurrentModificationException錯誤;

而Iterator在迭代的時候;都會需要依據儲存的資料內容進行判斷;

所以只有Iterator介面中的remove才是實現刪除資料的正確方法。

如:

package cn.mldn.demo;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class JavaCollectDemo {
	public static void main(String[] args) {
		// 如果使用Set.of()或List.of()建立的集合不支援刪除操作
		Set<String> all = new HashSet<String>();
		all.add("小白");					// 儲存資料
		all.add("Java");					// 儲存資料
		all.add("Java"); 					// 儲存重複資料
		all.add("xbhog");				// 儲存資料
		Iterator<String> iter = all.iterator(); 
		while (iter.hasNext()) {				// 集合是否有資料
			String str = iter.next();			// 獲取資料
			if ("Java".equals(str)) {
				iter.remove() ; 				// 刪除當前資料
			} else {
				System.out.print(str + "、");
			}
		}
	}
}

ListIterator雙向迭代:

首先區別Iterator的作用:

Iterator完成的是從前向後單向輸出

ListIterator完成的是從後向前輸出

但是隻有實現Iterator從前向後的輸出後才能實現ListIterator從後向前的輸出(注意先後順序);

因為只有實現從前向後輸出結束後,指標才執行最後;

如果順序相反則會輸出為空。

其下的擴充方法

Modifier and Type Method Description
boolean hasPrevious() 如果該列表迭代器在反向遍歷列表時擁有更多元素,則返回true。
E previous() 返回列表中的前一個元素,並向後移動游標位置。

例項:執行雙向迭代

package Java從入門到專案實戰.Java類集框架.集合輸出;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class 雙向迭代輸出ListIterator {
    public static void main(String[] args) {
        List<String> all = new ArrayList<String>();
        all.add("小李");
        all.add("小宋");
        all.add("xbhog");
        ListIterator<String> iter = all.listIterator();
        System.out.println("執行從後往前的操作的話必須需要先執行從前往後的操作,這樣才能是指標指向後面");
        //如果不按照相關操作進行,則從後往前的操作輸出為空;
        System.out.println("從前往後輸出:-----------");
        while(iter.hasNext()){
            System.out.println(iter.next());
        }
        System.out.println("從後往前輸出:------------");
        while(iter.hasPrevious()){  //判斷是否有前一個元素
            System.out.println(iter.previous());  //有的輸出前一個元素
        }
    }
}

ListIterator介面實現了List集合的雙向迭代操作。

Enumeration列舉輸出:

該型別的輸出是建立在Vector集合上的;相當於依附產品;

其介面常用方法:

Modifier and Type Method Description
boolean hasMoreElements() 測試此列舉是否包含更多元素。
E nextElement() 如果該列舉物件至少還有一個要提供的元素,則返回該列舉的下一個元素。

例項:輸出vector集合資料

package cn.mldn.demo;
import java.util.Enumeration;
import java.util.Vector;
public class JavaCollectDemo {
	public static void main(String[] args) {
		Vector<String> all = new Vector<String>(); 		// 例項化Vector
		all.add("小黃");								// 儲存資料
		all.add("Java");								// 儲存資料
		all.add("xbhog");							// 儲存資料
		Enumeration<String> enu = all.elements() ;			// 獲取Enumeration例項
		while (enu.hasMoreElements()) {
			String str = enu.nextElement() ;
			System.out.print(str + "、");
		}
	}
}

注意Enumeration只有輸出操作沒有刪除操作。

foreach輸出:

沒啥好說的,既可以實現陣列輸出外,也支援集合的輸出;

package cn.mldn.demo;
import java.util.HashSet;
import java.util.Set;
public class JavaCollectDemo {
	public static void main(String[] args) {
		Set<String> all = new HashSet<String>(); 			// 例項化Set
		all.add("小黃");								// 儲存資料
		all.add("Java");								// 儲存資料
		all.add("xbhog");							// 儲存資料
		for (String str : all) {
			System.out.print(str + "、"); 
		}
	}
}

實現自定義foreach輸出:

首先需要知道實現foreach需要Iterator介面的支援;所以在Set與List集合才可以通過foreach實現輸出;

如果我們自己要實現自定義類的輸出,那麼我們就需要例項化Iterable介面完成iterator的功能;

package cn.mldn.demo;
import java.util.Iterator;
class Message implements Iterable<String> {// 支援foreach輸出
	private String[] content = { "Java", "Python", "Ubuntu" }; 	// 資訊內容
	private int foot; 					// 操作腳標
	@Override
	public Iterator<String> iterator() {	// 獲取Iterator例項
		return new MessageIterator();
	}
	private class MessageIterator implements Iterator<String> {
		@Override
		public boolean hasNext() {		// 判斷是否存在內容
			return Message.this.foot < 
				Message.this.content.length;
		}
		@Override
		public String next() {			// 獲取資料
			return Message.this.content[Message.this.foot++];
		}
	}
}
public class JavaCollectDemo {
	public static void main(String[] args) {
		Message message = new Message();	// Iterable介面例項
		for (String msg : message) {		// foreach輸出
			System.out.print(msg + "、");
		}
	}
}

Map集合

map的集合形式是鍵值對的方式;

其常用方法:

Modifier and Type Method Description
V get(Object key) Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
V put(K key, V value) Associates the specified value with the specified key in this map (optional operation).
static <K,V> Map<K,V> of() Returns an unmodifiable map containing zero mappings.

其中Map.of()可以將每一組資料轉為map進行儲存;

使用Map儲存Key-Value資料:

package Java從入門到專案實戰.Java類集框架.Map集合;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class 儲存Key_Value資料 {
    public static void main(String[] args) {
//        如果Key重複  則會報錯:java.lang.IllegalArgumentException
//        如果Value與Key設定為null,則會報錯:java.lang.NullPointerException
        Map<String,Integer> map = Map.of("one",1,"two",null);
        System.out.println(map);
//        System.out.println(map.get("one"));
//        Put只能在map子類中使用
    }
}

注意點:

  1. 如果Key重複 則會報錯:java.lang.IllegalArgumentException
  2. 如果Value與Key設定為null,則會報錯:java.lang.NullPointerException

HashMap子類:

特點:採用雜湊方式儲存資料,即無序排列

繼承結構:

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

HashMap進行map集合的操作:

package Java從入門到專案實戰.Java類集框架.Map集合;
import java.util.HashMap;
import java.util.Map;
public class HahMap子類 {
    public static void main(String[] args) {
        Map<String,Integer> map =  new HashMap<String,Integer>();
//        進行Map的集合操作
//        map.put("One",1);
//        map.put("Two",2);
//        map.put("Three",3);
//        System.out.println(map);

//        Map資料的儲存方法:
        System.out.println(map.put("One",1)); //儲存資料,但是儲存的資料的key不存在,返回null
        System.out.println(map.put("One",101));  // 返回1,也就是返回的是覆蓋掉的值

        map.put("Three",3);
        map.put("demo1",4);
        map.put("demo2",null);
        map.put("Te",6);
        map.put(null,7);
        /*結果
        * null、7
        * */
        System.out.println(map.get("demo2"));
        System.out.println(map.get(null));
    }
}

注意兩個輸出的註釋:

//Map資料的儲存方法:
System.out.println(map.put("One",1)); //儲存資料,但是儲存的資料的key不存在,返回null
System.out.println(map.put("One",101));  // 返回1,也就是返回的是覆蓋掉的值

在使用Map儲存資料的時候Key與Value都不能使用null,但是使用HashMap進行儲存資料可以將Key或Value設定為null,當然也可以Key=Value=null,但是這樣的實現儲存毫無意義。

put方法在發生覆蓋錢都可以返回原始內容,這樣就可以依據返回結果來判斷所設定的key是否存在;

HashMap資料擴充操作原理分析:

1) 首先觀察構造方法:

設定資料擴充閾值;

public HashMap() {
    // all other fields defaulted
    this.loadFactor = DEFAULT_LOAD_FACTOR; 
}

然後跳轉DEFAULT_LOAD_FACTOR檢視:

作用:容量的擴充閾值

/**
     * The load factor used when none specified in constructor.
     */
static final float DEFAULT_LOAD_FACTOR = 0.75f;

通過原始碼可以發現,每個Hashmap在物件例項化的時候都已經考慮到了資料儲存的擴充問題;

2) 觀察HashMap中的put方法

public V put(K key,V value){
    return putVal(hash(key),key,value,false,true);
}

在使用put方法進行資料儲存的時候會呼叫putVal方法,同時會將key進行雜湊處理(生成hash碼)

而putVal()方法為了方便資料儲存會將資料封裝為一個Node節點類物件,而在使用putVal()方法的操作過程會呼叫reasize()方法進行擴容;

3)容量擴充

當儲存的資料超過既定的儲存容量則會進行擴容,原則如下:

常量地址:DEFAULT_INITIAL_CAPACITY;作為初始化的容量配置,而後1向左移4為-》16;

常量的預設大小為16個元素,也就是說預設可以儲存的最大的內容是16;

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

當儲存的資料內容超過了設定的閾值DEFAULT_LOAD_FACTOR = 0.75f

相當於容量x閾值 = 16*0.75 = 12;即儲存到12個元素的時候就會進行容量擴充;

擴充的模式是2倍擴充:即每一次擴充2倍的容量。

4)大資料下的資料儲存方式:

在JDK1.8之後來到大資料時代,這就觸發了HashMap在大資料量中的訪問效率問題;

其中提供了一個重要的常量:TREEIFY_THRESHOLD

static final int TREEIFY_THRESHOLD = 8;

在使用HashMap進行儲存的時候,如果儲存的資料個數沒有超過閾值8,那麼會按照連結串列的形式進行資料的儲存;而超過了這個閾值,則會將連結串列轉為紅黑樹以實現樹的平衡;並且利用左旋與右旋保證資料的查詢效能。

LinkedHashMap子類:

特點:基於連結串列形式實現偶對的儲存,可以保證儲存順序與資料增加的順序相同;

繼承結構:

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

使用LinkedHashMao子類儲存資料:

package Java從入門到專案實戰.Java類集框架.Map集合;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMap子類儲存資料 {
    public static void main(String[] args) {
        Map<String,Integer> map = new LinkedHashMap<String,Integer>();
        map.put("張三",1);
        map.put("李四",2);
        map.put("王五",3);
        map.put(null,3);
        map.put("趙六",null);
        System.out.println(map);
    }
}

執行可以發現集合的儲存的順序與資料增加順序相同;同時LinkedHashMap子類允許儲存的Key或value內容為null;

Hashtable子類:

其繼承結構如下:

public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable

使用Hashtable子類儲存資料:

package Java從入門到專案實戰.Java類集框架.Map集合;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMap子類儲存資料 {
    public static void main(String[] args) {
        Map<String,Integer> map = new Hashtable<String,Integer>();
        map.put("張三",1);
        map.put("李四",2);
        System.out.println(map);
    }
}

HashMap與Hashtable的區別:

HashMap中的 方法都是非同步操作(非執行緒安全),HashMap中允許儲存null資料

Hashtable中的方法都是同步操作(執行緒安全),但是效率慢,Hashtable不允許儲存Null資料;否則會出現NUllpointException;

TreeMap子類:

特點:TreeMap屬於有序的Map集合型別;它可以按照key進行排序;所以需要Comaprable介面配合;

繼承結構如下:

public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, Serializable

TreeMap子類進行資料Key的排序:

package Java從入門到專案實戰.Java類集框架.Map集合;
import java.util.Map;
import java.util.TreeMap;
public class TreeMap子類進行資料Key的排序 {
    public static void main(String[] args) {
//        因為該程式儲存的Key屬於String型別,由於String中實現了Comparable介面,所以
//        可以根據儲存的字元的編碼進行由低到高的進行排序
        /*
        * public final class String
            implements java.io.Serializable, Comparable<String>, CharSequence
        *
        *
        * */
        Map<String,Integer> map = new TreeMap<String,Integer>();
        map.put("C",3);
        map.put("B",2);
        map.put("A",1);
        System.out.println(map);

    }
}

Map.Entry內部介面:

在JDK1.9開始可以利用Map介面中建立Map.entry內部介面例項;

package Java從入門到專案實戰.Java類集框架.Map集合;
import java.util.Map;
public class Map_Entry內部介面 {
    public static void main(String[] args) {
        Map.Entry<String,Integer> entry = Map.entry("One",1);
        System.out.println(entry.getKey());
        System.out.println(entry.getValue());
        //觀察使用的子類
        System.out.println(entry.getClass().getName());
    }
}

在程式繼續寧Map.Entry物件構建的時,只傳入Key和Value就會自動利用KeyValueHolder子類例項化Map.Entry介面物件。

Iterator輸出Map集合:

集合資料輸出的標準形式是基於Iterator介面完成的;Collection介面直接提供iterator方法可以獲得iterator介面例項;但由於Map介面中儲存的資料是多個Map.Entry介面封裝的二元偶物件,所以就必須採用Map集合的迭代輸出;

  1. 使得Map介面中的entrySet(),將Map集合變為Set集合;
  2. 取得Set介面例項後就可以利用iterator方法取得iterator的例項化物件;
  3. 使用iterator迭代找到每一個Map.Entry物件,並進行Key與Value的分。

Iterator和foreach輸出Map集合:

package Java從入門到專案實戰.Java類集框架.Map集合;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Map集合的輸出問題 {
    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<String,Integer>();
        map.put("One",1);
        map.put("two",2);
//        輸出的map中的Key與Value的值
        Set<Map.Entry<String, Integer>> set = map.entrySet();//將Map集合轉變為Set集合
        Iterator<Map.Entry<String,Integer>> iter = set.iterator();  //獲取Iterator介面
        while(iter.hasNext()){
//          Set中存的都是Map.Entry()物件
            Map.Entry<String, Integer> me = iter.next();
            System.out.print(me.getKey()+" "+me.getValue());
        }
//        System.out.println("\n");
        //foreach迴圈輸出Map集合:
        for(Map.Entry<String,Integer> entry:set){
            System.out.print(entry.getKey()+" "+entry.getValue());
        }
    }
}

自定義Key型別:

採用自定義類的形式實現,但是作為Key型別的類由於存在資料查詢的需求,所以必須在類中覆寫hashcode()與equals()方法。

package Java從入門到專案實戰.Java類集框架.Map集合;
import java.util.HashMap;
import java.util.Map;
class Member{
    private String name;
    private int age;

    public Member(String name,int age){
        this.name = name;
        this.age = 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;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Member)) return false;

        Member member = (Member) o;

        if (age != member.age) return false;
        if (name != null ? !name.equals(member.name) : member.name != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}
/*
* Key進行自定義;作為Key型別的類由於存在資料查詢的需求,所以應該在類中覆寫HashCode() 和equals();
* */
public class 自定義Key的值 {
    public static void main(String[] args) {
        Map<Member,String> map = new HashMap<Member,String>();
        map.put(new Member("張三",22),"xbhog");
        map.put(new Member("李四",23),"部落格");
        map.put(new Member("王五",26),"welcome");
        System.out.println(map.get(new Member("張三",22)));

    }
}

在儲存大量的資料中,key還是可能出現重複的問題,這個問題叫Hash衝突;

解決的方式:

鏈地址法(拉鍊法)、開放定址、再雜湊、建立公共溢位區;

完結:

該部落格字數6651,是迄今為止個人整理部落格篇幅最長,字數最多的;希望讀者能有所收穫,也希望稽核能過,選擇部落格園第一感覺,園子裡很舒服,學術氛圍很好,博文質量很高;不敢說自己的每篇文章寫的都可以,但是也注入了自己的心血,希望能上首頁!

相關文章