猿创征文|【JavaSE】Map集合详细介绍


Map集合

概述

java.util.Map<k,v>集合 Map集合的特点: 1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)。 2.Map集合中的元素,key和value的数据类型可以相同,也可以不同。 3.Map集合中的元素,key是不允许重复的,value是可以重复的。 4.Map集合中的元素,key和value是一一对应。

结构继承图

继承图详细介绍

Map集合介绍: 1、Map集合和Collection集合没有关系。 2、Map集合以key和value的这种键值对的方式存储元素。 3、key和value都是存储java对象的内存地址。 4、所有Map集合的kev特点:无序不可重复的。 Map集合的key和Set集合存储元素特点相同。

HashMap集合介绍: HashMap集合底层是哈希表数据结构,查询速度快,是非线程安全的。 在JDK8之后,如果哈希表单向链表中元素超过8个,单向链表这种数据结构会变成红黑树数据结构。当红黑树上的节点数量小于6时,会重新把红黑树变成单向链表数据结构。这种方式也是为了提高检索效率,二叉树的检索会再次缩小扫描范围。提高效率。初始化容量16默认加载因子.75 JDK1.8之前:数组+单向链表 JDK1.8之后:数组+单向链表/红黑树(链表的长度超过8):提高查询的速度 HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致 扩容是:扩容之后的容量是原容量的2倍。

LinkedHashMap集合介绍: 1.底层是哈希表+链表(保证迭代的顺序) 2.是一个有序的集合,存储元素和取出元素的顺序是一致的

Hashtable集合介绍: Hashtable集合底层也是哈希表数据结构,是线程安全的,其中所有的方法都带有 synchronized关键字,效率较低,现在使用较少了,因为控制线程安全有其它更好的方案.

Properties属性类介绍: Properties是线程安全的,因为继承Hashtable,另外Properties存储元素的时候也是采用key和value的形式存储,并且key和value只支持String类型不支持其它类型。 Properties被称为属性类。

SortedMap集合介绍: SortedMap集合的key存储元素的特点: 首先是无序不可重复的,另外放在SortedMap集合key部分的元素会自动按照大小顺序排序称为可排序称为可排序的集合。

TreeMap集合底层的数据结构是一个二叉树。

Map接口中常用方法

public V put(K key,V value):把指定的键与指定的值添加到Map集合中。 public V remove(Object key):把指定的键所对应的键值对元素在Map集合中删除,返回被删除元素的值 public V get(Object key):根据指定的键,在Map集合中获取对应的值。 boolean containsKey(Object key):判断集合中是否包含指定的键

put方法

public V put(K key,V value):把指定的键与指定的值添加到Map集合中。 返回值:v 存储键值对的时候,key不重复,返回值v是null。 存储键值对的时候,kev重复,会使用新的value替换map中重复的value,返回被替换的value值。

举例

private static void show01() {
          
   
        //创建Map集合对象 多态
        Map<String,String> map = new HashMap<>() ;

        String v1 = map.put("小飞","冰冰1") ;
        System.out.println("v1:"+v1); //key不重复,返回值是null。输出:v1:null

        String v2 = map.put("小飞","冰冰2") ;
        System.out.println("v2:"+v2); //键重复 返回被替换的值。输出:v2:冰冰1 

        System.out.println(map); // 输出:{小飞=冰冰2}

        map.put("冷风","孔晓云");
        map.put("杨过","小龙女");
        System.out.println(map);//输出:{杨过=小龙女, 小飞=冰冰2, 冷风=孔晓云}

    }

remove方法

public V remove(Object key):把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。 返回值:V key存在,v返回被删除的值。 key不存在,v返回null。

举例

private static void show02() {
          
   
        //创建Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("丽颖",168);
        map.put("音",165);
        map.put("玲",178);
        System.out.println(map); //{玲=178, 丽颖=168, 音=165}

        Integer v1 = map.remove("玲");
        System.out.println("v1:"+v1);  //v1:178 key存在,返回被删除的值
        System.out.println(map); //{丽颖=168, 音=165}

        Integer v2 = map.remove("林志");
        System.out.println("v2:"+v2);//v2:null key不存在 返回null
        System.out.println(map); //{丽颖=168, 音=165}
//        注意问题 :多使用包装类接收,少使用基本数据类型

    }

get方法

public V get(Object key):根据指定的键,在Map集合中获取对应的值。 返回值:key存在,返回对应的value值。key不存在,返回null。

举例

private static void show03() {
          
   
        //创建Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("丽颖",168);
        map.put("音",165);
        map.put("玲",178);

        Integer v1 = map.get("丽颖");
        System.out.println("v1:"+v1);// v1:168 有对应的key返回对应的值

        Integer v2 = map.get("热巴");
        System.out.println("v2:"+v2); //v2:null  没有对应的key返回null
    }

containsKey方法

boolean containsKey(Object key):判断集合中是否包含指定的键,包含返回true,不包含返回false

举例

private static void show04() {
          
   
        //创建Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("丽颖",168);
        map.put("音",165);
        map.put("玲",178);

        boolean v1 = map.containsKey("丽颖");
        System.out.println("v1:"+v1); //v1:true  有这个key 返回ture

        boolean v2 = map.containsKey("颖");
        System.out.println("v2:"+v2); //v2:false  没有这个key 返回false
    }

Map集合两种遍历方式【重点】

键找值方式

Map集合的第一种遍历方式:通过键找值的方式 Map集合中的方法: Set<k> keySet(): 返回此映射中包含的键的 Set视图。 实现步骤: 1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中。 2.遍历set集合,获取Map集合中的每一个key。 3.通过Map集合中的方法get(key),通过key找到value。

举例

public static void main(String[] args) {
          
   
        //创建Map集合对象
        Map<String,Integer> map=new HashMap<>();
        map.put("丽颖",168);
        map.put("音",165);
        map.put("玲",178);

        //1.使用keySet方法,把map集合所有的key取出来,存储到set集合中
        Set<String> set = map.keySet();

        //2.遍历set集合,获取map集合中每一个key
        //使用迭代器遍历
        Iterator<String> it = set.iterator();
        while (it.hasNext()){
          
   
            String next = it.next(); //获取map集合中每一个key
            //3. 通过Map集合中的get方法,通过key找到value
            Integer integer = map.get(next);
            System.out.println(next+"="+integer);
        }
        System.out.println("=================");

        //使用增强for遍历
        for (String s : set) {
          
   
            //3. 通过Map集合中的get方法,通过key找到value
            Integer integer = map.get(s);
            System.out.println(s+"="+integer);
        }

        //简化增强for遍历
        // Set<String> set = map.keySet(); map.keySet()就相当于set集合
        for (String s : map.keySet()) {
          
   
            //3. 通过Map集合中的get方法,通过key找到value
            Integer integer = map.get(s);
            System.out.println(s+"="+integer);
        }
    }

键值对方式

Map集合遍历的第二种方式:使用Entry对象遍历。这种方式效率比较高,因为获取的key和value都是直接从Entry对象中获取的属性值,这种方式比较适合于大数据量。

Entry键值对对象:

Map集合中的方法: Set<Map.Entry<K,V >> entrySet()返回此映射中包含的映射关系的 Set 视图。

实现步骤: 1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中 2.谝历Set集合,获取每一个Entry对象 3.使用Entry对象中的方法getKey()和getValue()获取键与值

举例

public static void main(String[] args) {
          
   
        //创建Map集合对象
        Map<String,Integer> map=new HashMap<>();
        map.put("丽颖",168);
        map.put("音",165);
        map.put("玲",178);

        //1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个set集合中
        Set<Map.Entry<String, Integer>> set = map.entrySet();

        //2.遍历set集合
        //使用迭代器遍历
        Iterator<Map.Entry<String, Integer>> it = set.iterator();
        while (it.hasNext()){
          
   
            Map.Entry<String, Integer> next = it.next();
            //3.使用Entry对象中的方法getKey(),getValue()获取键和值
            String key = next.getKey();
            Integer value = next.getValue();
            System.out.println(key+"="+value);
        }
        System.out.println("================");

        //使用增强for遍历
        for (Map.Entry<String, Integer> aa : set) {
          
   
            //3.使用Entry对象中的方法getKey(),getValue()获取键和值
            String key = aa.getKey();
            Integer value = aa.getValue();
            System.out.println(key+"="+value);
        }
    }

HashMap和哈希表数据结构

HashMap集合key部分允许null吗? 允许 但是要注意:HashMap集合的key null值只能有一个。有可能面试的时候遇到这样的问题。

HashMap集合: 1、HashMap集合底层是哈希表/散列表的数据结构。 2、哈希表是一个怎样的数据结构呢? 哈希表是一个数组和单向链表的结合体。 数组:在查询方面效率很高,随机增删方面效率很低。 单向链表:在随机增删方面效率较高,在查询方面效率很低。 哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。

3、最主要掌握的是: map.put(k,v) map.get(k) 以上这两个方法的实现原理,是必须掌握的。

4、HashMap集合的key部分特点:无序,不可重复。 为什么无序?因为不一定挂到哪个单向链表上。 不可重复是怎么保证的?equals方法来保证HashMap集合的key不可重复。如果key重复了,value会覆盖。 放在HashMap集合key部分的元素其实就是放到HashSet集合中了。所以HashSet集合中的元素也需要同时重写hashCode()+equals()

5、哈希表HashMap使用不当时无法发挥性能! 假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。这种情况我们成为:散列分布不均匀。什么是散列分布均匀? 假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的。是散列分布均匀的。 假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题? 不行,因为这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。也是散列分布不均匀。 散列分布均匀需要你重写hashCode()方法时有一定的技巧。

同时重写hashCode和equals方法

1、向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法! equals方法有可能调用,也有可能不调用。 拿put(k,v)举例,什么时候equals不会调用? k.hashCode()方法返回哈希值 哈希值经过哈希算法转换成数组下标。 数组下标位置上如果是null,eauals不需要执行。 拿get(k)举例,什么时候equals不会调用? k.hashCode()方法返回哈希值, 哈希值经过哈希算法转换成数组下标。 数组下标位置上如果是null,equals不需要执行。

2、注意:如果一个类的equals方法重写了,那么hashCode()方法必须重写。并且equals方法返回如果是true,hashCode()方法返回的值必须一样。 equals方法返回true表示两个对象相同,在同一个单向链表上比较。那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。所以hashCode()方法的返回值也应该相同。

3、hashCode()方法和equals()方法不用研究了,直接使用IDEA工具生成,但是这两个方法需要同时生成。

4、终极结论: 放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。

5、对于哈希表数据结构来说: 如果o1和o2的hash值相同,一定是放到同一个单向链表上。 当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”。

HashMap存储自定义类型键值

HashMap存储自定义类型键值 Map集合保证key是唯一的: 作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一

举例

Person类

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

    public Person() {
          
   
    }

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

    @Override
    public String toString() {
          
   
        return "Person{" +
                "name=" + name +  +
                ", age=" + age +
                };
    }

    @Override
    public boolean equals(Object o) {
          
   
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
          
   
        return Objects.hash(name, age);
    }

    public String getName() {
          
   
        return name;
    }

    public void setName(String name) {
          
   
        this.name = name;
    }

    public int getAge() {
          
   
        return age;
    }

    public void setAge(int age) {
          
   
        this.age = age;
    }
}

主方法

public class 主方法 {
          
   
    public static void main(String[] args) {
          
   
    	show01();
        show02();
    }

    /*
        HashMap存储自定义类型键值
        key:String类型
            String类重写了hashCode和equals方法,可以保证key唯一
        value:Person类型
            value可以重复(同名同年龄的人视为同一个)
     */
    private static void show01() {
          
   
        //创建HashMap集合
        HashMap<String,Person> map =new HashMap<>();
        //往集合添加元素  key有重复会把新的value替换原来的value
        map.put("北京",new Person("张三",18));
        map.put("上海",new Person("李四",19));
        map.put("广州",new Person("王五",20));
        map.put("北京",new Person("赵六",18));
         //使用keySet加增强for遍历map集合
        Set<String> set = map.keySet();
        for (String s : set) {
          
   
            Person s1 = map.get(s);
            System.out.println(s+"-->"+s1);
        }
    }

    /*
        HashMap存储自定义类型键值
        key:Person类型
            Person类必须重写hashCode和equals方法,以保证key唯一
        value:String类型
            可以重复
     */
    private static void show02() {
          
   
        //创建HashMap集合
        HashMap<Person,String> map =new HashMap<>();
        //往集合添加元素
        map.put(new Person("女王",18),"英国");
        map.put(new Person("秦始皇",18),"秦国");
        map.put(new Person("普京",30),"俄罗斯");
        map.put(new Person("女王",18),"b国");
        //使用entryset和增强for遍历map集合
        Set<Map.Entry<Person, String>> entries = map.entrySet();
        for (Map.Entry<Person, String> entry : entries) {
          
   
            Person key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key+"-->"+value);
        }

    }
}

LinkedHashMap集合

java.util.LinkedHashMap<K, V>entends HashMap<K.,V> Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。 底层原理:哈希表+链表(记录元素的顺序)

举例

public static void main(String[] args) {
          
   
        HashMap<String,String> map = new HashMap<>();
        map.put("a","a");
        map.put("b","b");
        map.put("c","c");
        map.put("a","d");
        System.out.println(map); //{a=d, b=b, c=c}  key不允许重复,无序

        LinkedHashMap<String,String> linked = new LinkedHashMap<>();
        linked.put("a","a");
        linked.put("b","b");
        linked.put("c","c");
        linked.put("a","d");
        System.out.println(linked);// key 不允许重复,有序{a=d, b=b, c=c}
    }

Hashtable集合

java.util.Hashtable<K,V>集合 implements Map<K,V>接口

HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快。 Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢 。

HashMap集合(之前学的所有的集合):可以存储null值,null键 。 Hashtable集合,不能存储nul值,null键。

Hashtable和Vector集合一样,在idk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了。Hashtable的子类Properties依然活跃在历史舞台 Properties集合是一个唯一和IO流相结合的集合

举例

public class Hashtable集合 {
          
   
    public static void main(String[] args) {
          
   
        HashMap<String,String> map =new HashMap<>();
        map.put(null,"a");
        map.put("a",null);
        map.put(null,null);
        System.out.println(map); //{null=null, a=null}

        Hashtable<String,String> tab = new Hashtable<>();
          tab.put(null,"a");//NullPointerException 空指针异常
          tab.put("a",null);//NullPointerException 空指针异常
          tab.put(null,null);//NullPointerException 空指针异常
    }
}

Properties属性类

目前只需要掌播Properties属性类对象的相关方法即可。 Properties是一个Map集合,继承Hashtable,Properties的key和value都是string类型。 Properties被称为属性类对象。 Properties是线程安全的。

练习

计算一个字符串中每个字符出现的次数

示例

/*
    练习:计算一个字符串中每个字符出现的次数

    分析:
        1.使用Scanner获取用户输入的字符串
        2.创建Map集合,key是字符串中的字符,value是字符个数
        3.遍历字符串,获取每一个字符
        4.使用获取到的字符,去Map集合判断key是否存在
            key存在:
                通过字符串(key),获取value(字符个数)
                value++
                put(key,value)
            key不存在:
                put(key,1)
        5.遍历Map集合,输出结果
 */
public class 计算一个字符串中每个字符出现的次数 {
          
   
    public static void main(String[] args) {
          
   
        //1.使用Scanner获取用户输入的字符串
        Scanner sc =new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String next = sc.next(); //获取字符串
        //2.创建Map集合,key是字符串中的字符,value是字符个数
        HashMap<Character,Integer> map =new HashMap<>();
        //3.遍历字符串,获取每一个字符
        for (Character c:next.toCharArray()){
          
   
            //4.使用获取到的字符,去Map集合判断key是否存在
            if (map.containsKey(c)){
          
   
                //key已经存在,通过字符串(key),获取value(字符个数),让字符个数加一
                Integer value = map.get(c);
                value++;//让它加一
                map.put(c,value);//把新的覆盖上去
            }else {
          
   
                //key不存在:put(key,1)
                map.put(c,1); //集合中没这个key,让他存进集合,初始值为1
            }
        }
        // 不遍历 直接输出也ok
        System.out.println(map);

        //5.遍历Map集合,输出结果
        for (Character key:map.keySet()){
          
   
            Integer value = map.get(key);
            System.out.println(key+"="+value);
        }
    }
}

斗地主案例

/*
    斗地主综合案例:有序版本
    1.准备牌
    2.洗牌
    3.发牌
    4.排序
    5.看牌
 */
public class 斗地主案例 {
          
   
    public static void main(String[] args) {
          
   
        //1.准备牌
        //创建一个Map集合,存储牌的索引和组装好的牌
        HashMap<Integer,String> poker = new HashMap<>();
        //创建一个List集合,存储牌的索引
        ArrayList<Integer> pokerIndex = new ArrayList<>();
        //定义两个集合,存储花色和牌的序号
        List<String> colors = List.of("♠", "♥", "♣", "♦");
        List<String> numbers = List.of("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 nu:numbers){
          
   
            for (String co:colors){
          
   
                poker.put(index, nu + co);
                pokerIndex.add(index);
                index++;
            }
        }
//        System.out.println(poker);
//        System.out.println(pokerIndex);

        //2.洗牌
        Collections.shuffle(pokerIndex);
//        System.out.println(pokerIndex);

        //3.发牌
        //定义四个集合,存储玩家牌的索引和底牌的索引
        ArrayList<Integer> dipai = new ArrayList<>();
        ArrayList<Integer> wanjia1 = new ArrayList<>();
        ArrayList<Integer> wanjia2 = new ArrayList<>();
        ArrayList<Integer> wanjia3 = new ArrayList<>();
        //遍历存储牌索引的List集合,获取每一个牌的索引
        for (int i = 0; i < pokerIndex.size(); i++) {
          
   
            Integer s = pokerIndex.get(i);
            //先判断底牌
            if (i>=51){
          
   
                dipai.add(s);
            }else if (i%3==0){
          
   
                wanjia1.add(s);
            }else if (i%3==1){
          
   
                wanjia2.add(s);
            }else if (i%3==2){
          
   
                wanjia3.add(s);
            }
        }
        //4.排序
        Collections.sort(dipai);
        Collections.sort(wanjia1);
        Collections.sort(wanjia2);
        Collections.sort(wanjia3);
//        System.out.println(wanjia1);

        //5.看牌
        lookPoker("飞飞",poker,wanjia1);
        lookPoker("东海",poker,wanjia2);
        lookPoker("彬彬",poker,wanjia3);
        lookPoker("底牌",poker,dipai);


    }
    /*
        5.看牌  定义一个看牌的方法,提高代码的复用性
        参数:
            String name:玩家名称
            HashMap<Integer,String> poker:存储牌的扑克集合
            ArrayList<Integer> list: 存储玩家和底牌的List集合
        查表法:
            遍历玩家或底牌集合,获取牌的索引
            使用牌的索引,去Map集合找到对应的牌

     */
    public static void lookPoker(String name,HashMap<Integer,String> poker,ArrayList<Integer> list){
          
   
        //输出玩家名称不换行
        System.out.print(name+": ");
        //遍历玩家或底牌集合,获取牌的索引
        for (Integer integer : list) {
          
   
            //使用牌的索引,去Map集合找到对应的牌
            String value = poker.get(integer);
            System.out.print(value+" ");
        }
        System.out.println();//打印完每一个玩家的牌换行
    }


}

JDK9对集合添加的优化_of方法

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/291022.html

(0)
上一篇 2022年10月14日
下一篇 2022年10月14日

相关推荐

发表回复

登录后才能评论