ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

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

2022-10-13 20:24:36  阅读:240  来源: 互联网

标签:数据结构课程设计 小白学JAVA大概需要多久


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方法

标签:数据结构课程设计,小白学JAVA大概需要多久
来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有