java中集合中的數據結構

一、Java中集合

  Java中集合類是Java編程中使用最頻繁、最方便的類。集合類作爲容器類可以存儲任何類型的數據,當然也可以結合泛型存儲指定的類型(不過泛型僅僅在編譯期有效,運行時是會被擦除的)。集合類中存儲的僅僅是對象的引用,並不存儲對象本身。集合類的容量可以在運行期間進行動態擴展,並且還提供很多很方便的方法,如求集合的並集、交集等。

二、集合類結構

  Java中的集合包含多種數據結構,如鏈表、隊列、哈希表等。從類的繼承結構來說,可以分爲兩大類,一類是繼承自Collection接口,這類集合包含List、Set和Queue等集合類。另一類是繼承自Map接口,這主要包含了哈希表相關的集合類。下面我們看一下這兩大類的繼承結構圖:

1、List、Set和Queue

 圖中的綠色的虛線代表實現,綠色實線代表接口之間的繼承,藍色實線代表類之間的繼承。

   (1)List:我們用的比較多List包括ArrayList和LinkedList,這兩者的區別也很明顯,從其名稱上就可以看出。ArrayList的底層的通過數組實現,所以其隨機訪問的速度比較快,但是對於需要頻繁的增刪的情況,效率就比較低了。而對於LinkedList,底層通過鏈表來實現,所以增刪操作比較容易完成,但是對於隨機訪問的效率比較低。

  (2)Queue:一般可以直接使用LinkedList完成,從上述類圖也可以看出,LinkedList繼承自Deque,所以LinkedList具有雙端隊列的功能。PriorityQueue的特點是爲每個元素提供一個優先級,優先級高的元素會優先出隊列。

  (3)Set:Set與List的主要區別是Set是不允許元素重複的,而List則可以允許元素重複的。判斷元素的重複需要根據對象的hash方法和equals方法來決定。這也是我們通常要爲集合中的元素類重寫hashCode方法和equals方法的原因。我們還是通過一個例子來看一下Set和List的區別,以及hashcode方法和equals方法的作用:

public class SetTest {

    public static void main(String[] args) {
        Person p1 = new Person("lxp",10);
        Person p2 = new Person("lxp",10);
        Person p3 = new Person("lxp",20);

        ArrayList<Person> list = new ArrayList<Person>();
        list.add(p1);
        System.out.println("---------");
        list.add(p2);
        System.out.println("---------");
        list.add(p3);
        System.out.println("List size=" + list.size());

        System.out.println("----分割線-----");

        Set<Person> set = new HashSet<Person>();
        set.add(p1);
        System.out.println("---------");
        set.add(p2);
        System.out.println("---------");
        set.add(p3);
        System.out.println("Set size="+set.size());
    }


    static class Person{
        private String name;
        private int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public boolean equals(Object o) {
            System.out.println("Call equals();name="+name);
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Person person = (Person) o;

            return name.equals(person.name);

        }

        @Override
        public int hashCode() {
            System.out.println("Call hashCode(),age="+age);
            return age;
        }
    }
}
/**
*上述代碼的執行結果如下:

---------
---------
List size=3
----分割線-----
Call hashCode(),age=10
---------
Call hashCode(),age=10
Call equals();name=lxp
---------
Call hashCode(),age=20
Set size=2
*/

  從結果看出,元素加入List的時候,不執行額外的操作,並且可以重複。而加入Set之前需要先執行hashCode方法,如果返回的值在集合中已存在,則要繼續執行equals方法,如果equals方法返回的結果也爲真,則證明該元素已經存在,會將新的元素覆蓋老的元素,如果返回hashCode值不同,則直接加入集合。這裏記住一點,對於集合中元素,hashCode值不同的元素一定不相等,但是不相等的元素,hashCode值可能相同。

  HashSet和LinkedHashSet的區別在於後者可以保證元素插入集合的元素順序與輸出順序保持一致。而TresSet的區別在於其排序是按照Comparator來進行排序的,默認情況下按照字符的自然順序進行升序排列。

  Iterable:從這個圖裏面可以看到Collection類繼承自Iterable,該接口的作用是提供元素遍歷的功能,也就是說所有的集合類(除Map相關的類)都提供元素遍歷的功能。Iterable裏面包含了Iterator的迭代器,其源碼如下,大家如果熟悉迭代器模式的話,應該很容易理解。

2、Map:

      Map類型的集合最大的優點在於其查找效率比較高,理想情況下可以實現O(1)的時間複雜度。Map中最常用的是HashMap,LinkedHashMap與HashMap的區別在於前者能夠保證插入集合的元素順序與輸出順序一致。這兩者與TreeMap的區別在於TreeMap是根據鍵值進行排序的,當然其底層的實現也有本質的區別,如HashMap底層是一個哈希表,而TreeMap的底層數據結構是一棵樹。

  

  Map主要用於存儲健值對,根據鍵得到值,因此不允許鍵重複(重複了覆蓋了),但允許值重複。

 

HashMap

  HashMap 是一個最常用的Map,它根據鍵的HashCode 值存儲數據,根據鍵可以直接獲取它的值,具有很快的訪問速度。遍歷時,取得數據的順序是完全隨機的。
  HashMap最多隻允許一條記錄的鍵爲Null;允許多條記錄的值爲 Null。
  HashMap不支持線程的同步(即任一時刻可以有多個線程同時寫HashMap),可能會導致數據的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。
  Hashtable與 HashMap類似,它繼承自Dictionary類。不同的是:它不允許記錄的鍵或者值爲空;它支持線程的同步(即任一時刻只有一個線程能寫Hashtable),因此也導致了 Hashtable在寫入時會比較慢。

 

LinkedHashMap

  LinkedHashMap保存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先得到的記錄肯定是先插入的。也可以在構造時帶參數,按照應用次數排序。
  在遍歷的時候會比HashMap慢,不過有種情況例外:當HashMap容量很大,實際數據較少時,遍歷起來可能會比LinkedHashMap慢。因爲LinkedHashMap的遍歷速度只和實際數據有關,和容量無關,而HashMap的遍歷速度和他的容量有關。

 

TreeMap

  TreeMap實現SortMap接口,能夠把它保存的記錄根據鍵排序。
  默認是按鍵值的升序排序,也可以指定排序的比較器,當用Iterator 遍歷TreeMap時,得到的記錄是排過序的。

 

三種類型分別在什麼時候使用

  1、一般情況下,我們用的最多的是HashMap。HashMap裏面存入的鍵值對在取出的時候是隨機的,它根據鍵的HashCode值存儲數據,根據鍵可以直接獲取它的值,具有很快的訪問速度。在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。
  2、TreeMap取出來的是排序後的鍵值對。但如果您要按自然順序或自定義順序遍歷鍵,那麼TreeMap會更好。
  3、LinkedHashMap 是HashMap的一個子類,如果需要輸出的順序和輸入的相同,那麼用LinkedHashMap可以實現,它還可以按讀取順序來排列,像連接池中可以應用。
 

其他

1. HashSet是通過HashMap實現的,TreeSet是通過TreeMap實現的,只不過Set用的只是Map的key
2. Map的key和Set都有一個共同的特性就是集合的唯一性.TreeMap更是多了一個排序的功能.
3. hashCode和equal()是HashMap用的, 因爲無需排序所以只需要關注定位和唯一性即可.
  a. hashCode是用來計算hash值的,hash值是用來確定hash表索引的.
  b. hash表中的一個索引處存放的是一張鏈表, 所以還要通過equal方法循環比較鏈上的每一個對象纔可以真正定位到鍵值對應的Entry.
  c. put時,如果hash表中沒定位到,就在鏈表前加一個Entry,如果定位到了,則更換Entry中的value,並返回舊value
4. 由於TreeMap需要排序,所以需要一個Comparator爲鍵值進行大小比較.當然也是用Comparator定位的.
  a. Comparator可以在創建TreeMap時指定
  b. 如果創建時沒有確定,那麼就會使用key.compareTo()方法,這就要求key必須實現Comparable接口.
  c. TreeMap是使用Tree數據結構實現的,所以使用compare接口就可以完成定位了.
 
 
注意:

  1、Collection沒有get()方法來取得某個元素。只能通過iterator()遍歷元素。
  2、Set和Collection擁有一模一樣的接口。
  3、List,可以通過get()方法來一次取出一個元素。使用數字來選擇一堆對象中的一個,get(0)...。(add/get)
  4、一般使用ArrayList。用LinkedList構造堆棧stack、隊列queue。
  5、Map用 put(k,v) / get(k),還可以使用containsKey()/containsValue()來檢查其中是否含有某個key/value。 
        HashMap會利用對象的hashCode來快速找到key。哈希碼就是將對象的信息經過一些轉變形成一個獨一無二的int值,這個值存儲在一個array中。我們都知道所有存儲結構中,array查找速度是最快的。所以,可以加速查找。 發生碰撞時,讓array指向多個values。即,數組每個位置上又生成一個槤表。
  6、Map中元素,可以將key序列、value序列單獨抽取出來。
    使用keySet()抽取key序列,將map中的所有keys生成一個Set。
    使用values()抽取value序列,將map中的所有values生成一個Collection。
    爲什麼一個生成Set,一個生成Collection?那是因爲,key總是獨一無二的,value允許重複。

 

轉自:http://blog.csdn.net/fg2006/article/details/6411200

轉自:https://www.cnblogs.com/paddix/p/5539326.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章