共计 6037 个字符,预计需要花费 16 分钟才能阅读完成。
第三阶段 JAVA 常见对象的学习
集合框架——Map 集合
在实际需求中,我们常常会遇到这样的问题,在诸多的数据中,通过其编号来寻找某一些信息,从而进行查看或者修改,例如通过学号查询学生信息。今天我们所介绍的 Map 集合就可以很好的帮助我们实现这种需求
(一) 概述及功能
(1) 概述
Map 是一种存储元素对的集合(元素对分别称作 键 和 值 也称键值对)它将键映射到值的对象。一个映射不能包含重复的键,并且每个键最 多只能映射到一个值。
怎么理解呢?
键 (key):就是你存的值的编号 值 (value):就是你要存放的数据
你可以近似的将键理解为下标,值依据键而存储,每个键都有其对应值。这两者是 1、1 对应的
但在之前下标是整数,但是 Map 中键可以使任意类型的对象。
Map 集合和 Collection 集合的区别?
- Map 集合存储元素是成对出现的,Map 集合的键是唯一的,值是可重复的
- Collection 集合存储元素是单独出现的,Collection 的子类 Set 是唯一的,List 是可重复的。
- Map 集合的数据结构值针对键有效,跟值无关,Collection 集合的数据结构是针对元素有效
(2) 功能
A:添加功能
// 添加元素
V put(K key,V value)
// 如果键是第一次存储,就直接存储元素,返回 null
// 如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
B:删除功能
// 移除所有的键值对元素
void clear()
// 根据键删除键值对元素,并把值返回
V remove(Object key)
C:判断功能
// 判断集合是否包含指定的键
boolean containsKey(Object key)
// 判断集合是否包含指定的值
boolean containsValue(Object value)
// 判断集合是否为空
boolean isEmpty()
D:获取功能
// 将 map 集合中的键和值映射关系打包为一个对象
Set<Map.Entry<K,V>> entrySet()
// 根据键获取值
V get(Object key)
// 获取集合中所有键的集合
Set<K> keySet()
// 获取集合中所有值的集合
Collection<V> values()
E:长度功能
// 返回集合中的键值对的对数
int size()
(二) Map 集合的遍历
package cn.bwh_01_iterator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapDemo {public static void main(String[] args) {Map<String, String> hm = new HashMap<String, String>();
hm.put("bwh002", "i");
hm.put("bwh001", "love");
hm.put("bwh003", "you");
// 方式一 键找值
Set<String> set = hm.keySet();
// 迭代器遍历
Iterator<String> it = set.iterator();
while (it.hasNext()) {String key = it.next();
String value = hm.get(key);
System.out.println(key + "---" + value);
}
// 增强 for 遍历
for (String key : set) {String value = hm.get(key);
System.out.println(key + "---" + value);
}
// 方式二 键值对对象找键和值(推荐)Set<Map.Entry<String, String>> set2 = hm.entrySet();
// 迭代器遍历
Iterator<Map.Entry<String, String>> it2 = set2.iterator();
while (it2.hasNext()) {
// 返回的是封装了 key 和 value 对象的 Map.Entry 对象
Map.Entry<String, String> entry = it2.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "---" + value);
}
// 增强 for 遍历
for (Map.Entry<String, String> me : set2) {String key = me.getKey();
String value = me.getValue();
System.out.println(key + "---" + value);
}
}
}
(三) Map 及子类总结
Map(双列集合)
- Map 集合的数据结构仅仅针对键有效,与值无关。
- 存储的是键值对形式的元素,键唯一,值可重复
HashMap
- 底层数据结构是哈希表,线程不安全,效率高
- 哈希表依赖两个方法:hashCod()和 equals()
-
执行顺序:
-
首先判断 hashCode()值是否相同
-
是:继续执行 equals(), 看其返回值
- 是 true:说明元素重复,不添加
- 是 false:就直接添加到集合
- 否:就直接添加到集合
-
-
- 最终:
- 自动生成 hashCode()和 equals()即可
LinkeHashMap
- 底层数据结构是由链表和哈希表组成
- 由链表保证元素有序
- 由哈希表保证元素唯一
Hashtable
- 底层数据结构是哈希表
- 哈希表依赖两个 …… 自动生成 hashCode()和 equals()即可
TreeMap
- 底层数据结构是红黑树(是一种自平衡的二叉树)
如何保证元素唯一性呢?
- 根据比较的返回值是否是 0 来决定
如何保证两种元素的排序呢?
-
自然排序(元素具备比较性)
- 让元素所属的类实现 comparable 接口
-
比较器排序(集合具备比较性)
- 让集合接收一个 comparator 的实现类对象
可以多层嵌套
HashMap 集合嵌套 HashMap
HashMap 集合嵌套 ArrayList
ArrayList 集合嵌套 HashMap
HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>
1:Hashtable 和 HashMap 的区别?
**Hashtable**: 线程安全,效率低。不允许 null 键和 null 值
**HashMap**: 线程不安全,效率高。允许 null 键和 null 值
(其实 HashMap 就是用来替代 Hashtable 的,就像 ArrayList 替代 vector 一样)
2:List,Set,Map 等接口是否都继承子 Map 接口?
List,Set 不是继承自 Map 接口,它们继承自 Collection 接口
Map 接口本身就是一个顶层接口
需要排序:TreeMap
不需要排序:HashMap
不知道具体需求:HashMap
(四) 经典案例
(1) 统计字符串中字符出现的次数
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeMap;
/*
* 案例(统计字符串中字符出现的次数)* 需求:* 获取一个字符串出现的次数
* 分析:* A:定义一个字符串(或者键盘录入)* B: 定义一个 TreeMap 集合
* 键:Character
* 值:Integer
* C:把字符串转换为字符数组
* D: 遍历字符数组,得到每一个字符
* E: 拿刚才得到的字符作为键去集合中找,看返回值
* 是 null:说明该键不存在, 就把该字符串作为键,1 作为值去存储
* 不是 null:说明该键存在,就把值加 1 然后重写存储该键和值
* F: 定义字符串缓冲区变量
* G:遍历集合,得到该建和值,按照要求拼接
* H:最后把字符串缓冲区转换为字符串输出
*/
public class CountDemo {public static void main(String[] args) {Scanner sc = new Scanner(System.in);
System.out.println("请输入需要统计的数据");
String line = sc.nextLine();
Map<Character, Integer> tm = new TreeMap<Character, Integer>();
char[] chs = line.toCharArray();
for (char ch : chs) {Integer i = tm.get(ch);
if (i == null) {tm.put(ch, 1);
} else {
i++;
tm.put(ch, i);
}
}
StringBuilder s = new StringBuilder();
Set<Character> set = tm.keySet();
for (Character key : set) {Integer value = tm.get(key);
s.append(key).append("(").append(value).append(")" + " ");
}
String result = s.toString();
System.out.println("result:" + result);
}
}
// 运行结果
请输入需要统计的数据
HelloWorld
result: H(1) W(1) d(1) e(1) l(3) o(2) r(1)
(2) 模拟斗地主案例
在讲解这个案例之前,我们先来了解一个我们下面案例中所需要知道的知识点
Collections 工具类
Collections: 是针对集合进行操作的工具类,都是静态方法。
面试题:
Collection 和 Collections 的区别?
Collection: 是单列集合的顶层接口,有子接口 List 和 Set。(Map 是双列的)
Collections: 是针对集合操作的工具类,有对集合进行排序和二分查找的方法
Collections 的静态方法
// 排序 默认情况下是自然顺序。public static <T> void sort(List<T> list)
// 二分查找
public static <T> int binarySearch(List<?> list,T key)
// 最大值
public static <T> T max(Collection<?> coll)
// 反转(逆序排序)
public static void reverse(List<?> list)
// 随机置换(犹如洗牌,每次运行结果不一样)
public static void shuffle(List<?> list)
如果同时有自然排序和比较器排序,以比较器排序为主 (也就是说,当同时实现了 Student 类的自然排序(implements Comparable<Student>) 以及比较器排序的话(new Comparator<Student>()),比较器排序会覆盖自然排序)
// 斗地主案例代码
package cn.bwh_03_PokerGame;
import java.util.*;
public class PokerGame {public static void main(String[] args) {HashMap<Integer, String> hm = new HashMap<Integer, String>();
ArrayList<Integer> array = new ArrayList<Integer>();
String[] colors = {"♥", "♠", "♣", "♦"};
String[] numbers = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
int index = 0;
for (String number : numbers) {for (String color : colors) {String poker = color.concat(number);
hm.put(index, poker);
array.add(index);
index++;
}
}
hm.put(index, "小王");
array.add(index);
index++;
hm.put(index, "大王");
array.add(index);
index++;
// 洗牌
Collections.shuffle(array);
// 发牌(发的是编号,为了保证编号是排序的,使用 TreeSet 接收)TreeSet<Integer> player1 = new TreeSet<Integer>();
TreeSet<Integer> player2 = new TreeSet<Integer>();
TreeSet<Integer> player3 = new TreeSet<Integer>();
TreeSet<Integer> handcards = new TreeSet<Integer>();
for (int x = 0; x < array.size(); x++) {if (x >= array.size() - 3) {handcards.add(array.get(x));
} else if (x % 3 == 0) {player1.add(array.get(x));
} else if (x % 3 == 1) {player2.add(array.get(x));
} else if (x % 3 == 2) {player3.add(array.get(x));
}
}
System.out.println("--------------------- 欢乐斗地主 ----------------------");
// 看牌(遍历 TreeSet 集合,获取编号,到 HashMap 集合找对应的牌)
lookpocker("player1", player1, hm);
lookpocker("player2", player2, hm);
lookpocker("player3", player3, hm);
lookpocker("预留", handcards, hm);
}
// 看牌功能实现
public static void lookpocker(String name, TreeSet<Integer> ts, HashMap<Integer, String> hm) {System.out.println(name + "的牌是:");
for (Integer key : ts) {String value = hm.get(key);
System.out.print(value + " ");
}
System.out.println();}
}
结尾:
如果内容中有什么不足,或者错误的地方,欢迎大家给我留言提出意见, 蟹蟹大家!^_^
如果能帮到你的话,那就来关注我吧!(系列文章均会在公众号第一时间更新)
在这里的我们素不相识,却都在为了自己的梦而努力 ❤
一个坚持推送原创 Java 技术的公众号:理想二旬不止