051、java集合_Map集合
本文最后更新于 394 天前,其中的信息可能已经过时,如有错误请发送邮件到 wuxianglongblog@163.com

java 集合_Map 集合

1.Map 介绍

现实生活中,我们常会看到这样的一种集合:IP 地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java 提供了专门的集合类用来存放这种对象关系的对象,即 java.util.Map 接口。

我们通过查看 Map 接口描述,发现 Map 接口下的集合与 Collection 接口下的集合,它们存储数据的形式不同

  • Collection 中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
  • Map 中的集合,元素是成对存在的 (理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
  • Collection 中的集合称为单列集合,Map 中的集合称为双列集合。
  • 需要注意的是,Map 中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。

1.1 Map 与 Collection 的区别

Map 集合的特点:

  • 将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值

Map 和 Collection 集合的区别:

  1. Map 集合存储元素是成对出现的,Map 的键是唯一的,值是可以重复的。
  2. Collection 集合存储元素的单独出现的,Collection 的儿子 Set 是唯一的,List 是可重复的

要点:

  1. Map 集合的数据结构针对的键有效,跟值无关
  2. Collection 集合的数据结构针对元素有效

1.3 Map 的功能

简单常用的 Map 功能:

Map 值得关注的类:

2.Map 的常用子类

通过查看 Map 接口描述,看到 Map 有多个子类,这里我们主要讲解常用的 HashMap 集合、LinkedHashMap 集合。

  • HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的 hashCode () 方法、equals () 方法。
  • LinkedHashMap<K,V>:HashMap 下有个子类 LinkedHashMap,存储数据采用的哈希表结构 + 链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的 hashCode () 方法、equals () 方法。
  • TreeMap<K,V>:TreeMap 集合和 Map 相比没有特有的功能,底层的数据结构是红黑树;可以对元素的进行排序,排序方式有两种: 自然排序比较器排序

tips:Map 接口中的集合都有两个泛型变量 <K,V>, 在使用时,要为两个泛型变量赋予数据类型。两个泛型变量 < K,V > 的数据类型可以相同,也可以不同。

3.Map 的常用方法

Map 接口中定义了很多方法,常用的如下:

  • public V put(K key, V value): 把指定的键与指定的值添加到 Map 集合中。
  • public V remove(Object key): 把指定的键所对应的键值对元素在 Map 集合中删除,返回被删除元素的值。
  • public V get(Object key) 根据指定的键,在 Map 集合中获取对应的值。
  • public Set<K> keySet(): 获取 Map 集合中所有的键,存储到 Set 集合中。
  • public Set<Map.Entry<K,V>> entrySet(): 获取到 Map 集合中所有的键值对对象的集合 (Set 集合)。
  • public boolean containKey(Object key): 判断该集合中是否有此键。

Map 接口的方法演示

public class MapDemo {
public static void main(String[] args) {
//创建 map对象
HashMap<String, String> map = new HashMap<String, String>();
//添加元素到集合
map.put("黄晓明", "杨颖");
map.put("文章", "马伊琍");
map.put("邓超", "孙俪");
System.out.println(map);
//String remove(String key)
System.out.println(map.remove("邓超"));
System.out.println(map);
// 想要查看 黄晓明的媳妇 是谁
System.out.println(map.get("黄晓明"));
System.out.println(map.get("邓超"));
}
}

tips:

使用 put 方法时,若指定的键 (key) 在集合中没有,则没有这个键对应的值,返回 null,并把指定的键值添加到集合中;

若指定的键 (key) 在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。

4.Map 的遍历

方式 1: 键找值方式

通过元素中的键,获取键所对应的值

分析步骤:

  1. 获取 Map 中所有的键,由于键是唯一的,所以返回一个 Set 集合存储所有的键。方法提示:keyset()
  2. 遍历键的 Set 集合,得到每一个键。
  3. 根据键,获取键所对应的值。方法提示:get(K key)

遍历图解:

image-20240307000322882

方式 2: 键值对方式

即通过集合中每个键值对 (Entry) 对象,获取键值对 (Entry) 对象中的键与值。

Entry 键值对对象:

我们已经知道,Map 中存放的是两种对象,一种称为 key(键),一种称为 value(值),它们在 Map 中是一一对应关系,这一对对象又称做 Map 中的一个 Entry(项)Entry 将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历 Map 集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值。

在 Map 集合中也提供了获取所有 Entry 对象的方法:

  • public Set<Map.Entry<K,V>> entrySet(): 获取到 Map 集合中所有的键值对对象的集合 (Set 集合)。

    获取了 Entry 对象,表示获取了一对键和值,那么同样 Entry 中,分别提供了获取键和获取值的方法:

  • public K getKey():获取 Entry 对象中的键。

  • public V getValue():获取 Entry 对象中的值。

操作步骤与图解:

  1. 获取 Map 集合中,所有的键值对 (Entry) 对象,以 Set 集合形式返回。方法提示:entrySet()
  2. 遍历包含键值对 (Entry) 对象的 Set 集合,得到每一个键值对 (Entry) 对象。
  3. 通过键值对 (Entry) 对象,获取 Entry 对象中的键与值。 方法提示:getkey() getValue()

遍历图解:

image-20240307000303821

tips:Map 集合不能直接使用迭代器或者 foreach 进行遍历。但是转成 Set 之后就可以使用了。

5.HashMap 存储自定义类型键值

练习:每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到 map 集合中。学生作为键,家庭住址作为值。

注意,学生姓名相同并且年龄相同视为同一名学生。

编写学生类:

public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
@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 && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}

编写测试类:

public class HashMapTest {
public static void main(String[] args) {
//1,创建HashMap集合对象。
Map<Student,String>map = new HashMap<Student,String>();
//2,添加元素。
map.put(newStudent("lisi",28), "上海");
map.put(newStudent("wangwu",22), "北京");
map.put(newStudent("zhaoliu",24), "成都");
map.put(newStudent("zhouqi",25), "广州");
map.put(newStudent("wangwu",22), "南京");
//3,取出元素。键找值方式
Set<Student>keySet = map.keySet();
for(Student key: keySet){
Stringvalue = map.get(key);
System.out.println(key.toString()+"....."+value);
}
}
}
  • 当给 HashMap 中存放自定义对象时,如果自定义对象作为 key 存在,这时要保证对象唯一,必须复写对象的 hashCode 和 equals 方法 (如果忘记,请回顾 HashSet 存放自定义对象)。
  • 如果要保证 map 中存放的 key 和取出的顺序一致,可以使用 java.util.LinkedHashMap 集合来存放。

6.LinkedHashMap

我们知道 HashMap 保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,还要速度快怎么办呢?

在 HashMap 下面有一个子类 LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。

public class LinkedHashMapDemo {
public static void main(String[] args) {
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
map.put("邓超", "孙俪");
map.put("李晨", "范冰冰");
map.put("刘德华", "朱丽倩");
Set<Entry<String, String>> entrySet = map.entrySet();
for (Entry<String, String> entry : entrySet) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}

结果:

邓超 孙俪
李晨 范冰冰
刘德华 朱丽倩

7.TreeMap 集合

1.TreeMap 介绍

TreeMap 集合和 Map 相比没有特有的功能,底层的数据结构是红黑树;可以对元素的进行排序,排序方式有两种: 自然排序比较器排序;到时使用的是哪种排序,取决于我们在创建对象的时候所使用的构造方法;

public TreeMap() //使用自然排序
public TreeMap(Comparator<? super K> comparator) //比较器排

2. 演示

自然排序

public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<Integer, String>();
map.put(1,"张三");
map.put(4,"赵六");
map.put(3,"王五");
map.put(6,"酒八");
map.put(5,"老七");
map.put(2,"李四");
System.out.println(map);
}

控制台的输出结果为:

{1=张三, 2=李四, 3=王五, 4=赵六, 5=老七, 6=酒八}

比较器排序

需求:

  1. 创建一个 TreeMap 集合,键是学生对象 (Student),值是居住地 (String)。存储多个元素,并遍历。
  2. 要求按照学生的年龄进行升序排序,如果年龄相同,比较姓名的首字母升序, 如果年龄和姓名都是相同,认为是同一个元素;

    实现:

为了保证 age 和 name 相同的对象是同一个,Student 类必须重写 hashCode 和 equals 方法

public class Student {
private int age;
private String name;
//省略get/set..
public Student() {}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@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 &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
}
public static void main(String[] args) {
TreeMap<Student, String> map = new TreeMap<Student, String>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//先按照年龄升序
int result = o1.getAge() - o2.getAge();
if (result == 0) {
//年龄相同,则按照名字的首字母升序
return o1.getName().charAt(0) - o2.getName().charAt(0);
} else {
//年龄不同,直接返回结果
return result;
}
}
});
map.put(new Student(30, "jack"), "深圳");
map.put(new Student(10, "rose"), "北京");
map.put(new Student(20, "tom"), "上海");
map.put(new Student(10, "marry"), "南京");
map.put(new Student(30, "lucy"), "广州");
System.out.println(map);
}

控制台的输出结果为:

{
Student{age=10, name='marry'}=南京,
Student{age=10, name='rose'}=北京,
Student{age=20, name='tom'}=上海,
Student{age=30, name='jack'}=深圳,
Student{age=30, name='lucy'}=广州
}

8.Map 集合练习

需求:

输入一个字符串中每个字符出现次数。

分析:

  1. 获取一个字符串对象
  2. 创建一个 Map 集合,键代表字符,值代表次数。
  3. 遍历字符串得到每个字符。
  4. 判断 Map 中是否有该键。
  5. 如果没有,第一次出现,存储次数为 1;如果有,则说明已经出现过,获取到对应的值进行 ++,再次存储。
  6. 打印最终结果

方法介绍

public boolean containKey(Object key): 判断该集合中是否有此键。

代码:

public class MapTest {
public static void main(String[] args) {
//友情提示
System.out.println("请录入一个字符串:");
String line = new Scanner(System.in).nextLine();
// 定义 每个字符出现次数的方法
findChar(line);
}
private static void findChar(String line) {
//1:创建一个集合存储字符以及其出现的次数
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
//2:遍历字符串
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
//判断 该字符 是否在键集中
if (!map.containsKey(c)) {//说明这个字符没有出现过
//那就是第一次
map.put(c, 1);
} else {
//先获取之前的次数
Integer count = map.get(c);
//count++;
//再次存入 更新
map.put(c, ++count);
}
}
System.out.println(map);
}
}

9. 补充知识点

1.JDK9 对集合添加的优化

通常,我们在代码中创建一个集合(例如,List 或 Set ),并直接用一些元素填充它。 实例化集合,几个 add 方法 调用,使得代码重复。

public class Demo01 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("abc");
list.add("def");
list.add("ghi");
System.out.println(list);
}
}

Java 9,添加了几种集合工厂方法,更方便创建少量元素的集合、map 实例。新的 List、Set、Map 的静态工厂方法可以更方便地创建集合的不可变实例。

例子:

public class HelloJDK9 {
public static void main(String[] args) {
Set<String> str1=Set.of("a","b","c");
//str1.add("c");这里编译的时候不会错,但是执行的时候会报错,因为是不可变的集合
System.out.println(str1);
Map<String,Integer> str2=Map.of("a",1,"b",2);
System.out.println(str2);
List<String> str3=List.of("a","b");
System.out.println(str3);
}
}

需要注意以下两点:

1:of () 方法只是 Map,List,Set 这三个接口的静态方法,其父类接口和子类实现并没有这类方法,比如 HashSet,ArrayList 等待;

2: 返回的集合是不可变的;

2.Debug 追踪

使用 IDEA 的断点调试功能,查看程序的运行过程

  1. 在有效代码行,点击行号右边的空白区域,设置断点,程序执行到断点将停止,我们可以手动来运行程序

  2. 点击 Debug 运行模式

  3. 程序停止在断点上不再执行,而 IDEA 最下方打开了 Debug 调试窗口

  4. Debug 调试窗口介绍

  5. 快捷键 F8,代码向下执行一行,第九行执行完毕,执行到第 10 行(第 10 行还未执行)

  6. 切换到控制台面板,控制台显示 请录入一个字符串: 并且等待键盘录入

  7. 快捷键 F8,程序继续向后执行,执行键盘录入操作,在控制台录入数据 ababcea

    回车之后效果:

    调试界面效果:

  8. 此时到达 findChar 方法,快捷键 F7,进入方法 findChar

  9. 快捷键 F8 接续执行,创建了 map 对象,变量区域显示

  10. 快捷键 F8 接续执行,进入到循环中,循环变量 i 为 0,F8 再继续执行,就获取到变量 c 赋值为字符‘a’ 字节值 97

  11. 快捷键 F8 接续执行,进入到判断语句中,因为该字符 不在 Map 集合键集中,再按 F8 执行,进入该判断中

  12. 快捷键 F8 接续执行,循环结束,进入下次循环,此时 map 中已经添加一对儿元素

  13. 快捷键 F8 接续执行,进入下次循环,再继续上面的操作,我们就可以看到代码每次是如何执行的了

  14. 如果不想继续 debug, 那么可以使用快捷键 F9, 程序正常执行到结束,程序结果在控制台显示

谨此笔记,记录过往。凭君阅览,如能收益,莫大奢望。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇