關於JAVA集合的那點事

(一) Vector   ArrayList   LinkedListjava

Vestor,ArrayList,LinkedList這三個類都實現了java.util.List接口;數組

Vector和ArrayList使用Objec的數組形式來存儲,可直接按序號索引元素,故搜索速度較快,但在數組中間插入新元素時要設計數組元素的內存移動,致使速度較慢;安全

LinkedList則是採用了雙向鏈表的存儲方式,插入新元素時只要指定該元素的先後項便可,速度較快,可是搜索時要向前或向後遍歷,速度較慢函數

Vector是線程安全的,他的不少方法都實現了線程同步(synchronized),可是性能上來說,Vector會低於ArrayList性能

 

(二)HashSetthis

HashSet 是 Set 接口的經常使用實現類,構造函數以下:spa

 public HashSet() {
       map = new HashMap<E,Object>();
 }線程

從構造函數咱們能夠看出,實際上HashSet是經過HashMap來實現的,所以HashSet也是採用Hash存儲機制進行數據的存儲。設計

咱們都知道HashSet不容許插入重複的值或對象,可是對於兩個內容相同的對象呢?code

運行下面的代碼我便能知曉一二:

public class Test {

 //定義一個內部類

 private static class Demo{

  private int value;

  public Demo(int value){

   this.value=value;

  }  

  //重寫toString方法,便於打印輸出

  public String toString(){

   return ("value="+value);

  }

  //重寫equals方法

   public boolean equals(Object o) {

              Demo demo = (Demo) o;

              return (demo.value == value) ? true : false;

      }

   //重寫hashCode方法

   public int hashCode(){

    return value;

   }

 }

 

 public static void main(String[] args) {

  HashSet<Demo> set=new HashSet<Demo>();

  set.add(new Demo(1));

  System.out.println(set);

  set.add(new Demo(1));

  System.out.println(set);

 } 

}

運行結果爲:

[value=1]
[value=1]

可是當咱們把Demo的equals方法或hashCode方法註釋掉一個或者所有註釋掉時咱們會發現運行結果變爲:

[value=1]
[value=1, value=1]

若是不重寫對象的hashCode方法和equals方法的狀況下,HashSet依舊會將內容相同的兩個對象一塊兒存入

那麼爲何會如此呢?

因爲HashSet是依靠HashMap實現的,因此要解答這個問題咱們必須搞清楚HashMap內部的存儲機制

經過另外一篇文章——深刻講解HashMap,咱們能夠發現,鍵值對在存入HashMap中定義的entry數組時,會先根據鍵(key)對象的hashCode計算出要存儲在數組的那個下標值下,而後在遍歷該下標值的entry鏈,若是找到了key與要插入的key相同的entry對象則替換該對象的值,不然,則將此新鍵值對插入到該entry鏈的頭部;

而對於HashSet<E>事實上就至關於HashMap<E,Object>

若是不重寫hashCode方法,那麼咱們知道Object默認的hashCode返回的是一個與內存地址相關的數值,兩個不一樣對象的hashCode返回值通常是不相同的(固然也有很小的概率相同),哪怕是這兩個對象的內容徹底一致,因此在插入entry數組的時候,這兩個對象存儲的下標值也有很大的機率不在同一個下標值內,天然就兩個對象都會被存儲在entry數組中;

那麼即便咱們重寫了hashCode也仍是不夠,由於在entry鏈中,會根據對象的equals方法判斷新添加進來的key時候已經存在,咱們知道Object默認的equals方法返回的是該對象的內存地址,天然的,兩個不一樣對象用equals進行比較時返回的就是false,也就是此時這兩個對象都會被存儲再同一個entry鏈中。

明白了上面的機制後,咱們能夠看出,若是咱們要實如今HashSet(或者是HashMap的key)中不會被插入內容相同的對象時,咱們就必須重寫要插入的對象的hashCode和equals方法,並且這兩個方法要根據該對象的內容來重寫,確保兩個內容相同的對象的hashCode返回值相同,並使用equals比較時返回true

 

(三)TreeSet  TreeMap  HashSet  HashMap

TreeMap它內部是一個樹形結構存儲結構,使用二叉樹對數據進行存儲;

TreeMap是有序的,天然要求元素是能夠比較大小的,若是構造函數指定Comparator的話,就使用這個Comparator比較大小

若是沒有指定Comparator的話,就使用天然排序(元素要實現Comparable接口).

二叉樹,通常查找時間複雜度爲 o(lg(n)),這個效率固然沒有HashMap的效率高因此除法是有排序的需求,否則通常都使用HashMap

TreeSet與TreeMap的關係就和HashSet與HashMap的關係同樣

因爲TreeSet  TreeMap  用的比較少,因此也未做深刻的瞭解,具體之後有須要再進一步分析

 

(四)HashMap  Hashtalbe

Hashtable是繼承與Dictionary類,HashMap是Map接口的一個實現(事實上,Hashtable也實現了Map接口);

Hashtable是線程安全,HashMap是Hashtable的輕量級實現(非線程安全實現),因此HashMap的效率會高於Hashtable

HashMap容許將null做爲一個entry的key或者value,而Hashtable不容許。

 

(五)關於集合的遍歷 

package com;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

public class 容器類的遍歷 {
 
 //有序的集合:數組和arraylist的遍歷,可經過下標值實現
  public void bianliArray(String[] a){
   for(int i=0;i<a.length;i++){
    System.out.println(i);
   }
  }
  
  public void bianliArraylist(ArrayList<String> a){
   for(int i=0;i<a.size();i++){
    System.out.println(a.get(i));
   }
  }
  
  //Collection接口定義了一個iterator()方法,該方法返回一個實現了Iterator接口對象(迭代器),而後再使用迭代器進行迭代遍歷  
  public void bianliCollection(Collection<String> c){
   Iterator<String> iterator=c.iterator();
   while(iterator.hasNext()){
    System.out.println(iterator.next());
    /*
     * 補充: 值得注意的是,當要在迭代過程當中刪除某個元素,則必需要用該迭代器的remove方法
     * 而不能使用collection自生的remove方法,這是由於集合進行進入迭代器時會被迭代器鎖定,
     * 迭代過程只有迭代器能對此集合進行操做
     */
   }  
  }
  
     //java5以後支持了另外一種 遍歷方式:foreach方式,這種方式的編寫格式簡單整潔,可是隻能用於只讀的狀況,
  //由於該方法不能對遍歷的元素進行修改和刪除操做,分析下面的方法的結果能夠發現集合中的字符串沒被修改過
  public void bianliForeach(Collection<String> c){
   for(String s: c){
    System.out.println(s);
    s="被修改過了";
   }
   for(String s: c){
    System.out.println(s);
   }
  }
  //可是分析下面這個程序能夠發現集合中的u對象倒是被修改了,從中能夠看出什麼麼?。。。。
  public void bianliForeach1(Collection<User> c){
   for(User u: c){
    System.out.println(u.getName());
    u.setName("被修改過了");
   }
   for(User u: c){
    System.out.println(u.getName());
   }
  }
  
  /*
   * 對於map的遍歷,因爲Map接口沒有定義和collection同樣的iterator方法
   * 不過在Map中定義裏entrySet和ketSet方法
   *   entrySet()返回此映射中包含的映射關係的 Set視圖,
   *   keySet() 返回此映射中包含的鍵的 Set 視圖。
   *   而後在經過set視圖的iterator方法
  */
  
  //用entrySet實現Map的遍歷,效率較高
  public  void entrySetOfMap(Map<String, String>  m){
   Iterator iterator=m.entrySet().iterator();
   while(iterator.hasNext()){
    Map.Entry<String, String> entry=(Map.Entry<String, String>)iterator.next();//Map.Entry鍵值對映射項
    System.out.println("key="+entry.getKey());
    System.out.println("value="+entry.getValue());
   }
  }
  
  //用keySet實現Map的遍歷,遍歷出key以後還要去map查找對應的value,效率較低
  public void keySetOfMap(Map<String, String> m){
   Iterator iterator=m.keySet().iterator();
   while(iterator.hasNext()){
    Object key=iterator.next();
    String value=(String)m.get(key);
    System.out.println("key="+key);
    System.out.println("value="+value);
   }
  }
 
}
相關文章
相關標籤/搜索