Java學習:集合雙列Map


數據結構

數據結構:

數據結構_棧:先進后出

  • 入口和出口在同一側

數據結構_隊列:先進先出

  • 入口和出口在集合的兩側

數據結構_數組:

  • 查詢快:數組的地址是連續的,我們通過數組的首地址可以找到數組,通過數組的索引可以快速的查找某一個元素。
  • 增刪慢:數組的長度是固定的,我們想要增加/刪除一個元素,必須創建一個新數組,把原數組的數據復制過來
例:
int[] arr = new int[]{1,2,3,4};

要把數組索引是3的元素刪除

  • 必須創建一個新的數組,長度是原數組的長度-1
  • 把原數組的其它元素復制到新數組中
  • 在新數組的地址賦值給變量arr
  • 原數組會在內存中被銷毀(垃回收收)

數據結構_鏈表:

  • 查詢慢:鏈表中地址不是連續的,每次查詢元素,都必須從頭開始查詢。
  • 增刪快:鏈結構,增加/刪除一個元素,對鏈的整體結構沒有影響,所以增刪快

鏈表中的每一個元素也稱之為一個節點
一個節點包含了一個數據源(存儲數組),兩指針域(存儲地址)

  • 單向鏈:鏈中只有一條鏈,不能保證元素的順序(存儲元素和取出元素的順序可能不一致)
  • 雙向鏈:鏈中有兩鏈,有一條鏈是專門記錄元素的順序,是一個有序的集合

二叉樹:分支不能超過兩

  • 排序樹/查找樹:在二叉樹的基礎上,元素是有大小順序的(左子樹小,右子樹大)
  • 平衡樹:左孩子和右孩子相等
  • 不平衡樹:左孩子和右孩子不相等

紅黑樹:
特點:趨近於平衡樹,查詢的速度非常的快,查詢葉子節點最大次數和最小次數不能超過2倍
約束:

  1. 節點可以是紅色的或者是黑色的
  2. 根節點是黑色的
  3. 葉子節點(空節點)是黑色的
  4. 每個紅色的節點的子節點都是黑色的
  5. 任何一個節點到其每一個葉子節點的所有路徑上黑色節點數相等

List接口

java.util.list接口 extends Collection接口

List接口的特點:

  1. 有序的集合,存儲元素和取出元素的順序是一致的(存儲123 取出123)
  2. 有索引,包含了一些帶索引的方法
  3. 允許存儲重復的元素

List接口中帶索引的方法(特有):

public void add(int index,E element):將指定的元素,添加到該集合中的指定位置上。
public E get(int index):返回集合中指定位置的元素。
public E remove(int index):移除列表中指定位置的元素,返回的是被移除的元素。
public E set(int index,E element):用指定元素替換集合中指定位置的元素,返回值的更新前的元素。

注意:
操作索引的時候,一定要防止索引越界異常

  • IndexOutOfBoundsException:索引越界異常,集合會報
  • ArrayIndexOutOfBoundsException:數組索引越界異常
  • StringIndexOutOfBoundsException:字符串索引越界異常
//創建一個List集合對象,多態
    List<String> list = new ArrayList<>();
    {
        //public void add(int index,E element):將指定的元素,添加到該集合中的指定位置上。
        //在索引2和索引3之間添加一個cainiao
        list.add(3,"cainiao");//{a,b,c,d}-->{a,b,c,cainiao,d}
        //移除元素
        String removeE = list.remove(2)
        //替換元素
        String setE = list.set(4,"A");
        }

List的子類

  • ArrayList集合

java.util.ArrayList集合數據存儲的結構是數組結構元素增刪慢,查找快,由於日常開發中使用最多的功能為查詢數據、遍歷數據,所以ArrayList是最常用的集合。

  • LinkedList集合

java.util.LinkedList集合數據存儲的結構是鏈結構。方便元素添加,刪除的集合。

java.util.LinkedList集合 implements List接口

ArrayList集合的特點:

  1. 底層是一個鏈的結構:查詢慢,增刪快
  2. 里邊包含了大量操作首尾元素的方法

注意:
使用ArrayList集合特有的方法,不能使用多態

  • public void addFirst(E e):將指定元素插入此列表的開頭
  • public void addLast(E e):將指定元素添加到此列表的結尾
  • public void addpush(E e):將元素推如此列表所表示的推棧
  • public E getFirst():返回此列表的第一個元素。
  • public E getLast():返回此列表的最后一個元素。
  • public E removeFirst():移除並返回此列表的第一個元素。
  • public E removeLast():移除並返回此列表的最后一個元素。
  • public E pop():從此列表所表示的推棧處彈出一個元素。相當於removeFirst
  • public boolean isEmpty():如果列表不包含元素,則返回true

Vector集合

Vector 類可以實現可增長的對象數組。
與新collection不同,Vector是同步的。

Set接口

java.util.Set接口 extends Collection接口

Set接口的特點:

  1. 不允許存儲重復的元素
  2. 沒有索引,沒有帶索引的方法,也不能使用普通的for循環遍歷
java.util.HashSet集合 implements Set接口

HashSet特點:

  1. 不允許存儲重復的元素
  2. 沒有索引,沒有帶索引的方法,也不能使用普通的for循環遍歷
  3. 是一個無序的集合,存儲元素和取出元素的順序有可能不一致
  4. 底層是一個哈希表結構(查詢的速度非常快)
Set<Integer> set = new HashSet<>();
//使用add方法往集合中添加元素
set.add(1);
set.add(3);
set.add(2);
set.add(1);
//使用迭代器遍歷Set集合
Iterator<Integer> it = set.iterator();
while(it.hasNext(){
    Iterator n = it.next();
    System.out.println(n);//1,2,3 無序且不重復
}

HashSet集合存儲數據的結構(哈希表)

哈希值:是一個十進制的整數,由系統隨機給出(就是對象的地址值,是一個邏輯地址,是模擬出來得到的地址,不是數據實際存儲的物理地址)
在Object類有一個方法,可以獲取對象的哈希值

int hashCode() 返回該對象的哈希碼值。

HashCode方法的源碼:

public native int hashCode();
native:代表該方法調用的是本地操作系的的方法

哈希表

哈希表:hashSet集合存儲數據的結構
jdk1.8版本之前:哈希表 = 數組+鏈表
jdk1.8版本之后:

  • 哈希表 = 數組+鏈表;
  • 哈希表 = 數組+紅黑樹(提高查詢的速度)

哈希表的特點:速度快

存儲數據到集合中,先計算元素的哈希值

  • abc:96354 在數組的存儲位置
  • 重地——通話:1179395 兩元素不同,但是哈希值相同 哈希沖突


數組結構:把元素進行了分組(相同哈希值的元素是一組)

鏈表/紅黑樹結構:把相同哈希值的元素連到一起(如何鏈表的長度超過了8位,那么就會把鏈轉換位紅黑樹(提高查詢的速度))

Set集合存儲元素不重復的原理

Set集合存儲元素不重復的元素的前提:

前提:存儲的元素必須重寫hashCode方法和equals方法

//創建HashSet集合對象
HashSet<String> set = new HashSet<>();//哈希表:數組+鏈表/紅黑樹 
String s1 = new String("abc");
String s2 = new String("abc");

set.add(s1);
set.add(s2);
set.add("重地");
set.add("通話");
set.add("abc");
System.out.println(set);/[重地,通話,abc]

原因:
Set集合在調用add方法的時候,add方法會調用元素hashCode方法和equals方法,判斷元素是否重復

HashSet存儲自定義類型元素

Set集合報錯元素原因:
存儲的元素(String,Integer,...Student,Person...),必須重寫hashCode方法和equals方法

LinkedHashSet集合

java.util.LinkedHashSet集合 extends HashSet集合

LinkedHashSet集合特點:
底層是一個哈希表(數組+鏈表/紅黑樹+鏈表:多了一條鏈(記錄元素的存儲順序),保存元素有序

HashSet<String> set = new HashSet<>();//[]無序,不允許重復
LinkedHashSet<String> set = new LinkedHashSet<>();[]//有序,不允許重復

可變參數

可變參數:是JDK1.5 之后出現的新特性

使用前提:

  • 當方法的參數列表數據類型已經確定,但是參數的個數不確定,就可以使用可變參數。

使用格式:定義方法時使用

  • 修飾符 返回值類型 方法名(數據類型...變量名){}

可變參數的原理:

  • 可變參數底層就是一個數組,根據傳遞參數個數不同,會創建不同長度的數組,來存儲這些參數
  • 傳遞的參數個數,可以是0個 (不傳遞),1,2...多個

可變參數的注意事項:

  1. 一個方法的參數列表,只能有一個可變參數
  2. 如果方法的參數有多個,那么可變參數必須寫在參數列表的末尾
public static void method(String b, double c ,int d ,int...a){}

//可變參數的特殊(終極)寫法
public static void method(Object...obj){}

Collections集合工具類的方法
java.util.Collections是集合工具類,用來對集合進行操作。部分方法如下:
    public static <T> void sort(List<T> List ,Comparator<? super T>):將集合中元素按照指定規則排序。
    
    Comparator和Comparable的區別
        Comparable:自己(this)和別人(參數)比較,自己需要實現Comparable接口,重寫比較的規則compareTo方法
        Comparator:相當於找一個第三方的裁判,比較兩
        
    Comparator的排序規則:
        o1-o2:升序

Map集合

Collection接口: 定義了單列集合規范 Collection<E>

  • 每次存儲一個元素 單個元素

Map接口:定義了雙列集合的規范 Map<K,V>

  • 每次存儲一對元素

java.util.Map<K,V>集合

Map集合的特點:

  1. Map集合是一個雙列集合,一個元素包含兩值(一個key,一個value)
  2. Map集合中的元素,key和value的數據類型可以相同,也可以不同
  3. Map集合中的元素,key是不允許重復的,value是可以重復的
  4. Map集合中的元素,key和value是一一對應的
java.util.HashMap<K,V>集合 implements Map<K,V>接口

HashMap集合的特點:
1.HashMap集合底層是哈希值:查詢的速度特別的快

  • JDK.8之前:數組+單向鏈表
  • JDK.8之后:數組+單向鏈表/紅黑樹(鏈表的長度超過8):提高查詢的速度

2.HashMap集合是一個無序的集合,存儲元素和取出元素的順序有可能不一致

java.util.LinkedHashMap<k,v>集合 extends HashMap<K,V>集合

LinkedHashMap的特點:

  1. LinkedHashMap集合底層是哈希表+鏈表(保證迭代的順序)
  2. LinkedHashMap集合是一個有序的集合,存儲元素和取出元素的順序是一致的

Map接口中的常用方法

public V put (K key,V value):把指定的鍵與指定的值添加到Map集合中。

返回值:v

  • 存儲鍵值對的時候,key不重復,返回值V是null
  • 存儲鍵值對的時候,key重復,會使用新的value替換map中重復的value,返回被替換的value值

public V remove(Object key):把指定的值 所對應的鍵值對元素 在Map集合中刪除,返回被刪除元素的值。
返回值:V

  • key存在,v返回被刪除的值
  • key不存在,v返回null

public V get(Object key)根據指定的鍵,在Map集合中獲取對應的值。
返回值:

  • key存在,返回對應的value值
  • key不存在,返回null

boolean containsKey(Object key) 判斷集合中是否包含指定的鍵

  • 包含返回true,不包含返回false

Map集合的第一種遍歷方式:通過鍵找值的方式

Map集合中的方法:

  • Set<K> keySet() 返回此映射中包含的鍵的 Set 視圖。

實現步驟:

  1. 使用Map集合中的方法keySet(),把Map集合所有的key取出來,存儲到一個Set集合中。
  2. 遍歷Set集合,獲取Map集合中的每一個Key
  3. 通過Map集合中的方法get(key),通過key找到value

Entry鍵值對對象

Map.Entry<K,V>:在Map接口中有一個內部接口Entry
作用:當Map集合一創建,那么就會在Map集合中創建一個Entry對象,用來記錄鍵與值(鍵值對對象,鍵與值的映射關系)

Map集合遍歷的第二種方式:使用Entry對象遍歷

Set<Map.Entry<K,V> entrySet() 返回此映射中包含的鍵的 Set 視圖。
實現步驟:

  1. Set<Map.Entry<K,V>> entrySet(): 把Map集合內部的多個Entry對象取出來存儲到一個Set集合中
  2. 遍歷Set集合:獲取Set集合中的每一個Enter對象
  3. 獲取Entry對象中的方法:getKey()獲取key,getValue() 獲取value

HashMap存儲自定義類型鍵值

key:Person類型

  • Person類型必須重寫hashCode方法和equals方法,以保證key唯一

value:String類型

  • 可以重復
java.util.linkedHashMap<K,V> extends HashMap<K,V>

Map 接口的哈希表和鏈表列表實現,具有可預知的迭代順序。

底層原理:

  • 哈希表+鏈表(記錄元素的順序)

Hashtable集合

java.util.Hashtable<K,V>集合 implements Map<k,V>接口
  • Hashtable:底層也是一個哈希表,是一個線程安全的集合,是單線程集合,速度慢
  • HashMap:底層是一個哈希表,是一個線程不安全的集合,是多線程的集合,速度快
  • HashMap集合(之前學的所有集合):可以存儲null值,null鍵
  • Hashtable集合:不能存儲null值,null鍵
  • Hashtable集合和Vector集合一樣,在jdk1.2版本之后被更先進的集合(HashMap,ArrayList)取代了
  • Hashtable的子類properties依然活躍在歷舞台
  • Properties集合是一個唯一和I/O流相結合的集合

練習:

  • 計算一個字符串中每一個字符出現的次數

分析:
1.使用Scanner獲取用戶輸入的字符串
2.創建Map集合,key是字符串中的字符,value是字符的個數
3.遍歷字符串,獲取每一個字符
4.使用獲取到的字符,去Map集合判斷key是否存在

  • key存在:
通過字符(key),獲取value(字符個數)
value++
put(key,value)把新的value存儲到Map集合中
  • Key不存在:
put(key,1)

5.遍歷Map集合,輸出結果

    public class CaiNiao{
        public static void main(String[] args){
            //1.使用Scanner獲取用戶輸入的字符串
            Scanner sc = new Scanner(System.in);
            System.out.println("請輸入一個字符串:");
            String str = sc.next();
            //2.創建Map集合,key是字符串中的字符,value是字符的個數
            HashMap<Character.Integer> map = new HashMap<>();
            //3.遍歷字符串,獲取每一個字符
            for(char c : str.toCharArray()){
                //4.使用獲取到的字符,去Map集合判斷key是否存在
                if(map.containsKey(c)){
                    //key存在 
                    Integer value = map.get(c);
                    value++;
                    map.put(c,value);
                }else{
                    //key不存在 
                    map.put(c,1);
                }
            }
            //5.遍歷Map集合,輸出結果
            for(Character key : map.keySet(){
                Integer value = map.get(key);
                System.out.println(key+"="+value);
            }    
        }
    }

JDK9對集合添加的優化

JDK9的新特性:

  • list接口,Set接口,Map接口:里邊增加了一個靜態的方法of,可以給集合一次性添加多個元素
  • static <E> List<E> of (E... elements)

使用前提:

  • 當集合中存儲的元素的個數已經確定了,不在改變時使用

注意:

  1. of方法只適用於list接口,set接口,Map接口,不適用於接口的實現類
  2. 2of方法的返回值是一個不能改變的集合,集合不能再使用add,put方法添加元素,會拋出異常
  3. 3Set接口和Map接口在調用of方法的時候,不能有重復的元素,否則會拋出異常

Debug追蹤

Debug調試程序:

  • 可以讓代碼逐行執行,查看代碼執行的過程,調試程序中出現的bug

使用方式:

  • 在行號的右邊,鼠標左鍵單擊,添加斷點(每個方法的第一行,哪里有bug添加到哪里)
  • 右鍵,選擇Debug執行程序
  • 程序就會停留在添加的第一個斷點處

執行程序:

  • f8:逐行執行程序
  • f7:進入到方法中
  • shift + f8:跳出方法
  • f9:跳到下一個斷點,如果沒有下一個斷點,那么就結束程序
  • ctrl + f2:退出debug模式,停止程序

斗地主綜合案例:有序版本(雙列)

  • 1.准備牌
  • 2.洗牌
  • 3.發牌
  • 4.排序
  • 5.看牌

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class CaiNiao{
    public static void main(String[] args){
    //1.准備牌
    //創建一個Map集合,存儲牌的索引和組裝好的牌
    HashMap<Integer,String> poker = new HashMap<>();    
    //創建一個List集合,存儲花色和牌索引
    ArrayList<Integer> pokerIndex = new ArrayList<>();
    //定義兩個數組,一個數組存儲牌的花色,一個數組存儲牌的序號
    String[] colors = {"?","?","?","??"};
    String[] numbers = {"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
    //先把大王和小王存儲到集合中
    //定義一個牌的索引
    int index = 0;
    poker.put(index,"大王");
    pokerIndex.add(index);
    index++;
    poker.put(index,"小王");
    pokerIndex.add(index);
    index++;

    //循環嵌套遍歷兩個數組,組裝52張牌
    for(String numbers:numbers){
        for (String color : colors){
            poker.put(index.color+number);
            pokerIndex.add(index);
            index++;
        }
    }
    //    System.out.println(poker);
    //    System.out.println(pokerIndex);
    
    /*
    2.洗牌
    使用集合的工具類Collections中的方法
    static void shuffle(List<?> list) 使用默認的隨機源對指定列表進行置換。
    
    */
    Collections.shuffle(poker);
    //System.out.println(poker);
    
    /*
    3.發牌
    */
    //定義4 個集合,存儲玩家的牌和底牌
    ArrayList<String> player01 = new ArrayList<>();
    ArrayList<String> player02 = new ArrayList<>();
    ArrayList<String> player03 = new ArrayList<>();
    ArrayList<String> dipai = new ArrayList<>();
    /*
    遍歷poker集合,獲取每一張牌
    使用poker集合的索引%3給3個玩家輪流發牌
    剩余3張牌給底牌
    注意:
        先判斷底牌(i>51),否則牌就發沒了
    
    */
    for(int i = 0;i<poker.size();i++){
        //獲取每一張牌
        String p = poker.get(i);
        //輪流發牌
        if(i>=51){
            //給底牌發牌
            diPai.add(p);
        }else if (i%3==0){
            //給玩家1發牌
            player01.add(p);
        }else if (i%3==1){
            //給玩家2發牌
            player02.add(p);
        }else if(i%3==2){
            //給玩家3發牌
            player03.add(p);
        }
    }
    
    /*
        4.排序
        使用Collectio中的方法sort(List)
        默認是升序排序
    */
    Collection.sort(player01);
    Collection.sort(player02);
    Collection.sort(player03);
    Collection.sort(dipai);
    
    
    
        //5.看牌
    lookpoker("劉德華:",poker,player01);
    lookpoker("周潤發:",poker,player02);
    lookpoker("周星馳:",poker,player03);
    lookpoker("底牌 :",poker,dipai);

    }
    /*
        定義一個看牌方法,提高代碼的復用性
        參數:
            String name:玩家名稱
            HashMap<Integer,String> poker:存儲牌的poker集合
            ArrayList<Integer> List:存儲玩家和底牌的list集合
        查表法:
            遍歷玩家或者底牌集合,獲取牌的索引
            使用牌的索引,去Map集合中,找到對應的牌

    */
    
    public static void lookpoker(String name ,HashMap<Integer,String> poker,ArrayList<Integer> list){
        //輸出玩家名稱,不換行
        System.out.println(name + ";");
        //遍歷玩家或者底牌集合,獲取牌的索引
        for(Integer key : list){
            //使用牌的索引,去Map集合中,找到對應的牌
            String value = poker.get(key);
            System.out.println(value+"");
        }
        System.out.println();//打印完每一個玩家的牌,換行
    }
    

}

 


免責聲明!

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



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