java集合框架(二)——Map集合

目录

一、整体概述

 二、Map常用方法

 三、Map 集合的遍历方式

1.  键找值

2.  键值对

 3.  Lambda 表达式

 四、Map集合案例——统计投票人数

五、Map 底层原理

1. HashMap :无序、不重复、无索引

 2. LinkedHashMap : 有序、不重复、无索引

3. TreeMap: 默认升序排序,不重复,无索引

五、集合的嵌套


一、整体概述

  • Map集合称为双列集合,格式:{key1=value1,key2=value2,key3=value3,…}
  • 一次需要存一对数据做为一个元素Map集合的每个元素“key=value”称为一个键值对/键值对对象/一个Entry对象,Map集合也被叫做“键值对"
  • 集合Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值
  • 应用场景:需要存储一一对应的数据时,就可以考虑用Map集合来做
  • Map集合体系特点:
    • tip: Map系列集合的特点都是由键决定的,值只是一个附属品,值不做要求
    • HashMap(由键决定特点):  无序、不重复、无索引; (用的最多)
    • LinkedHashMap (由键决定特点):  有序、不重复、无索引
    • TreeMap (由键决定特点):  按照大小默认升序排序、不重复、无索引
	public static void main(String[] args) {
	    //Map<String,Integer> map = new HashMap<>(); //经典代码 按照键 无序 不重复 无索引
	    Map<String,Integer> map = new LinkedHashMap<>(); //经典代码 按照键 有序 不重复 无索引
	    map.put("大佬",100);
	    map.put("手表",100);
	    map.put("手表",50);
	    map.put("樊振东",79);
	    map.put("手表",150);//重复的数据会覆盖前面的数据
	    System.out.println(map);//{大佬=100, 手表=150, 樊振东=79}
	
	    Map<Integer,String> map1 = new TreeMap<>(); //经典代码 按照键 有规定排序默认按升序 不重复 无索引
	    map1.put(25,"java");
	    map1.put(15,"樊振东");
	    map1.put(36,"牛魔王");
	    System.out.println(map1);//{15=樊振东, 25=java, 36=牛魔王}
	}

 二、Map常用方法

	public static void main(String[] args) {
	    //Map<String,Integer> map = new HashMap<>(); //经典代码 按照键 无序 不重复 无索引
	    Map<String,Integer> map = new LinkedHashMap<>(); //经典代码 按照键 有序 不重复 无索引
	    map.put("大佬",100);
	    map.put("手表",100);
	    map.put("手表",50);
	    map.put("樊振东",79);
	    map.put("手表",150);//重复的数据会覆盖前面的数据
	    System.out.println(map);//{大佬=100, 手表=150, 樊振东=79}
	
	    //2. public int size() : 获取集合大小
	    System.out.println(map.size());
	
	    //public void clear(): 清空集合
	    //map.clear();
	
	    //3. public boolean isEmpty() : 判断集合是否为空
	    System.out.println(map.isEmpty());
	
	    //4.public V get(object key)根据键获取对应值
	    System.out.println(map.get("樊振东")); //79
	    System.out.println(map.get("牛马")); // 不存在 null
	
	    //5.public V remove(object key):根据键删除整个元素(删除键会返回键的值)
	    System.out.println(map.remove("手表"));
	
	    //6.public boolean containsKey(0bject key):判断是否包含某个键 ,包含返回true ,反之
	    System.out.println(map.containsKey("樊振东"));
	
	    //7.public boolean containsValue(object value): 判断是否包含某个值。
	    System.out.println(map.containsValue(79));
	
	    //8.public Set<K> keySet():获取Map集合的全部键
	    System.out.println(map.keySet()); //[大佬, 樊振东]
	
	    //9.public Collection<V> values();获取Map集合的全部值
	    Collection<Integer> values = map.values();
	    System.out.println(values); //[100, 79]
	
	    //10.把其他Map集合的数据倒入到自己集合中来。(拓展)
	    Map<String,Integer> map2 = new HashMap<>();
	    map2.put("j1",11);
	    map2.put("j2",88);
	    map2.put("j8",66);
	    Map<String,Integer> map3 = new HashMap<>();
	    map3.put("j2",55);
	    map3.put("j9",99);
	    map2.putAll(map3);
	    System.out.println(map2); //{j1=11, j2=55, j8=66, j9=99}
	    System.out.println(map3); //{j2=55, j9=99}
	 }

 三、Map 集合的遍历方式

1.  键找值

(先获取Map集合全部的键,再通过遍历键来找值)

  需要用到的方法:public Set<K> keySet() 获取所有键的集合;

                               public V get(Object key) 根据键获取其对应的值

	public static void main(String[] args) {
	    Map<String,Double> map = new HashMap<>();
	    map.put("蜘蛛精",165.9);
	    map.put("蜘蛛精",175.9);
	    map.put("紫霞",169.8);
	    map.put("孙悟空",180.5);
	    map.put("牛魔王",190.6);
	
	    //获取键
	    Set<String> keys = map.keySet();
	    System.out.println(keys);
	
	    //遍历键得到对应的值
	    for (String key : keys) {
	        double height = map.get(key);
	        System.out.println(key + "===>" + height);
	    }
	}

2.  键值对

	public static void main(String[] args) {
	    Map<String,Double> map = new HashMap<>();
	    map.put("蜘蛛精",169.8);
	    map.put("紫霞",165.8);
	    map.put("至尊宝",169.5);
	    map.put("牛魔王",183.6);
	    System.out.println(map);
	
	    //1. 调用Map集合提供的entrySet方法,把Map集合转换成键值对类型的Set集合
	    Set<Map.Entry<String, Double>> entries = map.entrySet();
	    for (Map.Entry<String, Double> entry : entries) {
	        String key = entry.getKey();
	        double values = entry.getValue();
	        System.out.println(key + "===>" + values);
	    }
	}

 3.  Lambda 表达式

1.  理解:new  BiConsumer<String, Double>() 之后,在forEach里面,会把Map集合转换成键值对类型的Set集合,再拿到key和value值,调用accept方法,传入key和value值

	map.forEach(new BiConsumer<String, Double>() {
	    @Override
	    public void accept(String k, Double v) {
	        System.out.println(k + "--->" + v);
	    }
	});

简化写法与完整代码:

	public static void main(String[] args) {
	    Map<String,Double> map = new HashMap<>();
	    map.put("蜘蛛精",169.8);
	    map.put("紫霞",165.8);
	    map.put("至尊宝",169.5);
	    map.put("牛魔王",183.6);
	    System.out.println(map);
	 
	    map.forEach((k,v) -> {
	        System.out.println(k + "--->" + v);
	    });
	}

 四、Map集合案例——统计投票人数

	/*
	* 需求: 某个班级80名学生,现在需要组织秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多。
	* 分析: 将80个学生选择的数据拿到程序中去,[A,A,B,A,B,C,D,…]
	     一一对应,景点和选择人数,所以考虑用Map集合来存储最终结果
	     准备一个Map集合用于存储统计的结果,Map<String,Integer>,键是景点,值代表投票数量
	     遍历80个学生选择的景点,每遍历一个景点,就看Map集合中是否存在该景点,不存在存入“景点=1存在则其对应值+1
	 */
	public static void main(String[] args) {
	    //1. 把80个学生选择的景点数据拿到程序中来
	    List<String> data = new ArrayList<>();
	    String[] selects = {"A","B","C","D"};
	    Random r =new Random();
	    for (int i = 0; i < 80; i++) {
	        int ran  = r.nextInt(4);
	        String num = selects[ran];
	        data.add(num);
	    }
	    System.out.println(data);
	
	    //开始统计每个景点的投票人数
	    //准备一个Map集合用于统计最终的结果
	    Map<String,Integer> result  = new HashMap<>();
	
	    //开始遍历80个景点数据
	    for (String s : data) {
	        //询问Map集合里面是否存在该景点
	       if (result.containsKey(s)){
	           //说明这个景点之前统计过,其值加1,存入到Map集合里面去
	          int numbers = result.get(s) + 1;
	          result.put(s,numbers);
	       }else {
	           //这个景点第一次统计
	           result.put(s,1);
	       }
	    }
	    System.out.println(result);
	}

五、Map 底层原理

1. HashMap :无序、不重复、无索引

  • HashMap跟HashSet的底层原理是一摸一样的,都是基于哈希表实现的

(实际上原来的Set集合的底层就是基于Map实现的,只是Set只要键数据,不要值数据)

流程:首先创建一个长度为16的数组;把HashMap的键值作为一个Entry对象,然后利用计算哈希值存入数组中; 

如果有一个重复数据要存入,在JDK8之前会直接替换原来的数据,原来的数据存到新数据下面,JDK8之后,新数据会存到原来的数据下面;

但存入数据长度大于12时,会创建一个新数组(扩容);

当链表长度大于8,数组长度大于64,会转变成红黑树

  • HashMap集合是一种增删改查数据,性能都较好的集合
  • 但是它是无序,不能重复,没有索引支持的(由键决定特点)
  • 如果键存储的是自定义类型的对象,可以通过重写hashcode和equals方法,这样可以保证多个对象内容一样时,HashMap集合就能认为是重复的
  • HashMap的键依赖hashcode方法和equals方法保证键的唯一 

当你尝试将一个新的键值对添加到 HashMap 中时,HashMap 首先会调用键对象的 hashCode() 方法来计算其哈希码,并根据这个哈希码确定键应该存储的位置。

如果在该位置没有其他键,新的键值对就会被添加到 HashMap 中。

如果该位置已经有其他键,HashMap 会使用键对象的 equals() 方法来检查新键是否与现有键相等。

如果 equals() 返回 true,表示两个键相等,HashMap 将更新现有键的值(如果 HashMap 允许键被覆盖)。

如果 equals() 返回 false,表示两个键不相等,HashMap 将使用一种冲突解决策略(通常是链表或红黑树)来存储新的键值对。

	public static void main(String[] args) {
	    Map<Student,String> map = new HashMap<>();
	    map.put(new Student("蜘蛛精",25,168.5),"盘丝洞");
	    map.put(new Student("蜘蛛精",25,168.5),"水帘洞");
	    map.put(new Student("至尊宝",29,145.5),"水帘洞");
	    map.put(new Student("牛魔王",19,175.5),"牛头山");
	    System.out.println(map);
	}

此时如果没有重写HashCode和equals方法会导致两个蜘蛛精,重写方法如下:

	//在Student类里面重写equals和HashCode方法
	@Override
	public boolean equals(Object o) {
	    if (this == o) return true;
	    if (o == null || getClass() != o.getClass()) return false;
	    Student student = (Student) o;
	    return age == student.age && Double.compare(height, student.height) == 0 && Objects.equals(name, student.name);
	}
	
	@Override
	public int hashCode() {
	    //姓名 年龄 身高来计算哈希值的
	    return Objects.hash(name, age, height);
	}

 2. LinkedHashMap : 有序、不重复、无索引

1.  基于哈希表,每个键值对元素多了一个双链表的机制记录元素顺序

2.  map.put("键","值") 封装成一个Entry对象;创建一个长度为16的数组,根据键计算哈希值,存入数组;

3.  基于双链表实现有序

3. TreeMap: 默认升序排序,不重复,无索引

1.  原理:基于红黑树实现

2.  支持两种方式来指定排序规则(与之前的一样,代码简略了,只写了一种方式)

  • 让类实现comparable接口,重写比较规则
  • TreeMap集合有一个有参构造器,支持创建Comparator比较器对象,以便来指定比较规则
	public static void main(String[] args) {
	    Map<Student,String> map = new TreeMap<>(new Comparator<Student>() {
	        @Override
	        public int compare(Student o1, Student o2) {
	            return Double.compare(o1.getHeight(),o2.getHeight());
	        }
	    });
	
	    //简化代码
	    //Map<Student,String> map = new TreeMap<>((o1, o2) -> Double.compare(o1.getHeight(),o2.getHeight()));
	    
	    map.put(new Student("蜘蛛精",25,168.5),"盘丝洞");
	    map.put(new Student("蜘蛛精",25,168.5),"水帘洞");
	    map.put(new Student("至尊宝",29,145.5),"水帘洞");
	    map.put(new Student("牛魔王",19,175.5),"牛头山");
	    System.out.println(map);
	}

六、集合的嵌套

1. 需求:
要求在程序中记住如下省份和其对应的城市信息,记录成功后,要求可以查询出湖北省的城市信息。
[江苏省=南京市,扬州市,苏州市,无锡市,常州市

湖北省=武汉市,孝感市,十堰市,宜昌市,鄂州市

河北省=石家庄市,唐山市,保定市,张家口市]


2. 分析
定义一个Map集合,键用表示省份名称,值表示城市名称,注意:城市会有多个。
根据“湖北省”这个键获取对应的值展示即可。 

public static void main(String[] args) {
    Map<String, List<String>> map = new HashMap<>();

    List<String> cities1 = new ArrayList<>();
    Collections.addAll(cities1,"南京市","扬州市","苏州市","无锡市","常州市");
    map.put("江苏省",cities1);

    List<String> cities2 = new ArrayList<>();
    Collections.addAll(cities2,"武汉市","孝感市","十堰市","宜昌市","鄂州市");
    map.put("湖北省",cities2);

    List<String> cities3 = new ArrayList<>();
    Collections.addAll(cities3,"石家庄市","唐山市","保定市","张家口市");
    map.put("河北省",cities3);
    System.out.println(map);

    //通过键来获取值
    List<String> cities = map.get("湖北省");
    for (String city : cities) {
        System.out.println(city);
    }

    //遍历全部信息
    map.forEach((p,c) -> {
        System.out.println(p + "--->" + c);
       /* 江苏省--->[南京市, 扬州市, 苏州市, 无锡市, 常州市]
        湖北省--->[武汉市, 孝感市, 十堰市, 宜昌市, 鄂州市]
        河北省--->[石家庄市, 唐山市, 保定市, 张家口市]*/
    });
}


记录,回顾,知新  Byebye!

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值