Java認識泛型與容器


Java泛型與容器初探

一、泛型

“泛型”的意思是“適用於許多許多的類型”,實現了參數化類型的概念。其最初的目的是希望類或方法具備最廣泛的表達能力,通過解耦類或方法與所使用的類型之間的約束。不用像參數是類或接口那樣對程序有過多約束(方法的參數不必僅限於一種類或接口與它們的子類)

使用泛型,具體來說,在定義一個類的時候,類名后面加上<T>這個類型參數,那么在類中,可以用T來表示不特定的數據類型。在創建該泛型實例時,將T換成你所需要的具體的數據類型。這個數據類型不能是基本類型。

class A<T> {

private T v;

​ public T get(){ return T;}

}

......

A<Integer> temp = new A<Integer>;

Integer result = temp.get();

 

告訴編譯器想使用什么類型,然后編譯器幫你處理一切細節。

 

import java.util.*;

public class C{

​   public static void main(String[] args){

​   List list1 = new ArrayList();​

   list1.add("heheh");​

  list1.add(35.5);​

  for(int i = 0; i < list1.size();i++){

​     Object s = list1.get(i);​ System.out.println(s);

​     }

​   }

}

 

對這段代碼,編譯器會報使用了未經檢查或不安全的操作。注: 有關詳細信息, 請使用 -Xlint:unchecked 重新編譯。

但是任然可以運行。

import java.util.*;

public class C {

    public static void main(String[] args){

    List<String> list2 = new ArrayList<String>();

    list2.add("heheh");

    list2.add("wuwuw");

    //list2.add(35.5);

    for(int i = 0; i < list2.size();i++){

        Object s = list2.get(i);

        System.out.println(s);

        }

    }

}

 

 

 

 

而對於這段代碼,添加了<String>類型參數以后,如果依然add(35.5)這個double類型的數,就會報錯無法運行。因為加入的double類型與你已經確定的<String>類型沖突了,相當於你給add(String str)方法傳入了一個double類型的實參。

對於接口來說,使用泛型的方式也是一樣的。(List實際上就是一個接口的栗子)

接口使用泛型的一個例子是生成器,generator是工廠設計模式的一種應用,專門負責創建對象。一般而言,一個生成器只定義一個方法,該方法用以產生新對象。

public interface Generator<T>{

​ T next();//返回一個T類型的對象。

}

當你需要一個生成器時,就可以:

public class AGenerator implements Generator<A>{

​ public A next(){

​ ......

​ }

}

當然,不僅僅類和接口可以實現泛型,方法也可以。以下是一個基本的指導原則:無論何時,只要你能做到,你就應該盡量使用泛型方法。要定義泛型方法,需將泛型參數置於返回值之前。與泛型類中的方法相比,區別在於只在該方法中出現了類型參數(或者在泛型類中,方法里出現了新的類型參數,需要把方法定義為泛型方法? 沒有考證,想當然的個人理解)。

public class Lalala{

public <T> void f(T x){

System.out.println(x.getClass().getName());

​ }

}

注意,對於一個static的方法而言,無法訪問泛型類的類型參數,所以,如果static方法需要使用泛型能力,就必須使其成為泛型方法。也就是說,如果靜態方法要使用泛型的話,必須將靜態方法也定義成泛型方法

class Lalala<T>{

static <T> void xixixi(T t){}

}

如果你希望有多個不同種類的參數的話,可以將<T>擴展成<A,B,C,D>這樣的形式。

通配符:<? extends A>,這個類型參數可以代表A與所有繼承了A的類型,而<? super A> (超類型通配符)則代表了所有A與A的超類作為類型參數。更特殊的還有無界通配符 <?>,表示: 我是想用Java的泛型來編寫這段代碼,我在這里並不要用原生類型,但是在當前這種情況下,泛型參數可以持有任何類型。 比如List(相當於List<object>)和List<?>,前者表示持有任何Object類型的原生List,后者表示 具有某種特定類型的非原生List,只是我們不知道那種類型是什么。

(在定義對象時)這並不是說通配符限制了這個參數的范圍,恰恰相反,通配符反而讓不同類之間可以交流:

Class A{}

Class B extends A{}

Class G<T>{}

//下面這行就會報錯,盡管B的確是A的子類:不兼容的類型: C<B>無法轉換為C<A>

//G<A> g = new G<B>();

但是如果改成這樣

G<? extends A> g = new G<B>();

就不會報錯。

而當在定義類或方法時使用了通配符,那就限制了傳入參數的類型。

public class G<T extends A>{

private T key;

}

  • tip:如果你在定義類的時候使用了 class A<T extends E>,那你就真的限制了上界或者下界,注意與通配符區分。

盡管如此,以List作為G為例,你還是不能對g使用add()這樣的方法(除了add(null)),因為編譯器不知道g當時指向的會是哪一種具體的類型。你的g對象中可能有A的任何子類,由於向上轉型,到底是什么子類沒有人關心。

​ 對於參數值是未知類型的容器類,只能讀取其中元素,不能向其中添加元素, 因為,其類型是未知,所以編譯器無法識別添加元素的類型和容器的類型是否兼容,唯一的例外是NULL

那么如何添加元素進去?只要那個函數接受的參數是Object就行了。對於上述的add例子,它的參數隨着你創建g時變成了"? extends A",從這個描述中,編譯器不能了解具體需要的是那個子類,所以不會接受任何類型的A。而對於接受參數是Object類型的函數,不涉及任何通配符,編譯器就允許這個調用,add(Object o)這樣就可以了。

對於<? super T>:與上述例子對於,它的get方法你不清楚返回的是哪個類型。

有一種情況特別需要使用<?>而不是原生類型。如果向一個使用<?>的方法傳遞原生類型,那么對編譯器來說,可能會推斷出實際的類型參數,使得這種方法可以回轉並調用另一個使用這個確切類型的方法。這種技術成為捕獲轉換。捕獲轉換只有在這樣的情況下可以工作:即在方法內部,你需要使用確切的類型。比如說f2()使用的是<?>,但是在f2()中調用了需要確切類型的函數f1(),這時候就會使用到捕獲轉換。

 

擦除特性:

在泛型代碼內部,無法獲得任何有關泛型參數類型的信息。

不管你的類型參數是什么,SomeClass<A>和SomeClass<B>使用getClass得到的是一樣的。

如果你希望在SomeClass中調用A類的方法,會報錯,因為編譯器根本不知道你的類型參數A自己帶有的方法。

 

二、容器

​ ----事先注明,有些接口沒有介紹,所以有些方法要自己看看相應的類實現了哪些接口才知道----

容器的用途是“保存對象”。包括Collection,Map兩大類等。

類圖關系大致 如圖,但是實際上並不嚴謹,有些其他關系和抽象類什么的沒有畫出,在此僅為了表示一個直觀的大概的印象,方便初學者理清層次關系:

(另,有畫錯的地方,Deque應該是Queue的子接口,並且LinkedList同時也繼承並實現了它)

在介紹容器之前,先了解一下容器中存在的一個比較重要的成員:Iterator。

Iterator接口繼承了頂級接口Iterable,通常用來遍歷序列中的對象。

以Collection為例,它的用法是,當Collection的子類對象調用了iterator()方法,返回一個Iterator對象。該Iterator對象第一次調用next方法時,會獲得(返回)Collection對象中的第一個元素。(可以把迭代器理解成一個指針,每次調用next方法,就指向下一個元素)

遍歷時,通常用it.next()來表示遍歷過程中的對象。

而在List中有一個listIterator方法,返回一個ListIterator對象,從類圖中看,它可以雙向遍歷,也多了一些功能。要注意的是,add方法將指定元素插入當前位置之前。

(如果在遍歷過程中,用了remove方法,是不影響遍歷的,可以正常調用next等方法)。

除了ListIterator以外,還有可分割迭代器spliterator和倒序迭代器descendingIterator的存在。

 

一、Collection<T>

Collection<T>是一個泛型接口,List<T>,Set<T>是繼承它的接口,再往下就分別是實現了List的LinkedList,ArrayList(以及Vector接口和實現了Vector的Stack),還有實現了Set<T>的TreeSet(實際上TreeSet的上層是SortedTree<T>,SortedTree<T>是Set<T>的子接口)和HashSet。

Object類中的equals(Object)方法用於檢測一個對象是否等於另一個對象,在Object類中,這個方法判斷的根據是兩個對象是否具有相同的引用,如果是,那么就返回true。而hashCode方法(散列碼)是由對象導出得到的一個整型值,不同的對象得到的散列碼一般是不同的。由於hashCode方法定義在Object類中,因此每個對象都有一個默認的散列碼,也就是對象的存儲地址。如果需要覆寫equals方法時,必須也覆寫hashCode(也就是定義一個返回整型數的方法,這個整型數的產生方式要根據你的需要區分開不同的對象,比如調用該類中各個屬性的hashCode方法然后做個運算)。equals和hashCode的定義必須一致:如果兩個對象equals了,那么它們的散列碼就必須具有相同的值。

tips:在字符串類型中,散列碼是由內容導出的,所以內容相同散列碼也相同。而StringBuffer類中沒有定義hashCode方法,它的散列碼就還是對象的存儲地址。Double啊什么的類都覆寫了自己的根據內容比較的方法。

 

兩個toArray方法注意返回值,toArray()返回的是該Collectionl里所有元素對象的Object類型的數組(因此有時要注意轉化類型),而toArray(<T>[])里這個參數就是用來存儲這些元素的數組,返回的也正是這個數組。

范例:

String[] y = x.toArray(new String[0])

*@param  a the array into which the elements of this collection are to be
* stored, if it is big enough; otherwise, a new array of the same
*       runtime type is allocated for this purpose.

removeIf()方法移除的是符合參數格式的字符串。(有點python的列表解析時的感覺)

c.removeIf(obj -> obj%2 == 1)

最后強調一下,Collction接口中的方法,↓下面這些 都是有的,但是可能有些沒有畫在圖里,也沒有重復介紹了。

 

  1. List<T>:特點是有序,元素有存入的前后之分,以線性方式存儲。而且可以重復。

    要注意一件事:正如前文所說,類圖有很多沒有畫出來的其他類,比如一些中間的抽象類,這些對List等接口都是有影響的。如果你打開編輯器或IDE查看JDK中的源碼,會發現接口中有部分父接口聲明過的方法,在子接口中又重復聲明了,why? 搜索了一下,沒有得到一致准確的答案,但是大概可以確定的是,對菜雞的我和還需要看這篇文章學習的朋友來說,應該是體驗不到其中的差別的,只要照常調用方法就行了。所以這個坑先留着,有大神知道的話,希望一定要指導一下!

    listIterator(int index)方法是從index位置開始產生一個ListIterator對象,也就是第一次調用next()時返回的對象。

    subList(int fromIndex,int toIndex),左閉右開區間,返回一個新的List,並不修改原來的List。

    tip:

    在遍歷的過程中是不對List操作進行修改的,無論是刪除和添加,因為如果在遍歷中一直向集合中新增加元素,會造成死循環的,還有就是如果在遍歷過程中刪除元素,會造成數組下表越界等問題.

    ​ 所以一種操作就是先找到要刪的元素,放入一個List中,然后用removeAll方法。或者就直接用removeIf,亦或是用Iterator的remove方法在遍歷過程中刪除。

    ​ 接下來重點介紹一下實現了List接口的兩個類,

    1. LinkedList(√):底層數據結構是鏈表。線程不安全。把這個當成數據結構課自己實現過的鏈表用就行了,當你需要用鏈表時,建立一個LinkedList對象,鏈表的一般操作都已經幫你實現了。

      • All Implemented Interfaces:

        Serializable, Cloneable, Iterable<E>, Collection<E>, Deque<E>, List<E>, Queue<E>

      看實現了的接口,大概就知道LinkedList可以用來實現鏈表、隊列和雙端隊列了。

      方法還是挺多的,但是學過數據結構,基本看名字就知道有什么用了,也沒什么需要注意的。

      clone返回的是淺拷貝 @return a shallow copy of this {@code LinkedList} instance。

      peekelement的效果是基本一樣的,返回第一個元素,只是如果沒有元素時,peek返回null,element拋出一個異常。

      pollremove一樣,都是移除並返回第一個元素,但是為空時,前者返回null,后者拋異常。

      addoffer一樣,都是把一個元素加到最后,為空的時候,使用add方法會報錯,而offer方法會返回false。這兩個方法分別來自List和Queue(大概也可以根據自己要用LinkedList干什么來選方法)

      pop是加一個元素到front of the list,push就是返回並移除開頭的元素。

    2. ArrayList(√):底層數據結構是數組。線程不安全。粗略的把它當作一個能夠長度不定,操作靈活的數組就行了。

      • All Implemented Interfaces:

        Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess

      說白了就是把數組當成一個類,然后加入了一些對“數組”的方法操作。不過,很自然就能想到,便利的代價就是效率更低。

      sort方法就是傳入一個比較器參數,把ArrayList里的參數按比較器來排序。

      clone:@return a clone of this ArrayList instance,是淺拷貝。

      trimToSize方法可以修剪ArrayList的容量,因為ArrayList會預留一些空間,用這個方法可以刪除多余的。

      手動擴容: ensureCapacity(int minCapacity)

      forEach(Consumer<? super E> action) 對每個元素執行action操作。

    3. Vector:底層數據結構是數組。線程安全。Vector是List的子接口,和Queue是一個層次上的,而與實現Queue的Deque相對應的是,實現了Vector的Stack類。但是據說過時了,所以不介紹了,了解有這樣一個東西就好。

  2. Set元素不可重復 。同時沒有鏈表按序的特性。它最常被使用的是測試歸屬性(歸屬 性),可以很容易地詢問某個對象是否在某個Set中,因此,查找就成了Set中最重要的操作。Set具有和Collection完全一樣的接口,沒有額外的功能。所以把Collection的類圖,名字換成Set就行了。根據編程思想里的原話:

    實際上Set就是Collection,只是行為不同。

    (像LinkedList這些子接口會重復聲明父接口的部分方法,大概也是一樣的感覺?)

    Set是基於對象的值來確定歸屬性的。

    重點來看下面兩種實現類。

    1. TreeSet:基於TreeMap實現,其底層數據結構是紅黑樹(是一個自平衡的二叉樹),保證元素的排序方式,默認是自然順序(Comparable),也可以自定義比較器順序(Comparator)。

      • All Implemented Interfaces:

        Serializable, Cloneable, Iterable<E>, Collection<E>, NavigableSet<E>, Set<E>, SortedSet<E>

      實現了NavigableSet接口意味着它支持一系列的查找、定位的操作。

      TreeSet(NavigableMap\<E,Object> m) 構造方法通過傳遞的參數可以擴展對於邊界查詢的方法。

      TreeSet(Comparator<? super E> comparator) 傳遞的是一個Comparator參數,Comparator是一個接口,通過實現這個接口,可以傳遞一個自定義的排序的方式。元素將根據這個比較器進行排序。(一般是你傳入的自定義類,實現了Comparator接口,從而達到給該類元素排序的目的,后面的PriorityQueue一樣的道理)

      TreeSet(Collection<? extends E> c),創建了包含了Collection參數中所含有的元素的對象。

      TreeSet(SortedSet\<E> set),Constructs a new tree set containing the same elements and using the same ordering as the specified sorted set.和上面一個差不多但是連順序也記錄。

      public SortedSet\<E> subSet(E fromElement, E toElement)調用了另一個subSet函數,​ return subSet(fromElement, true, toElement, false);

      headSet(E toElement) 得到一個以 toElement為上界(不包括)的Set(Returns a view of the portion of this set whose elements are strictly less than toElement.),而headSet(E toElement, boolean inclusive) Returns a view of the portion of this set whose elements are less than (or equal to, if inclusive is true) toElement.,可以看出后面這個inclusive參數就是決定是否要包括toElement。

      tailSet類似,只不過是決定下界的。

      subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive),則是返回處於上界下界之中的Set,兩個Inclusive就不用解釋了把。subSet(E fromElement, E toElement) 對於這個方法來說,默認是fromInclusive是true的,而toInclusive是false的,也就是,含頭不含尾。

      由於TreeSet是有序的,所以就有first()方法返回the first (lowest) element,和它對應的是last()方法 。

      floor返回的是比floor的參數更小或相等的元素中最大的那個(因為它是地板嘛)。注意和lower的對比,lower是不包含相等的。 也就是 floor(E e)返回的是 element <= e 中最大的,lower則是 element < e。 與floor對應的是ceiling方法。

      對於pollFirst和pollLast,看到poll就應該猜到了,(Retrieves and removes the first (lowest) /last(highest) element, or returns null if this set is empty. ) 也就是說檢索到以后還要把它取出來。

      TreeSet的clone方法返回的是:a shallow copy of this set 淺拷貝。

    2. HashSet:底層數據結構是哈希表(是一個 元素 為鏈表 的 數組) 。和TreeSet不同,它的輸出順序是隨機的。

      • All Implemented Interfaces:

        Serializable, Cloneable, Iterable<E>, Collection<E>, Set<E>

      HashSet存儲的對象存取都是哈希值,自身先后通過hashcode()和equals()方法來判斷元素是否重復。哈希值就是元素的身份證(map中的Key)。當hashCode值不相同,就直接存儲了,當hashCode值相同時,會在判斷一次euqals方法的返回值是否為true,如果為true則視為用一個元素,不用存儲,如果為false,這些相同哈希值不同內容的元素都存放一個桶里(當哈希表中有一個桶結構,每一個桶都有一個哈希值)

      在添加自定義對象的時候,兩個類的屬性值相等,但是依然會被判定為不同的元素,因為沒有重寫hashCode(),所以默認調用的是Object類的hashCode(),而不同類的hashCode一般是不同的。所以當你以自定義類對象為類型參數的時候,記得要重寫。

      構造方法:

      HashSet構造的默認容量(initialCapacity)是16。

      HashSet(int initialCapacity, float loadFactor),前一個是初始容量可以理解,后一個load factor,加載因子(表示元素填滿的程度)。加載因子越高,空間開銷越小,但是查找的成本就會提高;反之,空間開銷大,查找的成本低。對於不用手動設置的三個初始化方式,它們的默認加載因子是0.75,是一個折中的值。(那個帶dummy的也是沒看懂)

      具體的解釋找到這一段:

      The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets. As a general rule, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the lookup cost (reflected in most of the operations of the HashMap class, including get and put). The expected number of entries in the map and its load factor should be taken into account when setting its initial capacity, so as to minimize the number of rehash operations. If the initial capacity is greater than the maximum number of entries divided by the load factor, no rehash operations will ever occur.

      還有這個(講HashMap的一篇很全面的博客:https://www.cnblogs.com/skywang12345/p/3310835.html)

      容量 是哈希表中桶的數量,初始容量 只是哈希表在創建時的容量。加載因子 是哈希表在其容量自動增加之前可以達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行 rehash 操作(即重建內部數據結構),從而哈希表將具有大約兩倍的桶數。通常,默認加載因子是 0.75, 這是在時間和空間成本上尋求一種折衷。加載因子過高雖然減少了空間開銷,但同時也增加了查詢成本(在大多數 HashMap 類的操作中,包括 get 和 put 操作,都反映了這一點)。在設置初始容量時應該考慮到映射中所需的條目數及其加載因子,以便最大限度地減少 rehash 操作次數。如果初始容量大於最大條目數除以加載因子,則不會發生 rehash 操作。

3.Queue:隊列的特征是先進先出,這部分功能在LinkedList的方法里實現了。(別忘了LinkedList也實現了DeQue接口,需要用到普通的隊列功能的時候別搞錯了)。

就這么幾種方法。和LinkedList對比一下,就可以知道哪部分是實現Queue的,當用作一個隊列的時候該用哪些方法。

而和Collection相比:provide additional insertion, extraction, and inspection operations.

  1. Deque子接口

    • All Superinterfaces:

      Collection<E>, Iterable<E>, Queue<E>

    主要方法如下:

    This interface extends the Queue interface. When a deque is used as a queue, FIFO (First-In-First-Out) behavior results. Elements are added at the end of the deque and removed from the beginning. The methods inherited from the Queue interface are precisely equivalent to Deque methods as indicated in the following table:

    Queue Method Equivalent Deque Method
    add(e) addLast(e)
    offer(e) offerLast(e)
    remove() removeFirst()
    poll() pollFirst()
    element() getFirst()
    peek() peekFirst()

    Deques can also be used as LIFO (Last-In-First-Out) stacks. This interface should be used in preference to the legacy Stack class. When a deque is used as a stack, elements are pushed and popped from the beginning of the deque. Stack methods are precisely equivalent to Deque methods as indicated in the table below:

    Stack Method Equivalent Deque Method
    push(e) addFirst(e)
    pop() removeFirst()
    peek() peekFirst()

    從摘自API文檔的這段介紹來看,當你需要FIFO結構時,意味着元素從隊尾進,從隊首出,就用繼承自Queue接口的方法,而這些方法有了含義更清晰的表達,也就是表格中的對應關系,addLast把新來的元素放到隊尾,效果和Queue的add一樣的。而當你需要LIFO的棧結構, 下面那個也是一樣的。

    另外還兩個方法:removeFirstOccurrence(Object o)和removeLastOccurrence(Object o),看方法名就知道是什么了。

  2. PriortyQueue:優先隊列。

    • All Implemented Interfaces:

      Serializable, Iterable, Collection, Queue

  • Constructor Description
    PriorityQueue() Creates a PriorityQueue with the default initial capacity (11) that orders its elements according to their natural ordering.
    PriorityQueue(int initialCapacity) Creates a PriorityQueue with the specified initial capacity that orders its elements according to their natural ordering.
    PriorityQueue(int initialCapacity, Comparator<? super E> comparator) Creates a PriorityQueue with the specified initial capacity that orders its elements according to the specified comparator.
    PriorityQueue(Collection<? extends E> c) Creates a PriorityQueue containing the elements in the specified collection.
    PriorityQueue(Comparator<? super E> comparator) Creates a PriorityQueue with the default initial capacity and whose elements are ordered according to the specified comparator.
    PriorityQueue(PriorityQueue<? extends E> c) Creates a PriorityQueue containing the elements in the specified priority queue.
    PriorityQueue(SortedSet<? extends E> c) Creates a PriorityQueue containing the elements in the specified sorted set.

可以看出和TreeSet有相似的地方,那就是可以傳入Comparator參數。而這就是PriorityQueue的特點。Comparator是一個接口,根據Comparator中實現的規則,可以改變你入列以后的排序順序。比如說你希望有一個按官職大小排隊的隊伍,在Comparator中實現了,傳參進來,然后來了個縣令,隊里只有一個人,又來了個捕頭,排第二沒毛病(這個排序不是因為FIFO,而是因為他官職更小),這時候來了個尚書,因為他官職最大,所以他就排隊到第一。出列的時候,沒有變,隊首出。這就是PriorityQueue。

其余的方法,看實現的接口就知道了,基本就是Queue的方法。

要說的話,public Comparator<? super E> comparator() { return comparator;} 這個方法可以返回該隊列的Comparator屬性(private final Comparator<? super E> comparator)。

 

 

二、Map

Map是和Collection一個層次上的接口,它的結構是key-value。

K,V兩個參數分別對應了key和value,這樣基本懂了大部分方法了。

entrySet、keySet、values三個方法分別得到所有鍵值的Set(元素類型是Map的內部接口Entry,Entry提供了一些操作)、鍵的Set、值的Collection。

三個帶compute的方法都是用來計算參數中的key對應的新value的。

getOrDefault返回key對應的value,如果key不存在的,就返回defaultValue。

putIfAbsent,如果不存在參數中的那個鍵值對或者值為空,就put。普通的put方法會直接覆蓋。

remove(Object key, Object value) 移除的前提是二者都要與map中的鍵值對匹配,才能執行刪除操作。

merge,如果參數中的鍵不存在或者值為空,就用value和它組成一對;如果存在的話,就用第三個參數計算出來的結果代替它。具體怎么用,和compute差不多,寫的時候還沒有試過……

 

  1. HashMap 散列表,存儲鍵值映射的數據,不保證映射的順序。 可以和TreeMap一起對比HashSet和TreeSet。

    • All Implemented Interfaces:

      Serializable, Cloneable, Map

    Constructor Description
    HashMap() Constructs an empty HashMap with the default initial capacity (16) and the default load factor (0.75).
    HashMap(int initialCapacity) Constructs an empty HashMap with the specified initial capacity and the default load factor (0.75).
    HashMap(int initialCapacity, float loadFactor) Constructs an empty HashMap with the specified initial capacity and load factor.
    HashMap(Map<? extends K,? extends V> m) Constructs a new HashMap with the same mappings as the specified Map.

    又是和Hash掛鈎的,所以又能看見load factor。

    clone方法照例 Returns a shallow copy

    沒有其他特別的方法。

  2. TreeMap: 底層實現基於紅黑樹。有序。

    • All Implemented Interfaces:

      Serializable, Cloneable, Map<K,V>, NavigableMap<K,V>, SortedMap<K,V>

    Constructor Description
    TreeMap() Constructs a new, empty tree map, using the natural ordering of its keys.
    TreeMap(Comparator<? super K> comparator) Constructs a new, empty tree map, ordered according to the given comparator.
    TreeMap(Map<? extends K,? extends V> m) Constructs a new tree map containing the same mappings as the given map, ordered according to the natural ordering of its keys.
    TreeMap(SortedMap<K,? extends V> m) Constructs a new tree map containing the same mappings and using the same ordering as the specified sorted map.
       

    方法還挺多的,但是由於實現都基於紅黑樹,原理相同,所以基本可以和TreeSet對標。

     

  3. LinkedHashMap:雙向鏈表+HashMap


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

也就是說實現了HashMap的按存放順序排列的功能。

附博客一篇:https://blog.csdn.net/justloveyou_/article/details/71713781

 

 

斷斷續續寫那么多,也沒什么規划,而且還有些理解上的錯誤沒改過來,恐怕其他人很難讀下去了……有堅持到這里的我表示深深的歉意。

寫這篇博客最主要還是記錄下自己學習的過程,一邊捋思路,一邊翻書,碰到難點了看博客,后面發現直接翻API更清楚,這個學習過程中得到的經驗才是這篇博客最重要的收獲,真正學到的,也就懶得再往回寫了。

等繼續深入容器的時候再來總結一下吧。


免責聲明!

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



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