当前位置: 首页 > news >正文

Java 黑马程序员学习笔记(进阶篇15)

1. 双列集合

(1) 双列集合的概念

① 双列集合指的是 java.util.Map 体系,存储的是  键值对(key-value) 形式的数据。

② 特点:

  • 键(key)唯一,不能重复(重复会覆盖原值)。
  • 值(value)可以重复
  • 一个键对应一个值
  • 允许有 null 键(只能有一个),和多个 null 值

2. Map 集合常用的 API

方法作用
V put(K key, V value)添加键值对(键重复则覆盖)
V remove(Object key)根据键删除对应键值对
boolean containsKey(Object key)判断是否包含某个键
boolean containsValue(Object value)判断是否包含某个值
void clear()清空所有键值对
int size()

获取键值对数量

① put 方法的作用
  • 将指定的 key 与 value 关联起来
  • 如果 Map 中已经存在该 key,则覆盖旧值,并返回旧的 value
  • 如果不存在该 key,则新增一个键值对,返回 null
② remove 方法的作用
  • 根据 key 删除 Map 中的键值对
  • 如果 key 存在,返回被删除的 value
  • 如果 key 不存在,返回 null

3. Map 的遍历方式

① 遍历 keySet(最常见的 “键找值” 写法)
Map<String, String> map = new HashMap<>();
map.put("name", "Alice");
map.put("age", "20");// 1. 获取所有 key
Set<String> keySet = map.keySet();// 2. 遍历 key,通过 key 找 value
for (String key : keySet) {String value = map.get(key);System.out.println(key + " = " + value);
}

特点:

  • 优点:代码直观,适合只需要 key 时
  • 缺点:通过 key 再 get (value) 会多一次哈希查找(性能略低于 entrySet)
② 遍历 EntrySet
方法名说明
Set<Map.Entry<K,V>> entrySet()获取所有键值对对象的集合
public class MapDemo03 {public static void main(String[] args) {//Map集合的第二种遍历方式//1.创建Map集合的对象Map<String, String> map = new HashMap<>();//2.添加元素//键:人物的外号//值:人物的名字map.put("标枪选手", "马超");map.put("人物挂件", "明世隐");map.put("御龙骑士", "尹志平");//3.Map集合的第二种遍历方式//通过键值对对象进行遍历//3.1 通过一个方法获取所有的键值对对象,返回一个Set集合Set<Map.Entry<String, String>> entries = map.entrySet();//3.2 遍历entries这个集合,去得到里面的每一个键值对对象for (Map.Entry<String, String> entry : entries) {//entry  --->  "御龙骑士","尹志平"//3.3 利用entry调用get方法获取键和值String key = entry.getKey();String value = entry.getValue();System.out.println(key + "=" + value);}}
}
③ Lambda 表达式
(1) 概述:

得益于JDK8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式。

(2) 基本用例:
public class MapDemo03 {public static void main(String[] args) {//Map集合的第三种遍历方式//1.创建Map集合的对象Map<String,String> map = new HashMap<>();//2.添加元素//键:人物的名字//值:名人名言map.put("鲁迅","这句话是我说的");map.put("曹操","不可能绝对不可能");map.put("刘备","接着奏乐接着舞");map.put("柯镇恶","看我眼色行事");//3.利用lambda表达式进行遍历//底层://forEach其实就是利用第二种方式进行遍历,依次得到每一个键和值//再调用accept方法map.forEach(new BiConsumer<String, String>() {@Overridepublic void accept(String key, String value) {System.out.println(key + "=" + value);}});System.out.println("-----------------------------------");map.forEach((String key, String value)->{System.out.println(key + "=" + value);});System.out.println("-----------------------------------");map.forEach((key, value)-> System.out.println(key + "=" + value));}
}

4. HashMap 

① HashMap 基本概念

HashMap 是 Java 集合框架中实现 Map 接口的类,专门用于存储键值对(key-value)

② 核心特点
  • 无序性:键值对的存储顺序与取出顺序可能不一致(因为基于哈希表存储,按哈希值分布)。
  • null 支持:允许键或值为 null(注意:键只能有一个 null,因为 “键唯一”;值可以多个 null)。
  • 键的唯一性:若插入相同键,后插入的 value 会覆盖先插入的 value
  • 线程不安全:多线程同时操作时,可能出现数据不一致问题。若需线程安全,可考虑 ConcurrentHashMap 或 Collections.synchronizedMap()
  • 高效性:基于哈希表实现,插入、查询(get/put)的时间复杂度为 O (1)(理想情况:哈希分布均匀,无大量冲突)。
③ 底层数据结构(JDK8 及以后)

HashMap 底层由 “数组 + 链表 + 红黑树” 组成(JDK7 及之前是 “数组 + 链表”),结构优化的核心是 “链表转红黑树”,用于解决哈希冲突后的性能问题:

  • 数组:作为哈希表的 “桶(bucket)”,每个元素是一个 “节点(Node)”。
  • 链表:当多个键的哈希值冲突(计算后落到同一个数组索引),会以链表形式存储这些冲突的节点。
  • 红黑树:当链表的长度超过 8,且数组的长度 ≥ 64时,链表会自动转换为红黑树(红黑树是 “平衡二叉树”,查询效率从链表的 O (n) 优化到 O (logn)),大幅提升查询性能。
④ “键的唯一性” 如何保证?

HashMap 依赖 hashCode() 方法和 equals() 方法保证 “键唯一”:

  • 当存储自定义对象作为键时,必须重写 hashCode() 和 equals() 方法。否则会使用 Object 类的默认方法(基于 “对象地址” 判断相等性),导致 “逻辑相同的对象” 被误认为 “不同键”。
  • 值是自定义对象不需要重写这两个方法(因为 “值” 不参与 “键的唯一性” 判断)。
④ 综合练习
(1) 题目 1:学生信息与籍贯存储

需求:

  • 创建一个 Student 类,包含姓名(name)和年龄(age)两个属性,并提供构造方法和 toString() 方法。
  • 在 main 方法中创建一个 HashMap<Student, String>,用来存储学生对象和对应的籍贯。
  • 向 HashMap 中添加以下数据:
    • Student("zhangsan", 23) -> "江苏"
    • Student("lisi", 24) -> "浙江"
    • Student("wangwu", 25) -> "福建"
  • 通过 keySet() 方法获取所有学生对象(键),遍历并输出 “学生信息 = 籍贯”。

提示:

  • keySet() 方法会返回一个 Set<K>,其中 K 是 Map 的键类型,在这里是 Student
  • 遍历 Set 时,通过 get(key) 方法获取对应的值(籍贯)。

测试类:

package demo1;import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;public class test1 {public static void main(String[] args) {HashMap<Student, String> hm = new HashMap<>();Student s1 = new Student("zhangsan",23);Student s2 = new Student("lisi",24);Student s3 = new Student("wangwu",25);hm.put(s1,"江苏");hm.put(s2,"浙江");hm.put(s3,"福建");Set<Student> keys = hm.keySet();   //不太理解,这个Set是从哪里来的for (Student key : keys) {String value = hm.get(key);System.out.println(key + "=" + value);}System.out.println("-------------------");Set<Map.Entry<Student, String>> entries = hm.entrySet();for (Map.Entry<Student, String> entry : entries) {Student key = entry.getKey();String value = entry.getValue();System.out.println(key + "=" + value);}System.out.println("----------------------");hm.forEach((student, s)->System.out.println(student + "=" + s));}
}

JavaBean:

package demo1;import java.util.Objects;public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}@Overridepublic 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 && Objects.equals(name, student.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}
关键逻辑 1:这个 Set 是从哪里来的?
Set<Student> keys = hm.keySet();
  • 这里的 keys 就是 hm 中所有 Student 对象的集合(s1, s2, s3)。
  • 类型是 Set<Student>,因为:
    • 元素是 Student 类型(Map 的 key 类型)
    • 元素唯一(Set 特性)
关键逻辑 2:遍历过程
for (Student key : keys) {String value = hm.get(key); // 通过 key 取 valueSystem.out.println(key + "=" + value);
}
  • 先拿到所有 key (keys)
  • 循环每个 key
  • 用 hm.get(key) 取出对应的值(籍贯)
(2) 题目 2:随机字母频率统计

需求:

  • 定义一个字符串数组 {"A","B","C","D"}
  • 使用 Random 从数组中随机取字母,共取 80 次,存到 ArrayList 中。
  • 使用 HashMap<String,Integer> 统计每个字母出现的次数。
  • 遍历 HashMap,找出出现次数最多的数字(最大值)。
  • 再次遍历 HashMap,输出所有出现次数等于最大值的字母。
  • 打印格式如下:
{A=20, B=22, C=18, D=20}
22
B

提示:

  • containsKey(key) 方法可以判断 Map 中是否包含某个键
  • get(key) 方法可以获取键对应的值
  • entrySet() 方法返回键值对的 Set 集合,方便同时遍历键和值
package demo1;import java.util.*;public class test2 {public static void main(String[] args) {String[] arr = {"A","B","C","D"};ArrayList<String> list = new ArrayList<String>();Random r = new Random();for (int i = 0; i < 80; i++) {int index = r.nextInt(arr.length);list.add(arr[index]);}HashMap<String,Integer> hm = new HashMap<>();for (String name : list) {if (hm.containsKey(name)) {   //不太理解int count = hm.get(name);   //不太理解count++;hm.put(name, count);} else{hm.put(name,1);}}System.out.println(hm);int max = 0;Set<Map.Entry<String, Integer>> entries = hm.entrySet();for (Map.Entry<String, Integer> entry : entries) {int count = entry.getValue();if (count > max) {max = count;}}System.out.println(max);for (Map.Entry<String, Integer> entry : entries) {int count = entry.getValue();if (count == max) {System.out.println(entry.getKey());}}}
}
关键逻辑 1:if (hm.containsKey(name))
  • containsKey() 是 Map 接口 的方法
  • 作用:判断 hm 中是否已经存在某个 key(这里 key 是 name,也就是当前遍历到的字母)
  • 返回值:
    • true:已经存在这个字母
    • false:还没有这个字母

例子:

  • 如果 name = "A",而 hm 中已经有 "A" 这个 key,就返回 true
  • 如果是第一次遇到 "A",就返回 false
关键逻辑 2:int count = hm.get(name)
  • get(name) 是 Map 接口 的方法
  • 作用:根据 key 取出对应的 value
  • 这里的 value 是一个 Integer(即这个字母已经出现的次数)
http://www.dtcms.com/a/427351.html

相关文章:

  • 【开题答辩过程】以《基于SpringBoot+Vue+uni-app的智慧校园服务系统的设计与实现》为例,不会开题答辩的可以进来看看
  • 做二手电脑的网站宣城网站建设 有限公司
  • 没有服务器 怎么做网站建设企业高端网站
  • 极简时钟APP(手机全能计时工具) 极简版
  • 华为光模块命名规则
  • 做企业网站用什么cms好易语言怎么做网页网站
  • 域名做网站北京 网站 公司
  • 深入浅出 Redis:从核心原理到运维实战指南一
  • 自定义含工具包`Ubuntu22.04.5.iso`镜像
  • Day 29 - 密码管理器开发 - Python学习笔记
  • Docker镜像结构全解析
  • ubuntu 22.04安装CUDA 13.0
  • 鸿蒙NEXT Wi-Fi扫描开发指南:从基础到实战
  • wordpress做的学校网站北京网页设计公司
  • 精读 C++20 设计模式:行为型设计模式——观察者模式
  • 广州营销型网站建设旅游网站内容规划特点
  • 【mdBook】6 在持续集成中运行 mdbook
  • Jenkins安装并与GitLab集成,实现dev、qa、uat、prod多分支持续集成的详细步骤
  • k8s部署前后分离架构微服务——跨域和缓存问题
  • 临沂网站建设推荐wordpress 评论倒序
  • MyBatis Plus注解 @TableField
  • 石家庄建设工程招标办网站成都网站推广公司
  • 【C#避坑实战系列文章16】性能优化(CPU / 内存占用过高问题解决)
  • JavaScript性能优化实战:从指标到落地的全链路方案
  • 上海网站优化加盟网站建设的公司开发
  • 网站怎么发布到服务器青岛不错的网站公司
  • linux0.11学习之启动主线要点(一)
  • Invoke-customs are only supported starting with Android O (--min-api 26)
  • 安卓基础组件014--button圆角 背景色 边框
  • 【Android】浅谈kotlin协程应用