java的集合類【Map(映射)、List(列表)與Set(集)比較】


 https://baike.baidu.com/item/java%E9%9B%86%E5%90%88%E7%B1%BB/4758922?fr=aladdin

https://www.cnblogs.com/zhuoqingsen/p/8573643.html

集合類存放於java.util包中。

集合類存放的都是對象的引用,而非對象本身,出於表達上的便利,我們稱集合中的對象就是指集合中對象的引用(reference)。
集合類型主要有3種: set(集)、list(列表)和map(映射)
集合接口分為:Collection和Map,list、set實現了Collection接口

我們為什么要設定不同的集合類型,是為了放置不同的數據,而且不同類型用在不同的場合。這三個類放在何處呢,它們放在java.util包中,Set、List和Map都是接口,它們有各自的實現類。Set的主要實現類:HashSet和TreeSet,List的主要實現類是ArrayList,LinkedList而Map主要實現類是HashMap和TreeMap。

 

總的說來, Java API中所用的集合類,都是實現了 Collection接口,他的一個類繼承結構如下:
Collection<--List<-- Vector<--Stack
Collection<--List<-- ArrayList
Collection<--List<--LinkedList
Collection<--Set<-- HashSet
Collection<--Set<--HashSet<--LinkedHashSet
Collection<--Set<--SortedSet<--TreeSet

List 接口及其實現類

  • 有序集合,集合中每個元素都有其對應的順序索引,類似數組,索引也是從 0 開始,可以根據元素的索引,來訪問元素。

  • List 集合允許添加相同的元素,因為它是通過下標來取值的,不會因為元素相同而產生沖突。

 

  如何應用接口List的一個重要實現類 ArrayList呢?

  1. ArrayList集合類的定義與添加集合元素(不同類型的元素)

  

  2.訪問集合元素通過get(集合下標) 獲取的是抽象Object對象類型,這里需要強轉弱,自動拆箱

  •   String s1=(String)ls.get(0);
  •   int s2=(Integer)ls.get(1);
  •   double s1=(Double)ls.get(2);

 

  3.刪除元素

  ls.remove(1);//刪除下標為1的對象

 

  4.遍歷集合元素

  數組遍歷是通過游標遍歷,List集合類遍歷集合元素類似於數組遍歷,也可以通過索引遍歷,另外List集合類還可以使用Iterator接口遍歷集合元素(實際上,所有Collection集合類都可以使用Iterator接口遍歷集合元素,因為實現類都實現Iterable<E>接口

  

 

  • 使用Iterator接口遍歷集合元素

  

  5.對元素集合進行排序

  一般有兩種方式

  第一種是

  封裝類實現了Comparator接口,封裝類就是平常的VO/PO類,例如User

  

復制代碼
public class User implements Comparable<User>  
{  
    private String name;  
    private Integer age;  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public Integer getAger() {  
        return age;  
    }  
    public void setAge(Integer age) {  
        this.age= age;  
    }  
    public int compareTo(User arg0) {  
        return this.getAge().compareTo(arg0.getAge());  
    }  
}  

//測試客戶端代碼
public static void main(String[] args) {  
        User user1 = new User();  
        user1.setName("zhangsan");  
        user1.setAge(10);  
        User user2 = new User();  
        user2.setName("lisi");  
        user2.setAge(6);  
        List<User> list = new ArrayList<User>();  
        list.add(user1);  
        list.add(user2);  
        Collections.sort(list);  //排序
        for(User u : list){  
            System.out.println(u.getName());  //輸出
     } 

     }
  lisi
 zhangshan
復制代碼

 

  第二種是封裝類沒有實現Comparable接口,此時需要在調用sort的另一個有參方法,依舊是這個封裝類,但是沒有實現Comparable,我這里就不寫封裝類,同上

  

復制代碼
        User user1 = new User();  
        user1.setName("zhangsan");  
        user1.setAge(10);  
        User user2 = new User();  
        user2.setName("lisi");  
        user2.setAge(6);  
        List<User> list = new ArrayList<User>();  
        list.add(user1);  
        list.add(user2);          //初始化list
        Collections.sort(list,new new Comparator<User >(){
                      
            //覆蓋排序方法
          public int compare(User u1, User u2) {
                //按照User的年齡進行升序排列
                if(u1.getAge() > u2.getAge()){
                    return 1;
                }
                if(u1.getAge() == u2.getAge()){
                    return 0;
                }
                return -1;
            } 
         });  //排序    
  
復制代碼

  如何應用Vector?

  Vector與ArrayList在用法上幾乎完全相同。但是它是線程安全的,也就是加了同步鎖,相比之下性能會顯得慢一些

  那么在多線程下的ArrayList怎么樣?

  兩個線程同時訪問,對容器的同一位置進行了同一時刻的賦值,則會達不到希望容器產生的效果,比如

  •   循環10000次在多線程可能會小於100000次
  •   在ArrayList擴容的時候會造成數組越界,產生非常詭異的異常

 

  Set接口及其實現類

  Set集合類特點是什么?

  • 無序集合

  • 不允許包含重復元素,根據equals方法判斷兩個對象相同

  如何應用Set接口的一個重要實現類HashSet呢

  1.HashSet集合類的定義與添加集合元素

 

  • HashSet集合添加一個元素時,會先得到該對象的hashCode值,然后通過equals方法判斷集合內是否已存在相同的對象,如果已存在相同的對象,即hashcode指向的內存地址相同,則無法添加該對象。String字符串重寫了equals方法,雖然str1(小明)與str2(小張)是兩個不同的對象,但str1.equals(str2);返回是false,因此添加str2失敗。
  • HashSet是先比較元素的HashCode的值,也就是哈希值,如果哈希值相同的情況下,會調用equals方法比較兩個元素的值是否相同。
  • Set的另外一個實現類TreeSet則是通過調用CompareTo的方法,如果返回值為0,證明兩個元素相同,因此TreeSet其實是一個有序的集合。

  2.遍歷集合元素

  •  HashSet集合只能通過Iterator接口或者增強for循環遍歷集合元素,且因為HashSet不能保證元素的排列順序,因而遍歷讀取集合元素的順序是有可能發生變化的

 

//增強for的方式
for(Person p:set){ System.out.println(p.name+p.age); }

 

  3.刪除元素

  • set.remove(p1);

 

  Map接口及其實現類

     Map集合的特點是什么?

  •  雙列集合,保存兩組值,一組是鍵key,另一組是值value,key和value之間存在單向一對一關系,通過指定的key,總能找到唯一的、確定的value

     

  • Map集合類的key不允許重復,根據equals方法判斷兩個key相同

  • Map集合類的key和value可以是任何類型的數據(鍵和值都可以為null)

  如何應用HashMap?

  • HashMap集合類添加鍵值對時,先根據equals方法判斷要添加鍵值對的key是否存在,如果存在,value會發生覆蓋,如果不存在,添加新的鍵值對。

 

  遍歷集合元素

  1.keySet()方法獲取元素

  原理:

  • 將Map集合中的所有鍵(Key)存入到Set集合中,因為Set集合具備迭代器,所以可以用迭代方式取出所有的鍵,再根據get方法獲取每一個鍵對應的值。簡單說就是:Map集合---->Set集合 ---->迭代器取出

 

 

   2.entrySet()方法獲取元素:

  原理:

  • 將Map集合中的映射關系(Key-Value)存入到了Set集合中,而這個映射關系的數據類型是Map.Entry,在通過迭代器將映射關系存入到Map.Entry集合中,並通過其中的getKey()和getValue()放取出鍵值。

 

  其余還有其他遍歷方式,比如說增強for,但是前提都是要獲得Set存儲Key的集合,即調用map.KeySet(),然后轉化為String,在通過getValue("Key")的方式獲取值,在實際開發當中比如獲取數據庫查詢回來的ResultSet 都是rs,getString("字段名)

  • 遍歷得到結果的順序並不是添加順序,實際上,Map里的key和Set集合的存儲形式(無序)類似,與HashSet集合不能保證元素的順序一樣,HashMap也不能保證其中key-value鍵值對的順序。

 

  刪除集合元素

  map.remove(key);

 

  集合類之間的幾種比較(小結)

  1.Vector、ArrayList、LinkedList的異同?分別適用於何種需求?

ArrayList和Vector都是采用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增加和插入元素,都允許通過順序索引查找元素,但是插入數據、刪除數據要設計到數組元素移動等內存操作,所以查找數據快、增刪數據慢。ArrayList和Vector在用法上幾乎相同,但有一點明顯區別:ArrayList是線程不安全的,多線程情況下,需要寫代碼保證集合的同步性;Vector是線程安全的,它使用了synchronized方法保證了多線程情況下集合的同步性;也正因為Vector的線程安全,它在性能上比ArrayList差。

LinkedList,首先它是一個List集合,可以根據索引來隨機訪問集合中的元素,這個與ArrayList的用法相同。除此之外,LinkedList采用雙向鏈表實現存儲,查找數據時需要進行向前或向后遍歷,直到找到索引數據;但是插入數據時只需要記錄本項的前后項即可,所以查找數據慢、增刪數據快。

Arraylist 底層使用的是Object數組;LinkedList 底層使用的是雙向鏈表數據結構(注意雙向鏈表和雙向循環鏈表的區別)

ArrayList的空 間浪費主要體現在在list列表的結尾會預留一定的容量空間,而LinkedList的空間花費則體現在它的每一個元素都需要消耗比ArrayList更多的空間(因為要存放直接后繼和直接前驅以及數據)

 

適用需求建議:

  • 需要經常遍歷集合元素,建議使用ArrayList、Vector
  • 需要經常執行插入、刪除操作來改變集合的大小,建議使用LinkedList
  • 多線程的情況下,可以考慮Vector。但也可以考慮ArrayList,另外寫代碼保證集合的同步性
  • 關於CopyOnWriteArrayList也是一個線程安全的容器,原理是

  CopyOnWriteArrayList底層就是數組,加鎖就交由ReentrantLock來完成。

  在添加的時候就上鎖,並復制一個新數組,增加操作在新數組上完成,將array指向到新數組中,最后解鎖。

  在修改時,復制出一個新數組,修改的操作在新數組中完成,最后將新數組交由array變量指向。

  寫加鎖,讀不加鎖

 

  2.HashSet與TreeSet的異同?分別適用於何種需求?

HashSet與TreeSet相同點:都是Set接口的實現類,集合元素都不允許重復;

HashSet與TreeSet不同點:

HashSet中的數據是無序的,可以放入null,且只能放入一個null,通過計算元素的hashCode值快速操作,所以HashSet的效率很高。

TreeSet中的數據是自動排序的,這個有序不同於List接口那樣按照插入的先后順序而且有對應的索引,這個順序是按照元素對象自己定義的排序規則而定的。默認情況下,TreeSet會調用集合元素的compareTo(Object obj)方法來比較元素之間大小關系,然后將集合元素按升序排列,基於TreeMap。因此,TreeSet中的每個對象的類都必須實現Comparable接口定義排序規則才可以正常使用,如String等這些類本身已經實現Comparable接口並有默認順序,而null沒有實現Comparable接口,所以TreeSet不允許放入null值。

 

  3.HashMap、TreeMap、HashTable的異同?分別適用於何種需求?

HashMap和HashTable的關系類似於ArrayList和Vector的關系,HashTable與HashMap的用法基本相同,兩者的區別有:

HashMap允許有空鍵(null)與空值(null),非線程安全,效率較高;

Hashtable不允許有空鍵(null)與空值(null),線程安全,效率較低;

TreeMap與HashMap的關系也類似與TreeSet和HashSet的關系,TreeMap實現SortMap接口,能夠把它保存的鍵值對根據key排序,基於紅黑樹,從而保證TreeMap中所有鍵值對處於有序狀態。TreeMap要求所有的key必須實現Comparable接口,默認遵循key的默認排序規則,也可以指定排序的比較器。當用Iterator 遍歷TreeMap時,得到的記錄是排過序的(類似TreeSet),不允許有空鍵(null)。

 

  by the way

  fail-fast 機制是java集合(Collection)中的一種錯誤機制。當多個線程對同一個集合的內容進行操作時,就可能會產生fail-fast事件。拋java.util.ConcurrentModificationException


免責聲明!

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



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