Java 複習筆記7 - 集合框架

集合從概念上來看就是一堆元素放在一塊兒,咱們其實可使用數組來處理這樣的狀況(操做一堆數據),可是數組存在各類各樣的限制,使用起來並不方便,所以才推出更強大的集合處理方式,即集合框架html

數組的問題:java

image-20191126160655434

容器最基本的功能就是存儲數據,取出數據,可是由於實際需求不一樣,好比是否有序,是否可重複,不一樣的數據結構,不一樣的存取效率等等緣由,系統提供了一衆不一樣特性的容器類,共同構成了集合框架算法

框架體系結構:api

image-20191125161828534

其中Collection接口支持(單個形式)數據的存儲,而Map接口支持對鍵值對形式的數據的存儲數組

List接口

特色:

  • 有序存儲
  • 可重複存儲
  • 可存儲null
  • 提供按照索引的操做方法
  • 動態增加(chang)

主要實現類:

  • ArrayList
    • 對於列表尾部的插入刪除效率更高
    • 隨機讀取快,查找快,隨機插入刪除慢
    • 底層使用數組實現(連續的內存)

      ps: 因爲其內部使用數組,而數組容量是固定的,這就致使了,添加刪除時都會引起數組的重建,以及數據的拷貝,數據結構

      舉個例子:火焰山終於下雨了,剛開始覺得只下一點點,因此找了一個杯子裝,後來發現裝不下了,要換一個盆(建立更大的數組),那麼須要先把杯子裏的水倒進盆裏(數據拷貝)併發

      適用於:讀取,查詢操做更頻繁的場景框架

  • LinkedList
    • 內部使用雙鏈表結構(非連續的內存)
    • 隨機添加刪除和順序添加刪除都很快
    • 查找慢,隨機讀取慢

      ps:因爲使用的鏈表結構,因此添加刪除時僅須要在任意內存保存數據而後將指針添加到上一個數據的結尾便可,因此這添加刪除數據時比使用數組的ArrayList快不少 ,可是當須要查找數據時,就須要頻繁的移動指針(地址不是連續的),形成了查詢速度慢,ide

      舉個例子: 你是要找王老闆作生意,可是你本身不認識他,因而你就找了劉老闆,劉老闆也不認識他,因而劉老闆找了李老闆,還好最後李老闆認識王老闆,找到了你要找的人,期間不能跨越中間的必須一個一個找下去學習

      適用於:添加,刪除操做更頻繁的建立

Set接口

主要實現類:

  • HashSet
    • 不保證迭代順序
    • 不可存儲重複元素
    • 能夠存儲null元素
    • 內部使用HashMap(具體參考下面HashMap內容),將全部鍵值對的值設置爲同一個Object對象

能夠說HashSet就是對HashMap進行了簡單的包裝:

​ 真正存儲數據的容器:

image-20191126141916782

​ 包裝後的添加數據的方法:

image-20191126141859533

iterable接口

iterable叫作可迭代對象

咱們知道集合框架提供了不少不一樣的容器實現類,可是每種容器的內部實現不一樣,致使咱們須要記憶不一樣的取值方法,這對於使用者來講無疑增長學習成本,因此Java統一了各類容器的取值方式,即經過iterable接口

好處:
  • 實現了iterable的類均可以被foreach循環直接使用
接口方法:
public Iterator iterator();//用於返回一個迭代器接口對象

Iterator接口

Iterator叫作迭代器

迭代器用於獲取容器中的值

接口方法:
public boolean hasNext(); //判斷是否還有下一個
public Integer next();//獲取下一個的值

使用案例:

//寫一個支持foreach語法的類
import java.util.Iterator;
public class MyTest implements Iterable<Integer> {
    private int num = 0;
  
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            @Override
            public boolean hasNext() {
                return num < 11;
            }
          
            @Override
            public Integer next() {
                return num++;
            }
        };
    }

    //測試:
    public static void main(String[] args) {
        for (Integer i:new MyTest()) {
            System.out.println(i);
        }
    }
}
//該類能夠迭代獲得0-10這11個數字

ps:collection接口繼承了iterable,因此全部collection的子類能夠被foreach遍歷

集合的遍歷:

咱們可使用foreach來遍歷collection的任何子類對象:

案例:

HashSet<String> set = new HashSet<String>();
set.add("1");
set.add("2");
set.add("3");

ArrayList<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");

for (String s: list) {
    System.out.println(s);
}

for (String s: set) {
    System.out.println(s);
}

須要特別強調的是不容許在迭代期間刪除或添加元素,將會引起:java.util.ConcurrentModificationException異常,意思是不容許併發修改

那麼咱們如何刪除元素呢?

迭代期間添加刪除元素

//刪除或添加單個元素:
ArrayList<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
for (String s: list) {
  list.add("11111");
  break;//操做完成後直接break;
}
//刪除或添加多個元素方法1
//先保存須要刪除的元素,迭代完成在一併刪除
ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);

ArrayList deletes = new ArrayList();
for (Integer i:
     list) {
  if (i > 1){
    deletes.add(i);
  }
}
list.removeAll(deletes);


//刪除或添加多個元素方法2
//使用lambda表達式  jdk1.8以後可用
list.removeIf(i -> i > 1);

//刪除或添加多個元素方法3
//迭代copy的集合,從原始集合刪除
for (Integer i:
     (ArrayList<Integer>)list.clone()) {
  if(i > 1){
    list.remove(i);
  }
}

//刪除或添加多個元素方法4
//使用迭代器
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
  if (iterator.next() > 1){
    iterator.remove();
  }
}

Map接口

特色:

  • 數據已鍵值對的形式存儲
  • 鍵值對用Entry類對象表示
  • 不能包含重複的key
  • 鍵值對一一對應

注意:

  • 避免使用可變對象做爲key,若是對象的值改變時會引發equals方法的變化,將會致使不可預知的問題,,簡單的說,map之因此不存儲重複的key是由於,map使用hashCode+equals方法判斷是否重複,若是你使用的是可變對象,那麼當對象的屬性變化時,可能致使判斷重複的機制發生錯誤,例如:原本插入了Person對象,名字爲張三和李四,後來你把張三的名字改爲李四,那麼這時候就誤判斷這兩個對象key是否相同
  • 如何判斷對象是否相等
    • 第一步調用對象的hashCode得到一個整數進行比較,若是相同再調用equals方法進行判斷,若是equals也返回true的話則表示兩個對象徹底相同,這樣的對象是不會被存儲的
  • 綜上所述,咱們在自定義類中若是進行去重,則應當重寫這兩個方法(hashCode,equals)

主要實現類:

  • HashMap
    • 基於Hash表實現
    • 容許使用null做爲key或value
    • Map中的Entry(鍵值對)是無序的
    使用方法

遍歷方法:

//HashMap,不是Collection的子類因此不能直接foreach
//可用使用下面幾種方式:
//方法1
for ( String key: map.keySet()) {
  System.out.println(key+" "+map.get(key));
}
//方法2  
for (Map.Entry entry:map.entrySet()){
  System.out.println(entry.getKey() +" "+entry.getValue());
}

HashMap 數據結構解析:

  • HashMap內部使用hash表(本質是一個數組見圖一)

  • HashMap使用hash算法計算獲得存放的索引位置,一次來加快查詢速度,(比ArrayList還要快)

  • 一樣的既然本質是數組則少不了擴容和複製數據的問題了,這與ArrayList的缺點是同樣的
  • hash值相同不能表示key徹底相同,須要調用equals再次確認

  • 若是說key的hash值相同可是equals判斷不相同,那麼使用樹結構或者鏈表來存儲這些hash相同的元素,(具體使用哪一種根據當前map中元素的數量,超過64個元素則使用樹結構)

image-20191126123312490

獲取元素方法:

image-20191126123117994

存儲元素方法:

image-20191126134535014

存儲元素方法補充:

代碼在putVal方法的最後,當添加元素後的大小超過閾值時則直接擴容hash表

image-20191126135920717

擴容/轉換方法:

image-20191126130936107

總結:

  • HashMap是使用Hash表(本質是數組)來存儲數據,

  • 當keyHash值相同可是equals判斷不一樣時使用鏈表(添加快,查詢慢)

  • 當元素數量超過64時則將鏈表轉爲數結構(添加慢,查詢快)

最後再來一張框架圖,幫助理解:

image-20191126160453485

圖中的Listiterator 能夠實現倒敘迭代,須要的話諮詢查詢API

相關文章
相關標籤/搜索