目录
Map和Set详解
1.二叉搜索树
2.Map常见方法
3.Set常见方法
4.哈希表
Map和Set详解
Map:一种键值对结构,hashMap中键和值均可以为空,hashTable中则不可以存放null值
Set:一种集合,不能存放重复元素,可以理解为与map中的键的集合。
Map 和 set 是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关 。在Java中Map和Set最常见到下面四个实现类,HashMap/TreeMap/HashSet/TreeSet,他们分别与两种数据结构相关,二叉搜索树和哈希表,下面的文章中我会详解这两种数据结构,以及Java是怎样对这两种数据结构进行实现的。
1.二叉搜索树
- 若它的左子树不为空,则左子树上所有节点都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
如下图所示:
public class BinarySearchTree {
public static class Node {
int key;
Node left;
Node right;
public Node(int key) {
this.key = key;
}
}
private Node root = null;
/**
* 在搜索树中查找 key,如果找到,返回 key 所在的结点,否则返回 null
* @param key
* @return
*/
public Node search(int key) {
Node cur = root;
while (cur != null) {
if (key == cur.key) {
return cur;
} else if (key < cur.key) {
cur = cur.left;
} else {
cur = cur.right;
}
}
return null;
}
/**
* 插入
* @param key
* @return true 表示插入成功, false 表示插入失败
*/
public boolean insert(int key) {
if (root == null) {
root = new Node(key);
return true;
}
Node cur = root;
Node parent = null;
while (cur != null) {
if (key == cur.key) {
return false;
} else if (key < cur.key) {
parent = cur;
cur = cur.left;
} else {
parent = cur;
cur = cur.right;
}
}
Node node = new Node(key);
if (key < parent.key) {
parent.left = node;
} else {
parent.right = node;
}
return true;
}
/**
* 删除成功返回 true,失败返回 false
* @param key
* @return
*/
public boolean remove(int key) {
return delete(root,key);
}
private boolean delete(Node root,int key){
/*
根据cur的孩子是否存在分四种情况
1. cur左右孩子均不存在
2. cur只有左孩子
3. cur只有右孩子
4. cur左右孩子均存在
看起来有四种情况,实际情况1可以与情况2或者3进行合并,只需要处理是那种情况即可
除了情况4之外,其他情况可以直接删除
情况4不能直接删除,需要在其子树中找一个替代节点进行删除
*/
Node cur = root;
Node parent = null;
while (cur != null) {
if (key == cur.key) {
break;
} else if (key < cur.key) {
parent = cur;
cur = cur.left;
} else {
parent = cur;
cur = cur.right;
}
}
// 该元素不在二叉搜索树中
if(null == cur){
return false;
}
//由于二叉查找树的性质,如果将当前节点替换为左子树中最大的或者右子树中最小的一定不会破坏二叉查找树的结构。
if((cur.left!=null&&cur.right==null)||(cur.left==null&&cur.right==null)){
cur=cur.left;
}else if(cur.left==null&&cur.right!=null){
cur=cur.right;
}else{
int val=SearchleftMax(cur.left);
cur.key=val;
delete(cur.left,val);
}
return true;
}
private int SearchleftMax(Node left) {
if(left==null){
return 0;
}
if(left.right==null){
return left.key;
}
return SearchleftMax(left.right);
}
}
性能分析:
- 最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log2N
- 最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2
可以看到如果退化为单支树,性能就会下降非常多。为了保证二叉搜索树的效率,大佬们还发明AVL树和红黑树,来限制树的高度尽量趋近于完全二叉树。
2.Map常见方法
在Java中Map和Set是两个接口,我们可以利用他们选择任意一个实现类。
map的常见方法:
方法 | 解释 |
---|---|
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
|
- Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap
- Map中存放键值对的Key是唯一的,value是可以重复的
- 在TreeMap中插入键值对时,key不能为空,否则就会抛NullPointerException异常,value可以为空。但是HashMap的key和value都可以为空。
- Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)。
- Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)。
- Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行重新插入。
3.Set常见方法
方法 | 解释 |
---|---|
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<? extends
E> c)
|
将集合
c
中的元素添加到
set
中,可以达到去重的效果
|
- Set是继承自Collection的一个接口类
- Set中只存储了key,并且要求key一定要唯一
- TreeSet的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的
- Set最大的功能就是对集合中的元素进行去重
- 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序。
- Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入
- TreeSet中不能插入null的key,HashSet可以。
4.哈希表
- 插入元素 根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
- 搜索元素 对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功
冲突的诞生:
关于如何解决哈希冲突我将会在下篇文章进行分享,欢迎大家关注我的博客在文章发布的第一时间来捧场呀!!!
主页已更新完Java基础内容,数据结构基础,
正在更新算法篇,数据库篇,
未来会更新Java项目,SpringBoot,Redis以及各种Java路线会用到的技术。
求点赞!求收藏!求评论!求关注!
谢谢大家!!!!!!!!!