java常用類-集合

collection1.png

  Java的類集(Collection)框架使你的程序處理對象組的方法標準化。類集框架被設計用于適應幾個目的今布。首先经备,這種框架是高性能的。對基本類集(動態(tài)數(shù)組部默,鏈接表侵蒙,樹和散列表)的實現(xiàn)是高效率的。一般很少需要人工去對這些“數(shù)據(jù)引擎”編寫代碼(如果有的話)傅蹂。第二點纷闺,框架必須允許不同類型的類集以相同的方式和高度互操作方式工作算凿。第三點,類集必須是容易擴展和/或修改的犁功。為了實現(xiàn)這一目標氓轰,類集框架被設計成包含一組標準的接口。對這些接口浸卦,提供了幾個標準的實現(xiàn)工具(例如LinkedList戒努,HashSet和TreeSet),通常就是這樣使用的镐躲。如果你愿意的話,也可以實現(xiàn)你自己的類集侍筛。為了方便起見萤皂,創(chuàng)建用于各種特殊目的的實現(xiàn)工具。一部分工具可以使你自己的類集實現(xiàn)更加容易匣椰。最后裆熙,增加了允許將標準數(shù)組融合到類集框架中的機制。
Set接口:
  集合接口定義了一個集合禽笑。它擴展了Collection并說明了不允許復制元素的類集的特性入录。因此,如果試圖將復制元素加到集合中時佳镜,add()方法將返回false(不拋異常)僚稿。
HashSet:
  對于 HashSet 而言,它是基于 HashMap 實現(xiàn)的蟀伸,HashSet 底層采用 HashMap 來保存所有元素蚀同,因此 HashSet 的實現(xiàn)比較簡單,它只是封裝了一個 HashMap 對象來存儲所有的集合元素啊掏,所有放入 HashSet 中的集合元素實際上由 HashMap 的 key 來保存蠢络,而 HashMap 的 value 則存儲了一個 PRESENT,它是一個靜態(tài)的 Object 對象迟蜜。 HashSet 的絕大部分方法都是通過調(diào)用 HashMap 的方法來實現(xiàn)的刹孔,因此 HashSet 和 HashMap 兩個集合在實現(xiàn)本質(zhì)上是相同的。

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

TreeSet:
  TreeSet是實現(xiàn)了SortedSet接口娜睛,SortedSet接口定義了幾種方法髓霞,使得對集合的處理更加方便。調(diào)用first()方法微姊,可以獲得集合中的第一個對象酸茴。調(diào)用last()方法,可以獲得集合中的最后一個元素兢交。調(diào)用subSet()方法薪捍,可以獲得排序集合的一個指定了第一個和最后一個對象的子集合。如果需要得到從集合的第一個元素開始的一個子集合,可以使用headSet()方法酪穿。如果需要獲得集合尾部的一個子集合凳干,可以使用tailSet()方法。
  TreeSet支持兩種排序方法:自然排序和定制排序被济。
  自然排序:TreeSet調(diào)用集合元素的compareTo(Object obj)方法來比較元素之間的大小救赐,然后按照元素升序排列。這就要求對象元素必須實現(xiàn)Comparable接口中的compareTo(Object obj)方法只磷。在自然排序下经磅,TreeSet判斷兩個對象是否相同的唯一標準:兩個對象通過compareTo(Object o)方法比較是否相等:該方法返回0,則認為相等钮追,返回1則認為不相等预厌。java的一些常見的類,例如Character元媚,String轧叽,Date等已經(jīng)實現(xiàn)了Comparable接口。
  定制排序:在創(chuàng)建TreeSet集合對象時刊棕,需要關(guān)聯(lián)一個Comparator對象炭晒,并且實現(xiàn)Comparator中的compare(T obj1,T obj 2)。當通過Comparator對象實現(xiàn)TreeSet的定制排序時甥角,也不可以向TreeSet中添加不同類型的對象(public TreeSet(Comparator<? super E> comparator))网严。

public class Text {
    public static void main(String[] args) {
        TreeSet<String> set = new TreeSet<String>();
                set.add("c");
        set.add("a");
        set.add("ef");
        set.add("b");
        set.add("e");
        set.add("d");
        SortedSet<String> subSet1 = set.subSet("b", "e");
        System.out.println(subSet1); //[b, c, d]
        SortedSet<String> subSet2 = set.headSet("b");
        System.out.println(subSet2);  //[a]
        SortedSet<String> subSet3 = set.tailSet("e");
        System.out.println(subSet3);  //[e, ef]
    }
}

用Set獲取數(shù)據(jù)時只能用迭代或者foreach來取值。
List接口
  List是有序的Collection嗤无,使用此接口能夠精確的控制每個元素插入的位置屿笼。用戶能夠使用索引(元素在List中的位置,類似于數(shù)組下 >標)來訪問List中的元素翁巍,這類似于Java的數(shù)組驴一。
ArrayList:
  ArrayList是一個基于數(shù)組上的鏈表,它擴展AbstractList并執(zhí)行List接口灶壶。ArrayList支持可隨需要而增長的動態(tài)數(shù)組肝断。在Java中,標準數(shù)組是定長的驰凛。在數(shù)組創(chuàng)建之后胸懈,它們不能被加長或縮短,這也就意味著你必須事先知道數(shù)組可以容納多少元素恰响。但是趣钱,你直到運行時才能知道需要多大的數(shù)組。為了解決這個問題胚宦,類集框架定義了ArrayList首有。本質(zhì)上燕垃,ArrayList是對象引用的一個變長數(shù)組。也就是說井联,ArrayList能夠動態(tài)地增加或減小其大小卜壕。數(shù)組列表以一個原始大小被創(chuàng)建。當超過了它的大小烙常,類集自動增大(當前長度的1.5倍轴捎,初始化長度為10,這里可以用初始化長度來進行性能優(yōu)化)蚕脏。
  當使用ArrayList時侦副,有時想要獲得一個實際的數(shù)組,這個數(shù)組包含了列表的內(nèi)容驼鞭≡韭澹可以通過調(diào)用方法toArray()來實現(xiàn)它。(數(shù)組轉(zhuǎn)list:Arrays.asList()返回一個受指定數(shù)組支持的固定大小的列表)

public class Text {
    public static void main(String[] args) {
        String[] array = new String[2];
        List<String> list2 = Arrays.asList(array);
        array[0] = "test2";
        System.out.println(list2);
        list2.add("test");
        System.out.println(list2);
    }
}

在add時會拋出UnsupportedOperationException终议,原因是asList返回的是一個Array的一個內(nèi)部類而不是真正的ArrayList:

image.png

Vector:
  同ArrayList一樣是一個基于數(shù)組上的鏈表,但是不同的是Vector是同步的葱蝗。
LinkedList:
  與ArrayList一樣實現(xiàn)List接口穴张,只是ArrayList是List接口的大小可變數(shù)組的實現(xiàn),LinkedList是List接口鏈表的實現(xiàn)两曼≡砀剩基于鏈表實現(xiàn)的方式使得LinkedList在插入和刪除時更優(yōu)于ArrayList,而隨機訪問則比ArrayList遜色些悼凑。
  LinkedList除了它繼承的方法之外偿枕,LinkedList類本身還定義了一些有用的方法,這些方法主要用于操作和訪問列表户辫。使用addFirst()方法可以在列表頭增加元素渐夸;使用。使用add()方法的add(int, Object)形式渔欢,插入項目到指定的位置墓塌。調(diào)用getFirst()方法可以獲得第一個元素。調(diào)用getLast()方法可以得到最后一個元素奥额。為了刪除第一個元素苫幢,可以使用removeFirst()方法;為了刪除最后一個元素垫挨,可以調(diào)用removeLast()方法韩肝。

List 在很多方面跟 Array 數(shù)組感覺很相似,尤其是 ArrayList九榔,那 List 和數(shù)組究竟哪個更好呢哀峻?
  相似之處:
    都可以表示一組同類型的對象
    都使用下標進行索引
  不同之處:
    數(shù)組可以存任何類型元素
    List 不可以存基本數(shù)據(jù)類型涡相,必須要包裝
    數(shù)組容量固定不可改變;List 容量可動態(tài)增長
    數(shù)組效率高; List 由于要維護額外內(nèi)容谜诫,效率相對低一些

Map 接口:
  Map用于保存具體映射關(guān)系的數(shù)據(jù)漾峡,因此Map集合里保存著兩組值,一組值用于保存Map里的key喻旷,另外一組值用于保存Map里的value生逸,key和value都可以是任意類型的數(shù)據(jù)。Map的key不允許重復且预,即同一個Map對象的任何兩個key通過equals方法比較總是返回false槽袄,每個key只能映射一個value。Map接口提供3種集合的視圖锋谐,Map的內(nèi)容可以被當作一組key集合遍尺,一組value集合,或者一組key-value映射涮拗。
HashMap:
  對于 HashMap 而言乾戏,系統(tǒng) key-value 當成一個整體進行處理,系統(tǒng)總是根據(jù) Hash 算法來計算 key-value 的存儲位置三热,這樣可以保證能快速存鼓择、取 Map 的 key-value 對。
  當程序執(zhí)行 put(key, value)方法時就漾,系統(tǒng)將調(diào)用key的 hashCode() 方法得到其 hashCode 值——每個 Java 對象都有 hashCode() 方法呐能,都可通過該方法獲得它的 hashCode 值。得到這個對象的 hashCode 值之后抑堡,系統(tǒng)會根據(jù)該 hashCode 值來決定該元素的存儲位置摆出。
  取值時,HashMap 可以根據(jù)索引首妖、快速地取出該 bucket 里的 Entry偎漫;在發(fā)生“Hash 沖突”的情況下,單個 bucket 里存儲的不是一個 Entry有缆,而是一個 Entry 鏈骑丸,系統(tǒng)只能必須按順序遍歷每個 Entry,直到找到想搜索的 Entry 為止

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }
 public V get(Object key) {
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);
        return null == entry ? null : entry.getValue();
    }
final Entry<K,V> getEntry(Object key) {
        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

Hashtable:
  Hashtable(先存在的)幾乎可以等價于HashMap妒貌。與HashMap相比Hashtable是synchronized通危,這意味著Hashtable是線程安全的,多個線程可以共享一個Hashtable灌曙;而如果沒有正確的同步的話菊碟,多個線程是不能共享HashMap的。Hashtable是線程安全的在刺,性能上就要比HashMap差一點逆害。
  另一個區(qū)別是HashMap的迭代器(Iterator)是fail-fast迭代器头镊,而Hashtable的enumerator迭代器不是fail-fast的。所以當有其它線程改變了HashMap的結(jié)構(gòu)(增加或者移除元素)魄幕,將會拋出ConcurrentModificationException相艇,但迭代器本身的remove()方法移除元素則不會拋出ConcurrentModificationException異常。但這并不是一個一定發(fā)生的行為纯陨,要看JVM坛芽。這條同樣也是Enumeration和Iterator的區(qū)別。
  Hashtable不可以接受null(HashMap可以接受為null的鍵值(key)和值(value))翼抠。

public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }
        // Makes sure the key is not already in the hashtable.
        Entry tab[] = table;
        int hash = hash(key);
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                V old = e.value;
                e.value = value;
                return old;
            }
        }
        modCount++;
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            rehash();
            tab = table;
            hash = hash(key);
            index = (hash & 0x7FFFFFFF) % tab.length;
        }
        // Creates the new entry.
        Entry<K,V> e = tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
        return null;
    }
int hash(Object k) {
        if (useAltHashing) {
            if (k.getClass() == String.class) {
                return sun.misc.Hashing.stringHash32((String) k);
            } else {
                int h = hashSeed ^ k.hashCode();

                // This function ensures that hashCodes that differ only by
                // constant multiples at each bit position have a bounded
                // number of collisions (approximately 8 at default load factor).
                h ^= (h >>> 20) ^ (h >>> 12);
                return h ^ (h >>> 7) ^ (h >>> 4);
             }
        } else  {
            return k.hashCode();
        }
    }

ConcurrentHashMap:
  ConcurrentHashMap也是題中線程安全的map咙轩,他融合了hashtable和hashmap二者的優(yōu)勢。

鎖.png

  圖左側(cè)清晰的標注出來阴颖,lock每次都要鎖住整個結(jié)構(gòu)活喊。
  ConcurrentHashMap正是為了解決這個問題而誕生的。
  ConcurrentHashMap鎖的方式是稍微細粒度的量愧。 ConcurrentHashMap將hash表分為16個桶(默認值)钾菊,諸如get,put,remove等常用操作只鎖當前需要用到的桶。
  試想偎肃,原來 只能一個線程進入煞烫,現(xiàn)在卻能同時16個寫線程進入(寫線程才需要鎖定,而讀線程幾乎不受限制软棺,之后會提到),并發(fā)性的提升是顯而易見的尤勋。
  更令人驚訝的是ConcurrentHashMap的讀取并發(fā)喘落,因為在讀取的大多數(shù)時候都沒有用到鎖定,所以讀取操作幾乎是完全的并發(fā)操作最冰,而寫操作鎖定的粒度又非常細瘦棋,比起之前又更加快速(這一點在桶更多時表現(xiàn)得更明顯些)。只有在求size等操作時才需要鎖定整個表暖哨。
(http://www.importnew.com/22007.html赌朋;http://blog.csdn.net/wisgood/article/details/19338693)
附:Collections.synchronizedMap()是Collections提供的同步map方法,他的實現(xiàn)還是很簡單的篇裁,使用了 synchronized 同步關(guān)鍵字來保證對 Map 的操作是線程安全的沛慢。

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
        return new SynchronizedMap<>(m);
    }

    /**
     * @serial include
     */
    private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;

        private final Map<K,V> m;     // Backing Map
        final Object      mutex;        // Object on which to synchronize

        SynchronizedMap(Map<K,V> m) {
            if (m==null)
                throw new NullPointerException();
            this.m = m;
            mutex = this;
        }

        SynchronizedMap(Map<K,V> m, Object mutex) {
            this.m = m;
            this.mutex = mutex;
        }

        public int size() {
            synchronized (mutex) {return m.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return m.isEmpty();}
        }
        public boolean containsKey(Object key) {
            synchronized (mutex) {return m.containsKey(key);}
        }
        public boolean containsValue(Object value) {
            synchronized (mutex) {return m.containsValue(value);}
        }
        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }
        //......
}

性能:ConcurrentHashMap性能是明顯優(yōu)于Hashtable和SynchronizedMap的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末达布,一起剝皮案震驚了整個濱河市团甲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌黍聂,老刑警劉巖躺苦,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件身腻,死亡現(xiàn)場離奇詭異,居然都是意外死亡匹厘,警方通過查閱死者的電腦和手機嘀趟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愈诚,“玉大人她按,你說我怎么就攤上這事∪怕罚” “怎么了尤溜?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長汗唱。 經(jīng)常有香客問我宫莱,道長,這世上最難降的妖魔是什么哩罪? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任授霸,我火速辦了婚禮,結(jié)果婚禮上际插,老公的妹妹穿的比我還像新娘碘耳。我一直安慰自己,他們只是感情好框弛,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布辛辨。 她就那樣靜靜地躺著,像睡著了一般瑟枫。 火紅的嫁衣襯著肌膚如雪斗搞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天慷妙,我揣著相機與錄音僻焚,去河邊找鬼。 笑死膝擂,一個胖子當著我的面吹牛虑啤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播架馋,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼狞山,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了叉寂?” 一聲冷哼從身側(cè)響起铣墨,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎办绝,沒想到半個月后伊约,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姚淆,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年屡律,在試婚紗的時候發(fā)現(xiàn)自己被綠了腌逢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡超埋,死狀恐怖搏讶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情霍殴,我是刑警寧澤媒惕,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站来庭,受9級特大地震影響妒蔚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜月弛,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一肴盏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧帽衙,春花似錦菜皂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谴垫,卻和暖如春章母,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弹渔。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工胳施, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留溯祸,地道東北人肢专。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像焦辅,于是被迫代替她去往敵國和親博杖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法筷登,類相關(guān)的語法剃根,內(nèi)部類的語法,繼承相關(guān)的語法前方,異常的語法狈醉,線程的語...
    子非魚_t_閱讀 31,623評論 18 399
  • Collection & Map Collection 子類有 List 和 Set List --> Array...
    任教主來也閱讀 3,162評論 1 9
  • 集合類簡介 為什么出現(xiàn)集合類?面向?qū)ο笳Z言對事物的體現(xiàn)都是以對象的形式渣慕,所以為了方便對多個對象的操作嘶炭,就要對對象進...
    阿敏其人閱讀 1,417評論 0 7
  • Collection ├List │├LinkedList │├ArrayList │└Vector │└Stac...
    AndyZX閱讀 874評論 0 1
  • 以下是《瘋狂Java講義》中的一些知識,如有錯誤逊桦,煩請指正眨猎。 集合概述 Java集合可以分為Set、List强经、Ma...
    hainingwyx閱讀 538評論 0 1