Java集合框架-頂層接口

image-20200706203157988

List接口

介紹

java.util.List 接口繼承自 Collection 接口 ,是單列集合中的一個重要分支。java

  • 容許出現重複元素
  • 線性存儲,有索引
  • 有序,存入和取出的順序是一致的

特有方法

方法 描述
void add(int index, E element) 將指定的元素插入此列表中的指定位置(可選操做)。
E get(int index) 返回此列表中指定位置的元素。
E remove(int index) 刪除該列表中指定位置的元素(可選操做)。
E set(int index, E element) 用指定的元素(可選操做)替換此列表中指定位置的元素。

測試代碼

package collection;

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

/**
 * 測試list接口的幾個特有方法
 * 有序,與index相關的經常使用方法
 */
public class TestList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        System.out.println(list);
        list.add("A");
        list.add("B");
        list.add("C");
        System.out.println(list);
        list.add(1, "a");
        System.out.println(list);
        System.out.println("刪除元素"+list.remove(0));
        System.out.println(list);
        System.out.println("被替換的元素" + list.set(0, "A"));
        System.out.println(list);
    }
}

結果數組

[]
[A, B, C]
[A, a, B, C]
刪除元素A
[a, B, C]
被替換的元素a
[A, B, C]

注意下標越界跟數組同樣安全

List子類

ArrayList集合

java.util.ArrayList 是可調整大小的數組的實現List接口網絡

元素增刪慢,查找快,知足平常開發中實現查詢數據、遍歷數據等功能框架

但不意味着隨意使用ArrayList完成任何需求,當頻繁增刪數據時,效率極低。工具

底層原理

數據存放在Object類型的數組中:性能

transient Object[] elementData; // non-private to simplify nested class access測試

既然是數組,那它是怎樣實現自動擴容的呢?this

以ArrayList 的add方法源碼舉例.net

// 在添加數據以前,確保容量充足
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

// 肯定具體容量 -> 計算容量
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// 若是列表爲空,返回默認容量 DEFAULT_CAPACITY = 10,不然返回最小容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

// 若是最小容量小於數組長度,則給數組擴容
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
// 擴容過程,舊容量 = 原數組長度,新容量 = 擴容1.5倍
// 若是新容量小於最小容量,新容量=最小容量
// 若是新容量 大於 最大數組大小 Integer.MAX_VALUE - 8 
//總之用數組存儲是不可能無限增加,超過21億多就拋出內存溢出異常
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}
// 最後調用工具類Arrays的複製方法,將原數組的數據複製到一個新的數組

Arrays.java

public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));
    return copy;
}
// 底層仍是調用了系統級的複製數組方法
// 本地方法
public static native void arraycopy(Object src,  int  srcPos,Object dest, int destPos,int length);

注意

ArrayList的實現不是同步的,存在線程安全問題。

LinkedList集合

特色

  • 底層用鏈表結構:查詢慢,增刪快
  • 包含大量操做首尾元素的方法
  • 使用特有方法時不要用多態

底層

用雙向鏈表存儲數據

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

繼承 AbstractSequentialList 類

實現了不少接口

  • List 列表的相關操做
  • Queue
    • Deque 隊列
  • Cloneable 克隆
  • Serializable 序列化,用於網絡傳輸
public class LinkedList<E>extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}

public interface Deque<E> extends Queue<E> {}

image-20200707210355761

Java LinkedList(鏈表) 相似於 ArrayList,是一種經常使用的數據容器。

與 ArrayList 相比,LinkedList 的增長和刪除對操做效率更高,而查找和修改的操做效率較低。

如下狀況使用 ArrayList :

  • 頻繁訪問列表中的某一個元素。
  • 只須要在列表末尾進行添加和刪除元素操做。

如下狀況使用 LinkedList :

  • 你須要經過循環迭代來訪問列表中的某些元素。
  • 須要頻繁的在列表開頭、中間、末尾等位置進行添加和刪除元素操做。

特有方法

方法 描述
void addFirst(E e) 在該列表開頭插入指定的元素。
void addLast(E e) 將指定的元素追加到此列表的末尾。
void push(E e) 將元素推送到由此列表表示的堆棧上。
E getFirst() 返回此列表中的第一個元素。
E getLast() 返回此列表中的最後一個元素。
E peek() 檢索但不刪除此列表的頭(第一個元素)。
E peekFirst() 檢索但不刪除此列表的第一個元素,若是此列表爲空,則返回 null 。
E peekLast() 檢索但不刪除此列表的最後一個元素,若是此列表爲空,則返回 null 。
E poll() 檢索並刪除此列表的頭(第一個元素)。
E pollFirst() 檢索並刪除此列表的第一個元素,若是此列表爲空,則返回 null 。
E pollLast() 檢索並刪除此列表的最後一個元素,若是此列表爲空,則返回 null 。
E removeFirst() 今後列表中刪除並返回第一個元素。
E removeLast() 今後列表中刪除並返回最後一個元素。
E pop() 今後列表表示的堆棧中彈出一個元素。

Vector

全部單列集合的祖先,Java 1.0版本就已經發布!

Java1.2版本以後,ArrayList取代了Vector。

Vector 類實現了一個動態數組。和 ArrayList 很類似,可是二者是不一樣的:

  • Vector 是同步訪問的。
  • Vector 包含了許多傳統的方法,這些方法不屬於集合框架。

Set接口

java.util.Set 接口繼承自 Collection 接口 ,也是單列集合中的一個重要分支。

  • 不容許存儲重複的元素
  • 沒有索引!沒有帶索引的方法,方法和Collection同樣
  • 不能用普通for循環遍歷,加強for能夠

Set子類

HashSet集合

java.util.HashSet 集合實現 Set 接口

特色

  • 不重複
  • 無序
  • 無索引

底層

HashTable 查詢速度很是快

public class HashSet<E>extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable{
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }
}

測試代碼

import java.util.HashSet;
import java.util.Iterator;

public class TestHashSet {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        System.out.println(set.add("a"));
        System.out.println(set.add("a"));
        System.out.println(set.add("b"));
        System.out.println(set.add("c"));

        System.out.println("========迭代器=======");
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            System.out.print(it.next() + "\t");
        }
        System.out.println("\n========加強for=======");
        for (String s : set) {
            System.out.print(s + "\t");
        }
    }
}

結果

true
false
true
true
========迭代器=======
a	b	c	
========加強for=======
a	b	c

Hash值

一個十進制的整數,由系統隨機給出。

能夠視爲模擬對象存儲的邏輯地址,而不是數據實際存儲的物理地址。

Object祖宗類中就提供獲取hash值的方法hashCode() ,hashCode調用了

public native int hashCode();

對具體實現感興趣的請轉至傳送門

哈希碼的通用約定以下:

  • 在java程序執行過程當中,在一個對象沒有被改變的前提下,不管這個對象被調用多少次,hashCode方法都會返回相同的整數值。對象的哈希碼沒有必要在不一樣的程序中保持相同的值。
  • 若是2個對象使用equals方法進行比較而且相同的話,那麼這2個對象的hashCode方法的值也必須相等。
  • 若是根據equals方法,獲得兩個對象不相等,那麼這2個對象的hashCode值不須要必須不相同。可是,不相等的對象的hashCode值不一樣的話能夠提升哈希表的性能。

Hash表

查詢快!

JDK 1.8 以前,哈希表 = 數組 + 鏈表

JDK 1.8 以後

  • 哈希表 = 數組 + 鏈表
  • 哈希表 = 數組 + 紅黑樹
  • 紅黑樹在數量大時提升查詢速度

Hash表的底層

數組結構:把元素分組,相同哈希值的放到一塊兒,用鏈表或紅黑樹保存

當鏈表長度超過8位,將鏈表轉爲紅黑樹

初始容量爲16

存儲數據到集合的過程

image-20200707222239621

HashSet的底層

就是HashMap !!

http://www.javashuo.com/article/p-memupmkd-bc.html

HashMap明天再補

相關文章
相關標籤/搜索