Java深入理解及巩固 Map & Set 集合
Posted 满眼*星辰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java深入理解及巩固 Map & Set 集合相关的知识,希望对你有一定的参考价值。
Map & Set
Map和Set接口关系
Map
概念
Map是一个接口类,该类没有继承自Collection,该类中存储的是<K,V>结构的键值对,并且K一定是唯一的,不能重复。
常用方法
方法 | 解释 |
---|---|
V get(Object key) | 返回 key 对应的 value |
V getOrDefault(Object key, V defaultValue) | 返回 key 对应的 value,key 不存在,返回默认值 |
V put(K key, V value) | 设置 key 对应的 value |
V remove(Object key) | 删除 key 对应的映射关系 |
Set< K> keySet() | 返回所有 key 的不重复集合 |
Collection< V> values() | 返回所有 value 的可重复集合 |
Set<Map.Entry<K, V>> entrySet() | 返回所有的 key-value 映射关系 |
boolean containsKey(Object key) | 判断是否包含 key |
boolean containsValue(Object value) | 判断是否包含 value |
代码演示
//两种创建方式
HashMap<String,Integer> hashMap = new HashMap<>();
Map<String,Integer> map = new HashMap<>();
//put
map.put("xing",1);
map.put("fei",2);
map.put("fan",3);
System.out.println(map);//{xing=1, fan=3, fei=2}
//get
int a = map.get("xing");
System.out.println(a); //1
//get
int b = map.getOrDefault("xing",88);
int c = map.getOrDefault("lala",88);
System.out.println(b);//1
System.out.println(c);//88
//remove
map.remove("fan");
System.out.println(map);//{xing=1, fei=2}
map.put("fan",3);
//keySet
Set<String> set = map.keySet();
System.out.println(set);//[xing, fan, fei]
//values
Collection<Integer> val = map.values();
System.out.println(val);//[1, 3, 2]
//entrySet
Set<Map.Entry<String,Integer>> set2 = map.entrySet();
for(Map.Entry<String,Integer> entry : set2) {
System.out.print(entry.getKey() + "=" + entry.getValue() + " ");//xing=1 fan=3 fei=2
}
System.out.println();
//containsKey
boolean d = map.containsKey("xing");
boolean e = map.containsKey("lala");
System.out.println(d);//true
System.out.println(e);//false
// containsValue
boolean f = map.containsValue(2);
boolean g = map.containsValue(5);
System.out.println(f);//true
System.out.println(g);//false
注意事项
- Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap
- Map中存放键值对的Key是唯一的,value是可以重复的
- 在Map中插入键值对时,key和value都可以为空
- Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)。
- Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)。
- Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行
重新插入。 - TreeMap和HashMap的区别
Map底层结构 | TreeMap | HashMap |
---|---|---|
底层结构 | 红黑树 | 哈希桶 |
插入/删除/查找时间复杂度 | O(logn) | O(1) |
是否有序 | 关于Key有序 | 无序 |
线程安全 | 不安全 | 不安全 |
插入/删除/查找区别 | 需要进行元素比较 | 通过哈希函数计算哈希地址 |
比较与覆写 | key必须能够比较,否则会抛出ClassCastException异常 | 自定义类型需要覆写equals和hashCode方法 |
应用场景 | 需要Key有序场景下 | Key是否有序不关心,需要更高的时间性能 |
Set
概念
Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key
常用方法
方法 | 解释 |
---|---|
boolean add(E e) | 添加元素,但重复元素不会被添加成功 |
void clear() | 清空集合 |
boolean contains(Object o) | 判断 o 是否在集合中 |
Iterator< E> iterator() | 返回迭代器 |
boolean remove(Object o) | 删除集合中的 o |
int size() | 返回set中元素的个数 |
boolean isEmpty() | 检测set是否为空,空返回true,否则返回false |
Object[] toArray() | 将set中的元素转换为数组返回 |
boolean containsAll(Collection<?> c) | 集合c中的元素是否在set中全部存在,是返回true,否则返回false |
boolean addAll(Collection<? extendsE> c) | 将集合c中的元素添加到set中,可以达到去重的效果 |
注意事项
- Set是继承自Collection的一个接口类
- Set中只存储了key,并且要求key一定要唯一
- Set的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的
- Set最大的功能就是对集合中的元素进行去重
- 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础
上维护了一个双向链表来记录元素的插入次序。 - Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入
- Set中可以插入null的key。
- TreeSet和HashSet的区别
Set底层结构 | TreeSet | HashSet |
---|---|---|
底层结构 | 红黑树 | 哈希桶 |
插入/删除/查找时间复杂度 | O(logn) | O(1) |
是否有序 | 关于Key有序 | 不一定有序 |
线程安全 | 不安全 | 不安全 |
插入/删除/查找区别 | 按照红黑树的特性来进行插入和删除 | 1. 先计算key哈希地址 2. 然后进行插入和删除 |
比较与覆写 | key必须能够比较,否则会抛出ClassCastException异常 | 自定义类型需要覆写equals和hashCode方法 |
应用场景 | 需要Key有序场景下 | Key是否有序不关心,需要更高的时间性能 |
代码演示
Set<Integer> set = new HashSet<>();
//add
set.add(1);
set.add(2);
set.add(3);
System.out.println(set);//[1, 2, 3]
//clear
set.clear();
System.out.println(set);//[]
set.add(1);
set.add(2);
set.add(3);
//contains
boolean a = set.contains(2);
boolean b = set.contains(4);
System.out.println(a);//true
System.out.println(b);//false
//iterator
Iterator<Integer> it = set.iterator();
while(it.hasNext()){
System.out.print(it.next() + " ");//1 2 3
}
System.out.println();
//remove
System.out.println(set.remove(1));//true
System.out.println(set.remove(4));//false
set.add(1);
//size
System.out.println(set.size());//3
//isEmpty
System.out.println(set.isEmpty());//false
//toArray
Object[] arr = set.toArray();
System.out.println(Arrays.toString(arr));//[1, 2, 3]
//containsAll
Set<Integer> set2 = new HashSet<>();
set.add(3);
set2.add(4);
System.out.println(set.containsAll(set2));//false
//addAll
set.addAll(set2);
System.out.println(set);//[1, 2, 3, 4]
巩固练习
(一)找出第一个重复的数据
list当中存放的数据为10万个,找出第一个重复的数据
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 10_0000; i++) {
list.add(random.nextInt(10_0000));
}
System.out.println(list);
Set<Integer> set = new HashSet<>();
for (int i = 0; i < list.size(); i++) {
if(set.contains(list.get(i))) {
System.out.println(list.get(i));
break;
}else {
set.add(list.get(i));
}
}
}
(二)重复的数字全部都去重
list当中有10万个数据,把重复的数字全部都去重
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 10_0000; i++) {
list.add(random.nextInt(10_0000));
}
System.out.println(list);
Set<Integer> set = new HashSet<>();
//方式一:
for (int i = 0; i < list.size(); i++) {
set.add(list.get(i));
}
System.out.println(set);
//方式二:
set.addAll(list);
System.out.println(set);
}
(三)统计重复数字及出现的次数
将10万个数据,统计重复数字及出现的次数
public static void main(String[] args) {
Random random = new Random();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10_0000; i++) {
list.add(random.nextInt(10_0000));
}
Map<Integer,Integer> map = new HashMap<>();
for (int i = 0; i < 10_0000; i++) {
int tmp = list.get(i);
if(map.containsKey(tmp)) {
map.put(tmp,map.get(tmp)+1);
}else {
map.put(tmp,1);
}
}
for (Map.Entry<Integer,Integer> entry : map.entrySet()) {
if(entry.getValue() > 1) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}
(四)只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1] 输出: 1 示例 2:
输入: [4,1,2,1,2] 输出: 4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/single-number
class Solution {
public int singleNumber(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int i = 0; i < nums.length; i++) {
if(set.contains(nums[i])) {
set.remove(nums[i]);
}else {
set.add(nums[i]);
}
}
for(int i : set) {
return i;
}
return -1;
}
}
class Solution {
public int singleNumber(int[] nums) {
int res = nums[0];
for(int i = 1; i < nums.length; i++) {
res = res ^ nums[i];
}
return res;
}
}
(五)复制带随机指针的链表
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和
random
指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有
x.random --> y 。返回复制链表的头节点。
用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。 random_index:随机指针指向的节点索引(范围从 0 到
n-1);如果不指向任何节点,则为 null 。 你的代码 只 接受原链表的头节点 head 作为传入参数。示例 1:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]] 示例 2:输入:head = [[1,1],[2,1]] 输出:[[1,1],[2,1]] 示例 3:
输入:head = [[3,null],[3,0],[3,null]] 输出:[[3,null],[3,0],[3,null]] 示例 4:
输入:head = [] 输出:[] 解释:给定的链表为空(空指针),因此返回 null。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/copy-list-with-random-pointer
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
Map<Node,Node> map = new HashMap<>();
Node cur = head;
while(cur != null) {
Node tmp = new Node(cur.val);
map.put(cur,tmp);
cur = cur.next;
}
cur = head;
while(cur != null) {
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return map.get(head);
}
}
(六)宝石与石头
给定字符串J 代表石头中宝石的类型,和字符串 S代表你拥有的石头。 S
中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。J 中的字母不重复,J 和 S中的所有字符都是字母。字母区分大小写,因此"a"和"A"是不同类型的石头。
示例 1:
输入: J = “aA”, S = “aAAbbbb” 输出: 3 示例 2:
输入: J = “z”, S = “ZZ” 输出: 0
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jewels-and-stones
class Solution {
public int numJewelsInStones(String jewels, String stones) {
Set<Character> set = new HashSet<>();
for(int i = 0; i < jewels.length(); i++) {
set.add(jewels.charAt(i));
}
int count = 0;
for(int i = 0; i < stones.length(); i++) {
if(set.contains(stones.charAt(i))) {
count++;
}
}
return count;
}
}
(七)旧键盘
旧键盘上
以上是关于Java深入理解及巩固 Map & Set 集合的主要内容,如果未能解决你的问题,请参考以下文章
JAVA里面set,map,list的区别是啥?(尽量详细)
JAVA零基础小白学习免费教程day14-Set&HashMap