java集合(容器)

一. 集合類帶來的好處

  1. 降低編程難度:在編程中會經(jīng)常需要鏈表向量等集合類题诵,如果自己動手寫代碼實現(xiàn)這些類层皱,需要花費較多的時間和精力性锭。調(diào)用Java中提供的這些接口和類,可以很容易的處理數(shù)據(jù)叫胖。

  2. 提升程序的運行速度和質(zhì)量:Java提供的集合類具有較高的質(zhì)量草冈,運行時速度也較快。使用這些集合類提供的數(shù)據(jù)結(jié)構(gòu)瓮增,程序員可以從“重復(fù)造輪子”中解脫出來怎棱,將精力專注于提升程序的質(zhì)量和性能。

  3. 無需再學(xué)習(xí)新的APl:借助泛型绷跑,只要了解了這些類的使用方法拳恋,就可以將它們應(yīng)用到很多數(shù)據(jù)類型中。如果知道了LinkedList<String>的使用方法砸捏,也會知道LinkedList<Double>怎么用谬运,則無需為每一種數(shù)據(jù)類型學(xué)習(xí)不同的API。

  4. 增加代碼重用性:也是借助泛型垦藏,就算對集合類中的元素類型進(jìn)行了修改梆暖,集合類相關(guān)的代碼也幾乎不用修改。

    集合類的特點有三個:

    1. 集合類這種框架是高性能的掂骏。對基本類集(動態(tài)數(shù)組轰驳,鏈接表,樹和散列表)的實現(xiàn)是高效率的弟灼。一般人很少去改動這些已經(jīng)很成熟并且高效的APl滑废;
    2. 集合類允許不同類型的集合以相同的方式和高度互操作方式工作;
    3. 集合類容易擴展和修改袜爪,程序員可以很容易地稍加改造就能滿足自己的數(shù)據(jù)結(jié)構(gòu)需求。

二. 集合架構(gòu)

image

Java中的集合類可以分為兩大類:一類是實現(xiàn)Collection接口薛闪;另一類是實現(xiàn)Map接口辛馆。

三. 實現(xiàn)Collection接口的集合區(qū)別

所有Java集合類都位于Java.util包中,與Java數(shù)組不同豁延,Java集合不能存放基本數(shù)據(jù)類型數(shù)據(jù)昙篙,而只能存放對象的引用

1. List集合:有序列表诱咏,允許存放重復(fù)的元素

ArrayList和LinkedList在用法上沒有區(qū)別苔可,但是在功能上還是有區(qū)別的。

  • ArrayList:數(shù)組實現(xiàn)袋狞,查詢快焚辅,增刪慢映屋,輕量級;(線程不安全)同蜻,底層是Object數(shù)組棚点,所以ArrayList具有數(shù)組的查詢速度快的優(yōu)點以及增刪速度慢的缺點。
  • LinkedList:雙向鏈表實現(xiàn)湾蔓,增刪快瘫析,查詢慢 (線程不安全),底層是一種雙向循環(huán)鏈表默责。在此鏈表上每一個數(shù)據(jù)節(jié)點都由三部分組成:前指針(指向前面的節(jié)點的位置)贬循,數(shù)據(jù),后指針(指向后面的節(jié)點的位置)桃序。最后一個節(jié)點的后指針指向第一個節(jié)點的前指針杖虾,形成一個循環(huán)。雙向循環(huán)鏈表的查詢效率低但是增刪效率高葡缰。利用LinkedList實現(xiàn)棧(stack)亏掀、隊列(queue)、雙向隊列(double-ended queue )泛释。 它具有方法addFirst()滤愕、addLast()、getFirst()怜校、getLast()间影、removeFirst()、removeLast()等茄茁。
  • Vector:與ArrayList相似魂贬,區(qū)別是Vector是重量級的組件,使用使消耗的資源比較多裙顽。在考慮并發(fā)的情況下用Vector(保證線程的安全),在不考慮并發(fā)的情況下用ArrayList(不能保證線程的安全)付燥。

2. set集合:無序列表,不重復(fù)的數(shù)據(jù)

Set接口的特點是無序的愈犹,不重復(fù)的數(shù)據(jù)键科。對Set中任意的兩個元素element1和element2都elementl.equals(element2)= false。另外漩怎,Set最多有一個null元素勋颖。此接口模仿了數(shù)學(xué)上的集合概念。檢索效率低下勋锤,刪除和插入效率高饭玲,插入和刪除不會引起元素位置改變。

HashSet

直接實現(xiàn)了Set接口叁执,其底層其實是包裝了一個HashMap去實現(xiàn)的茄厘。HashSet采用HashCode算法來存取集合中的元素矮冬,只不過生成一個HashSet的話,系統(tǒng)只提供key的訪問蚕断; 如果有兩個Key重復(fù)欢伏,那么會覆蓋之前的;equals返回true亿乳,hashCode返回相同的整數(shù)硝拧;哈希表;存儲的數(shù)據(jù)是無序的葛假。因此具有比較好的讀取和查找性能障陶。線程非安全。HashSet元素值可以為NULL聊训。

HashSet常用方法:
public boolean contains(Object o) :如果set包含指定元素抱究,返回true
public Iterator iterator()返回set中元素的迭代器
public Object[] toArray() :返回包含set中所有元素的數(shù)組public Object[] toArray(Object[] a) :返回包含set中所有元素的數(shù)組,返回數(shù)組的運行時類型是指定數(shù)組的運行時類型
public boolean add(Object o) :如果set中不存在指定元素带斑,則向set加入
public boolean remove(Object o) :如果set中存在指定元素鼓寺,則從set中刪除
public boolean removeAll(Collection c) :如果set包含指定集合,則從set中刪除指定集合的所有元素
public boolean containsAll(Collection c) :如果set包含指定集合的所有元素勋磕,返回true妈候。如果指定集合也是一個set,只有是當(dāng)前set的子集時挂滓,方法返回true

HashSet的equals和HashCode:

前面說過苦银,Set集合是不允許重復(fù)元素的,否則將會引發(fā)各種奇怪的問題赶站。那么HashSet如何判斷元素重復(fù)呢幔虏?
HashSet需要同時通過equals和HashCode來判斷兩個元素是否相等,具體規(guī)則是贝椿,如果兩個元素通過equals為true想括,并且兩個元素的hashCode相等,則這兩個元素相等(即重復(fù))烙博。
所以如果要重寫保存在HashSet中的對象的equals方法瑟蜈,也要重寫hashCode方法,重寫前后hashCode返回的結(jié)果相等(即保證保存在同一個位置)习勤。所有參與計算 hashCode() 返回值的關(guān)鍵屬性,都應(yīng)該用于作為 equals() 比較的標(biāo)準(zhǔn)焙格。
試想如果重寫了equals方法但不重寫hashCode方法图毕,即相同equals結(jié)果的兩個對象將會被HashSet當(dāng)作兩個元素保存起來,這與我們設(shè)計HashSet的初衷不符(元素不重復(fù))眷唉。
另外如果兩個元素哈市Code相等但equals結(jié)果不為true予颤,HashSet會將這兩個元素保存在同一個位置囤官,并將超過一個的元素以鏈表方式保存,這將影響HashSet的效率蛤虐。
如果重寫了equals方法但沒有重寫hashCode方法党饮,則HashSet可能無法正常工作,比如下面的例子驳庭。

import java.util.*;
public class Tests {
    private int count;
    public Tests(int count) {
        this.count = count;
    }
    @Override
    public String toString() {
        return "Tests{" + "count=" + count + " # hashCode=" + this.hashCode() + '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Tests r = (Tests) o;
        return count == r.count;
    }
//    @Override
//    public int hashCode() {
//        return Objects.hash(count);
//    }
    public static void main(String[] args) {
        Set set = new HashSet();
        set.add(new Tests(5));
        set.add(new Tests(-3));
        set.add(new Tests(9));
        set.add(new Tests(-2));
        System.out.println(set.contains(new Tests(-3)));
        System.out.println(set);
    }
}

注釋掉hashCode方法刑顺,運行結(jié)果:
false
[Tests{count=9 # hashCode=834600351}, Tests{count=-2 # hashCode=471910020}, Tests{count=-3 # hashCode=791452441}, Tests{count=5 # hashCode=321001045}]

取消注釋,運行結(jié)果:
true
[Tests{count=5 # hashCode=36}, Tests{count=9 # hashCode=40}, Tests{count=-3 # hashCode=28}, Tests{count=-2 # hashCode=29}]

如何達(dá)到不能存在重復(fù)元素的目的饲常?

“鍵”就是我們要存入的對象蹲堂,“值”則是一個常量。這樣可以確保贝淤,我們所需要的存儲的信息是“鍵”柒竞。而“鍵”在Map中是不能重復(fù)的,這就保證了我們存入Set中的所有的元素都不重復(fù)播聪。
HashSet如何過濾重復(fù)元素
調(diào)用元素HashCode獲得哈希碼--》判斷哈希碼是否相等朽基,不相等則錄入 ---》相等則判斷equals()后是否相等,不相等在進(jìn)行 hashcode錄入离陶,相等不錄入

LinkedHashSet

是HashSet的一個子類稼虎,LinkedHashSet也根據(jù)HashCode的值來決定元素的存儲位置,但同時它還用一個鏈表來維護元素的插入順序枕磁,插入的時候即要計算hashCode又要維護鏈表渡蜻,而遍歷的時候只需要按鏈表來訪問元素。此實現(xiàn)與HashSet的不同之外在于计济,后者維護著一個運行于所有條目的雙重鏈接列表茸苇。存儲的數(shù)據(jù)是有序的。查看LinkedHashSet的源碼發(fā)現(xiàn)它是樣的沦寂。

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {

    private static final long serialVersionUID = -2851667679971038690L;

    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }
....


  

JAVA8中学密, LinkedHashSet沒有定義任何方法,只有四個構(gòu)造函數(shù)传藏,它的構(gòu)造函數(shù)調(diào)用了父類(HashSet)的帶三個參數(shù)的構(gòu)造方法腻暮,父類的構(gòu)造函數(shù)如下:

 /**
     * Constructs a new, empty linked hash set.  (This package private
     * constructor is only used by LinkedHashSet.) The backing
     * HashMap instance is a LinkedHashMap with the specified initial
     * capacity and the specified load factor.
     *
     * @param      initialCapacity   the initial capacity of the hash map
     * @param      loadFactor        the load factor of the hash map
     * @param      dummy             ignored (distinguishes this
     *             constructor from other int, float constructor.)
     * @throws     IllegalArgumentException if the initial capacity is less
     *             than zero, or if the load factor is nonpositive
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

LinkedHashSet本質(zhì)上也是從LinkedHashMap而來,LinkedHashSet的所有方法都繼承自HashSet, 而它能維持元素的插入順序的性質(zhì)則繼承自LinkedHashMap.

下面是一個LinkedHashSet維持元素插入順序的例子

import java.util.LinkedHashSet;
import java.util.Set;
public class Tests {
    public static void main(String[] args) {
        Set set = new LinkedHashSet();
        set.add("abc");
        set.add("efg");
        set.add("hjk");
        System.out.println(set);
        set.remove(new String("abc"));
        set.add("abc");
        System.out.println(set);
    }
}

運行結(jié)果:
[abc, efg, hjk]
[efg, hjk, abc]

TreeSet類的特征

TreeSet實現(xiàn)了SortedSet接口毯侦,顧名思義這是一種排序的Set集合哭靖,查看jdk源碼發(fā)現(xiàn)底層是用TreeMap實現(xiàn)的,本質(zhì)上是一個紅黑樹原理侈离。 正因為它是排序了的试幽,所以相對HashSet來說,TreeSet提供了一些額外的按排序位置訪問元素的方法卦碾,例如first(), last(), lower(), higher(), subSet(), headSet(), tailSet().

TreeSet的排序分兩種類型铺坞,一種是自然排序起宽,另一種是定制排序。

自然排序(在元素中寫排序規(guī)則)

TreeSet 會調(diào)用compareTo方法比較元素大小济榨,然后按升序排序坯沪。所以自然排序中的元素對象,都必須實現(xiàn)了Comparable接口擒滑,否則會拋出異常腐晾。對于TreeSet判斷元素是否重復(fù)的標(biāo)準(zhǔn),也是調(diào)用元素從Comparable接口繼承而來額compareTo方法橘忱,如果返回0則是重復(fù)元素(兩個元素I相等)赴魁。Java的常見類都已經(jīng)實現(xiàn)了Comparable接口,

下面舉例說明沒有實現(xiàn)Comparable存入TreeSet時引發(fā)異常的情況钝诚。

import java.util.Set;
import java.util.TreeSet;
public class TestSets {
    public static void main(String[] args) {
        Set set = new TreeSet();
        set.add(new Err());
        set.add(new Err());
        set.add(new Err());
        System.out.println(set);
    }
}

class Err {
}

運行程序會拋出如下異常:
Exception in thread "main" java.lang.ClassCastException: clc.Err cannot be cast to java.lang.Comparable
  at java.util.TreeMap.compare(TreeMap.java:1294)
  at java.util.TreeMap.put(TreeMap.java:538)
  at java.util.TreeSet.add(TreeSet.java:255)
  at clc.TestSets.main(TestSets.java:17)

將上面的Err類實現(xiàn)Comparable接口之后程序就能正常運行了

class Err implements Comparable{
    @Override
    public int compareTo(Object o) {
        return 0;
    }
}

還有個重要問題是颖御,因為TreeSet會調(diào)用元素的compareTo方法,這就要求所有元素的類型都相同凝颇,否則也會發(fā)生異常潘拱。也就是說,TreeSet只允許存入同一類的元素拧略。例如下面這個例子就會拋出類型轉(zhuǎn)換異常

public static void main(String[] args) {
  Set set = new TreeSet();
  set.add(1);
  set.add("2");
  System.out.println(set);
}

運行結(jié)果:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.util.Date
  at java.util.Date.compareTo(Date.java:131)
  at java.util.TreeMap.put(TreeMap.java:568)
  at java.util.TreeSet.add(TreeSet.java:255)
  at clc.TestSets.main(TestSets.java:19)
定制排序(在集合中寫排序規(guī)則)

TreeSet還有一種排序就是定制排序芦岂,定制排序時候,需要關(guān)聯(lián)一個Comparator對象垫蛆,由Comparator提供排序邏輯禽最。下面就是一個使用Lambda表達(dá)式代替Comparator對象來提供定制排序的例子。下面是一個定制排序的列子:類似于C++容器sort()的參數(shù)bool cmp(){}函數(shù)袱饭。

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Tests {
    public static void main(String[] args) {
        Set set = new TreeSet(new MyCommpare());
        set.add(new M(5));
        set.add(new M(3));
        set.add(new M(9));
        System.out.println(set);
    }
}
class M {
    int age;
    public M(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "age:" + age;
    }
}
class MyCommpare implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {
        M m1 = (M) o1;
        M m2 = (M) o2;
        return m1.age > m2.age ? 1 : m1.age < m2.age ? -1 : 0;
    }
}

運行結(jié)果:
[age:3, age:5, age:9]

當(dāng)然將Comparator直接寫入TreeSet初始化中也可以川无。如下:

import org.junit.Test;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Tests {
    public static void main(String[] args) {
        Set set = new TreeSet(new MyCommpare());
        set.add(new M(5));
        set.add(new M(3));
        set.add(new M(9));
        System.out.println(set);
    }
    /**
     * Description: 將Comparator直接寫入TreeSet初始化中1
     */
    @Test
    public void testTreeSet() {
        Set set = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                M m1 = (M) o1;
                M m2 = (M) o2;
                return m1.age > m2.age ? 1 : m1.age < m2.age ? -1 : 0;
            }
        });
        set.add(new M(5));
        set.add(new M(3));
        set.add(new M(9));
        System.out.println(set);
    }
    /**
     * Description: 將Comparator直接寫入TreeSet初始化中
     */
    @Test
    public void testTreeSetLam() {
        Set set = new TreeSet((o1, o2) -> {
            M m1 = (M) o1;
            M m2 = (M) o2;
            return m1.age > m2.age ? 1 : m1.age < m2.age ? -1 : 0;
        });
        set.add(new M(5));
        set.add(new M(3));
        set.add(new M(9));
        System.out.println(set);
    }
  
}

class M {
    int age;
    public M(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "age:" + age;
    }
}

class MyCommpare implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {
        M m1 = (M) o1;
        M m2 = (M) o2;
        return m1.age > m2.age ? 1 : m1.age < m2.age ? -1 : 0;
    }
}

class Err implements Comparable {
    @Override
    public int compareTo(Object o) {
        return 0;
    }
}

運行結(jié)果: 
[age:3, age:5, age:9]

TreeSet是一個有序集合,TreeSet中元素將按照升序排列虑乖,缺省是按照自然順序進(jìn)行排列懦趋,意味著TreeSet中元素要實現(xiàn)Comparable接口。我們可以在構(gòu)造TreeSet對象時疹味,傳遞實現(xiàn)了Comparator接口的比較器對象仅叫。

Comparable和Comparator
Comparable 接口以提供自然排序順序。
對于那些沒有自然順序的類糙捺、或者當(dāng)您想要一個不同于自然順序的順序時诫咱,您可以實現(xiàn)Comparator 接口來定義您自己的排序函數(shù)『榈疲可以將Comparator傳遞給Collections.sort或Arrays.sort坎缭。

Comparator接口
當(dāng)一個類并未實現(xiàn)Comparable,或者不喜歡缺省的Comaparable行為』盟可以實現(xiàn)Comparator接口
直接實現(xiàn)Comparator的compare接口完成自定義比較類。
例:Arrays.sort(results, new Comparator<RepDataQueryResultVO>() 數(shù)組排序 RepDataQueryExecutor
例:Collections.sort(lst,new Comparator<TaskPrintSchemeVO>()

  • EnumSet特征
    EnumSet顧名思義就是專為枚舉類型設(shè)計的集合边臼,因此集合元素必須是枚舉類型哄尔,否則會拋出異常。 EnumSet集合也是有序的柠并,其順序就是Enum類內(nèi)元素定義的順序岭接。 EnumSet存取的速度非常快臼予,批量操作的速度也很快鸣戴。EnumSet主要提供以下方法,allOf, complementOf, copyOf, noneOf, of, range等粘拾。注意到EnumSet并沒有提供任何構(gòu)造函數(shù)窄锅,要創(chuàng)建一個EnumSet集合對象,只需要調(diào)用allOf等方法缰雇,下面是一個EnumSet的例子入偷。

    import java.util.EnumSet;
    
    public class EnumSets {
        public static void main(String[] args) {
            EnumSet es1 = EnumSet.allOf(Season.class);
            System.out.println(es1);
            EnumSet es2 = EnumSet.noneOf(Season.class);
            es2.add(Season.WINTER);
            es2.add(Season.SUMER);
            System.out.println(es2);
            EnumSet es3 = EnumSet.of(Season.WINTER, Season.SUMER);
            System.out.println(es3);
            EnumSet es4 = EnumSet.range(Season.SUMER,Season.WINTER );
            System.out.println(es4);
            EnumSet es5 = EnumSet.complementOf(es4);
            System.out.println(es5);
        }
    }
    
    enum Season {
        SPRING, SUMER, FALL, WINTER
    }
    
    運行結(jié)果:
    [SPRING, SUMER, FALL, WINTER]
    [SUMER, WINTER]
    [SUMER, WINTER]
    [SUMER, FALL, WINTER]
    [SPRING]
    

幾種Set的比較

  • HashSet外部無序地遍歷成員,成員可為任意Object子類的對象械哟,但如果覆蓋了equals方法疏之,同時注意修改hashCode方法。HashSet是基于Hash算法實現(xiàn)的暇咆,其性能通常都優(yōu)于TreeSet锋爪。我們通常都應(yīng)該使用HashSet,在我們需要排序的功能時爸业,我們才使用TreeSet其骄。
  • TreeSet外部有序地遍歷成員;附加實現(xiàn)了SortedSet, 支持子集等要求順序的操作沃呢,成員要求實現(xiàn)Comparable接口年栓,或者使用Comparator構(gòu)造TreeSet。成員一般為同一類型薄霜。
  • LinkedHashSet外部按成員的插入順序遍歷成員某抓,成員與HashSet成員類似。
  • HashSet的元素存放順序和我們添加進(jìn)去時候的順序沒有任何關(guān)系惰瓜,而LinkedHashSet 則保持元素的添加順序否副。TreeSet則是對我們的Set中的元素進(jìn)行排序存放。
  • 一般來說崎坊,當(dāng)您要從集合中以有序的方式抽取元素時备禀,TreeSet實現(xiàn)就會有用處。為了能順利進(jìn)行,添加到 TreeSet 的元素必須是可排序的曲尸。 而您同樣需要對添加到TreeSet中的類對象實現(xiàn) Comparable 接口的支持赋续。一般說來,先把元素添加到 HashSet另患,再把集合轉(zhuǎn)換為 TreeSet 來進(jìn)行有序遍歷會更快纽乱。

各種Set集合性能分析

  • HashSet和TreeSet是Set集合中用得最多的I集合色冀。HashSet總是比TreeSet集合性能好涵亏,因為HashSet不需要額維護元素的順序。
  • LinkedHashSet需要用額外的鏈表維護元素的插入順序搪花,因此在插入時性能比HashSet低鹏倘,但在迭代訪問(遍歷)時性能更高薯嗤。因為插入的時候即要計算hashCode又要維護鏈表,而遍歷的時候只需要按鏈表來訪問元素纤泵。
  • EnumSet元素是所有Set元素中性能最好的骆姐,但是它只能保存Enum類型的元素

四. 實現(xiàn)Map接口的集合區(qū)別

它提供了一組鍵值的映射。其中存儲的每個對象都有一個相應(yīng)的關(guān)鍵字(key)捏题,關(guān)鍵字決定了對象在Map中的存儲位置诲锹。
關(guān)鍵字應(yīng)該是唯一的,每個key 只能映射一個value涉馅。實現(xiàn)類有HashMap归园、TreeMap、LinkedHashMap稚矿、Hashtable等

  • HashMap:鍵值對庸诱,key不能重復(fù),但是value可以重復(fù)晤揣;key的實現(xiàn)就是HashSet桥爽;value對應(yīng)著放;允許null的鍵或值昧识;
  • Hashtable:線程安全的钠四,不允許null的鍵或值;
  • Properties::key和value都是String類型跪楞,用來讀配置文件缀去;
  • TreeMap:對key排好序的Map; key 就是TreeSet, value對應(yīng)每個key; key要實現(xiàn)Comparable接口或TreeMap有自己的構(gòu)造器;
  • LinkedHashMap: 此實現(xiàn)與HashMap的不同之處在于甸祭,后者維護著一個運行于所有條目的雙重鏈接列表缕碎。存儲的數(shù)據(jù)是有序的。

HashMap:

  • Map 主要用于存儲鍵(key)值(value)對池户,根據(jù)鍵得到值咏雌,因此鍵不允許重復(fù),但允許值重復(fù)凡怎。
  • HashMap 是一個最常用的Map,它根據(jù)鍵的HashCode值存儲數(shù)據(jù),根據(jù)鍵可以直接獲取它的值,具有很快的訪問速度赊抖。
  • HashMap最多只允許一條記錄的鍵為Null;允許多條記錄的值為 Null;
  • HashMap不支持線程的同步统倒,即任一時刻可以有多個線程同時寫HashMap;可能會導(dǎo)致數(shù)據(jù)的不一致。如果需要同步氛雪,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力檐薯。

HashMap實現(xiàn)原理---散列

Hash哈希算法的意義在于提供了一種快速存取數(shù)據(jù)的方法,它用一種算法建立鍵值與真實值之間的對應(yīng)關(guān)系。散列表又稱為哈希表注暗。散列表算法的基本思想是:以結(jié)點的關(guān)鍵字為自變量,通過一定的函數(shù)關(guān)系(散列函數(shù))計算出對應(yīng)的函數(shù)值墓猎,以這個值作為該結(jié)點存儲在散列表中地址捆昏。
當(dāng)散列表中的元素存放太滿,就必須進(jìn)行再散列毙沾,將產(chǎn)生一個新的散列表骗卜,所有元素存放到新的散列表中,原先的散列表將被刪除左胞。在Java語言中寇仓,通過負(fù)載因子(load factor)來決定何時對散列表進(jìn)行再散列。例如:如果負(fù)載因子0.75烤宙,當(dāng)散列表中已經(jīng)有75%位置已經(jīng)放滿遍烦,那么將進(jìn)行再散列。
負(fù)載因子越高(越接近1.0)躺枕,內(nèi)存的使用效率越高服猪,元素的尋找時間越長。負(fù)載因子越低(越接近0.0)拐云,元素的尋找時間越短罢猪,內(nèi)存浪費越多。

何時需重寫equals叉瘩?

當(dāng)一個類有自己特有的“邏輯相等”概念(不同于對象身份的概念)膳帕;
Object類僅僅提供了一個對引用的比較,如果兩個引用不是同一個那就返回false薇缅,這是無法滿足大多數(shù)對象比較的需要的,所以要覆蓋;
使用==操作符檢查實參是否為指向?qū)ο蟮囊谩?br> 使用instanceof操作符檢查實參是否為正確的類型
把實參轉(zhuǎn)換到正確的類型漆羔;
對于該類中每一個“關(guān)鍵”域鸟顺,檢查實參中的域與當(dāng)前對象中對應(yīng)的域值是否匹配兆沙。對于既不是float也不是double類型的基本類型的域,可以使用==操作符進(jìn)行比較褥符;對于對象引用類型的域,可以遞歸地調(diào)用所引用的對象的equals方法粗截,對于float和double類型的域婿屹,先轉(zhuǎn)換成int或long類型的值,然后使用==操作符比較;
當(dāng)你編寫完成了equals方法之后助赞,應(yīng)該問自己三個問題:它是否是對稱的婉徘、傳遞的、一致的植阴? 如果答案是否定的蟹瘾,那么請找到 這些特性未能滿足的原因,再修改equals方法的代碼

equals()和hashCode()同時覆寫

尤其強調(diào)當(dāng)一個對象被當(dāng)作鍵值(或索引)來使用的時候要重寫這兩個方法掠手;
覆寫equals后憾朴,兩個不同實例可能在邏輯上相等,但是根據(jù)Object.hashCode方法卻產(chǎn)生不同的散列碼喷鸽,違反“相等的對象必須具有相等的散列碼”众雷。
導(dǎo)致,當(dāng)你用其中的一個作為鍵保存到hashMap做祝、hasoTable或hashSet中砾省,再以“相等的”找另一個作為鍵值去查找他們的時候,則根本找不到混槐。

不同類型的hashCode取值

  • 如果該域是布爾型的编兄,計算(f ?0:1)
  • 如果是char,short,byte或int声登,計算(int)f
  • 如果是long類型狠鸳,計算(int)(f^(f>>>32))
  • 如果是float類型揣苏,計算Float.floatToIntBits(f)
  • 如果是double類型,計算Dobule.doubleToLongBits(f)
  • 如果該域是一個對象引用碰煌,遞歸調(diào)用hashCode
  • 如果該域是一個數(shù)組舒岸,則把每個元素當(dāng)做單獨的域來處理,對每個重要的元素計算一個散列碼

Map集合比較:

  • HashMap的存入順序和輸出順序無關(guān)芦圾。
  • LinkedHashMap 則保留了鍵值對的存入順序蛾派。
  • TreeMap則是對Map中的元素進(jìn)行排序。
  • 因為HashMap和LinkedHashMap 存儲數(shù)據(jù)的速度比直接使用TreeMap 要快个少,存取效率要高洪乍。
  • 當(dāng)完成了所有的元素的存放后,我們再對整個的Map中的元素進(jìn)行排序夜焦。這樣可以提高整個程序的運行的效率壳澳,縮短執(zhí)行時間。

注意:TreeMap中是根據(jù)鍵(Key)進(jìn)行排序的茫经。而如果我們要使用TreeMap來進(jìn)行正常的排序的話巷波,Key 中存放的對象必須實現(xiàn)Comparable 接口。

Map常用方法:

  • Object put(Object key,Object value):用來存放一個鍵-值對Map中
  • Object remove(Object key):根據(jù)key(鍵)卸伞,移除鍵-值對抹镊,并將值返回
  • void putAll(Map mapping) :將另外一個Map中的元素存入當(dāng)前的Map中
  • void clear() :清空當(dāng)前Map中的元素
  • Object get(Object key) :根據(jù)key(鍵)取得對應(yīng)的值
  • boolean containsKey(Object key) :判斷Map中是否存在某鍵(key)
  • boolean containsValue(Object value):判斷Map中是否存在某值(value)
  • public Set keySet() :返回所有的鍵(key),并使用Set容器存放
  • public Collection values() :返回所有的值(Value)荤傲,并使用Collection存放
  • public Set entrySet() :返回一個實現(xiàn) Map.Entry 接口的元素 Set

遍歷Map的四種方法

import java.util.*;

public class Tests {
    
    public static void main(String[] args) {

        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");

        //第一種:普遍使用垮耳,keySet()返回所有的key值
        for (String key : map.keySet()) {
            System.out.println("key= "+ key + " and value= " + map.get(key));
        }
        //第二種 通過Map.entrySet使用iterator遍歷key和value
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }
        //第三種:推薦,尤其是容量大時 通過Map.entrySet遍歷key和value
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }
        //第四種 通過Map.values()遍歷所有的value遂黍,但不能遍歷key
        for (String v : map.values()) {
            System.out.println("value= " + v);
        }
    }

}

總結(jié)

<img src="https://tva1.sinaimg.cn/large/006tNbRwgy1g9pvbo86b6j313q0u018f.jpg" style="zoom:150%;" />

HashMap和Hashtable的區(qū)別:

HashMap和Hashtable都是java的集合類终佛,都可以用來存放java對象,這是他們的相同點
以下是他們的區(qū)別:

  1. 歷史原因:
    Hashtable是基于陳舊的Dictionary類的雾家,HashMap是java 1.2引進(jìn)的Map接口的一個現(xiàn)實铃彰。
  2. 同步性:
    hashtable是同步的,這個類中的一些方法保證了Hashtable中的對象是線程安全的芯咧,而HashMap則是異步的豌研,因此HashMap中的對象并不是線程安全的,因為同步的要求會影響執(zhí)行的效率唬党,所以如果你不需要線程安全的結(jié)合那么使用HashMap是一個很好的選擇鹃共,這樣可以避免由于同步帶來的不必要的性能開銷,從而提高效率驶拱,我們一般所編寫的程序都是異步的霜浴,但如果是服務(wù)器端的代碼除外。
  3. 值:
    HashMap可以讓你將空值作為一個表的條目的key或value
    Hashtable是不能放入空值(null)的

ArrayList和Vector的區(qū)別

ArrayList與Vector都是java的集合類蓝纲,都是用來存放java對象阴孟,這是他們的相同點晌纫,
區(qū)別:

  1. 同步性: Vector是同步的,這個類的一些方法保證了Vector中的對象的線程安全的永丝,而ArrayList則是異步的锹漱,因此ArrayList中的對象并不 是線程安全的,因為同步要求會影響執(zhí)行的效率慕嚷,所以你不需要線程安全的集合那么使用ArrayList是一個很好的選擇哥牍,這樣可以避免由于同步帶來的不必 要的性能開銷。
  2. 數(shù)據(jù)增長: 從內(nèi)部實現(xiàn)的機制來講喝检,ArrayList和Vector都是使用數(shù)組(Array)來控制集合中的對象嗅辣,當(dāng)你向兩種類型中增加元素的時候,如果元素的數(shù)目超過了內(nèi)部數(shù)組目前的長度他們都需要擴展內(nèi)部數(shù)組的長度挠说,Vector缺省情況下自動增長原來一倍的數(shù)組長度澡谭,ArrayList是原來的50%,所以最后你獲得的這個集合所占的空間總是比你實際需要的要大损俭,所以如果你要在集合中保存大量的數(shù)據(jù)蛙奖,那么使用Vector有一些優(yōu)勢,因為你可以通過設(shè)置集合的初始大小來避免不必要的資源開銷杆兵。

線程安全總結(jié):

  1. 如果要求線程安全雁仲,使用Vector,Hashtable
  2. 如果不要求線程安全拧咳,使用ArrayList伯顶,LinkedList囚灼,HashMap
  3. 如果要求鍵值對骆膝,則使用HashMap,Hashtable
  4. 如果數(shù)據(jù)量很大灶体,又要求線程安全考慮Vector

arraylist和linkedlist聯(lián)系與區(qū)別

  1. ArrayList是實現(xiàn)了基于動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu)阅签,LinkedList基于鏈表的數(shù)據(jù)結(jié)構(gòu)。
  2. 對于隨機訪問get和set蝎抽,ArrayList覺得優(yōu)于LinkedList政钟,因為LinkedList要移動指針。
  3. 對于新增和刪除操作add和remove樟结,LinedList比較占優(yōu)勢养交,因為ArrayList要移動數(shù)據(jù)。 這一點要看實際情況的瓢宦。若只對單條數(shù)據(jù)插入或刪除碎连,ArrayList的速度反而優(yōu)于LinkedList。但若是批量隨機的插入刪除數(shù)據(jù)驮履,LinkedList的速度大大優(yōu)于ArrayList. 因為ArrayList每插入一條數(shù)據(jù)鱼辙,要移動插入點及之后的所有數(shù)據(jù)廉嚼。

HashMap與TreeMap聯(lián)系與區(qū)別

  1. HashMap通過hashcode對其內(nèi)容進(jìn)行快速查找,而TreeMap中所有的元素都保持著某種固定的順序倒戏,如果你需要得到一個有序的結(jié)果你就應(yīng)該使用TreeMap(HashMap中元素的排列順序是不固定的)怠噪。
  2. 在Map 中插入、刪除和定位元素杜跷,HashMap是最好的選擇傍念。但如果您要按自然順序或自定義順序遍歷鍵,那么TreeMap會更好葱椭。使用HashMap要求添加的鍵類明確定義了hashCode()和 equals()的實現(xiàn)捂寿。
  3. 兩個map中的元素一樣,但順序不一樣孵运,導(dǎo)致hashCode()不一樣秦陋。
    同樣做測試:
    在HashMap中,同樣的值的map,順序不同治笨,equals時驳概,false;
    而在treeMap中,同樣的值的map,順序不同旷赖,equals時顺又,true,說明等孵,treeMap在equals()時是整理了順序了的稚照。

參考資料:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市俯萌,隨后出現(xiàn)的幾起案子果录,更是在濱河造成了極大的恐慌,老刑警劉巖咐熙,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弱恒,死亡現(xiàn)場離奇詭異,居然都是意外死亡棋恼,警方通過查閱死者的電腦和手機返弹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爪飘,“玉大人义起,你說我怎么就攤上這事∈ζ椋” “怎么了默终?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我穷蛹,道長土陪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任肴熏,我火速辦了婚禮鬼雀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蛙吏。我一直安慰自己源哩,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布鸦做。 她就那樣靜靜地躺著励烦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泼诱。 梳的紋絲不亂的頭發(fā)上坛掠,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音治筒,去河邊找鬼屉栓。 笑死,一個胖子當(dāng)著我的面吹牛耸袜,可吹牛的內(nèi)容都是我干的友多。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼堤框,長吁一口氣:“原來是場噩夢啊……” “哼域滥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蜈抓,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤启绰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后资昧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酬土,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡荆忍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年格带,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刹枉。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡叽唱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出微宝,到底是詐尸還是另有隱情棺亭,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布蟋软,位于F島的核電站镶摘,受9級特大地震影響嗽桩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凄敢,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一碌冶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涝缝,春花似錦扑庞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至滩援,卻和暖如春栅隐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背玩徊。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工约啊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人佣赖。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓恰矩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親憎蛤。 傳聞我的和親對象是個殘疾皇子外傅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354

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

  • 3.3 集合 一方面, 面向?qū)ο笳Z言對事物的體現(xiàn)都是以對象的形式俩檬,為了方便對多個對象的操作萎胰,就要對對象進(jìn)行存儲。另...
    閆子揚閱讀 728評論 0 1
  • Java集合類可用于存儲數(shù)量不等的對象,并可以實現(xiàn)常用的數(shù)據(jù)結(jié)構(gòu)如棧,隊列等,Java集合還可以用于保存具有映射關(guān)...
    小徐andorid閱讀 1,941評論 0 13
  • 上一篇文章介紹了Set集合的通用知識棚辽。Set集合中包含了三個比較重要的實現(xiàn)類:HashSet技竟、TreeSet和En...
    Ruheng閱讀 15,643評論 3 57
  • 四、集合框架 1:String類:字符串(重點) (1)多個字符組成的一個序列屈藐,叫字符串榔组。生活中很多數(shù)據(jù)的描述都采...
    佘大將軍閱讀 752評論 0 2
  • 前言 關(guān)于Java集合框架(Java Collections Framework, JCF)的資料較于C++的ST...
    L2先森閱讀 310評論 0 0