目录
1.什么是Map?
Map
是一个接口类,该类没有继承自
Collection
,该类中存储的是
<K,V>
结构的键值对,并且
K
一定是唯一的,不
能重复
。
1.2关于Map.Entry<K, V>的说明
Map.Entry<K, V>
是
Map
内部实现的用来存放
<key, value>
键值对映射关系的内部类
,该内部类中主要提供了 <key, value>的获取,
value
的设置以及
Key
的比较方式。
1.3Map 的常用方法说明
注意:
1.
Map
是一个接口,不能直接实例化对象
,如果
要实例化对象只能实例化其实现类
TreeMap
或者
HashMap
2.
Map
中存放键值对的
Key
是唯一的,
value
是可以重复的
3.
在
TreeMap
中插入键值对时,
key
不能为空,否则就会抛
NullPointerException
异常
,
value
可以为空。但 是HashMap
的
key
和
value
都可以为空。
4.
Map
中的
Key
可以全部分离出来,存储到
Set
中
来进行访问
(
因为
Key
不能重复
)
。
5.
Map
中的
value
可以全部分离出来,存储在
Collection
的任何一个子集合中
(value
可能有重复
)
。
6. Map
中键值对的
Key
不能直接修改,
value
可以修改,如果要修改
key
,只能先将该
key
删除掉,然后再来进行 重新插入。
7. TreeMap
和
HashMap
的区别.
![](https://img-blog.csdnimg.cn/6aa775e699bf4f53b09adc127acb8e29.png)
1.4TreeMap的使用案例
import java.util.TreeMap;
import java.util.Map;
public static void TestMap(){
Map<String, String> m = new TreeMap<>();
// put(key, value):插入key-value的键值对
// 如果key不存在,会将key-value的键值对插入到map中,返回null
m.put("林冲", "豹子头");
m.put("鲁智深", "花和尚");
m.put("武松", "行者");
m.put("宋江", "及时雨");
String str = m.put("李逵", "黑旋风");
System.out.println(m.size());
System.out.println(m);
// put(key,value): 注意key不能为空,但是value可以为空
// key如果为空,会抛出空指针异常
//m.put(null, "花名");
str = m.put("无名", null);
System.out.println(m.size());
// put(key, value):
// 如果key存在,会使用value替换原来key所对应的value,返回旧value
str = m.put("李逵", "铁牛");
// get(key): 返回key所对应的value
// 如果key存在,返回key所对应的value
// 如果key不存在,返回null
System.out.println(m.get("鲁智深"));
System.out.println(m.get("史进"));
//GetOrDefault(): 如果key存在,返回与key所对应的value,如果key不存在,返回一个默认值
System.out.println(m.getOrDefault("李逵", "铁牛"));
System.out.println(m.getOrDefault("史进", "九纹龙"));
System.out.println(m.size());
//containKey(key):检测key是否包含在Map中,时间复杂度:O(logN)
// 按照红黑树的性质来进行查找
// 找到返回true,否则返回false
System.out.println(m.containsKey("林冲"));
System.out.println(m.containsKey("史进"));
// containValue(value): 检测value是否包含在Map中,时间复杂度: O(N)
// 找到返回true,否则返回false
System.out.println(m.containsValue("豹子头"));
System.out.println(m.containsValue("九纹龙"));
// 打印所有的key
// keySet是将map中的key防止在Set中返回的
for(String s : m.keySet()){
System.out.print(s + " ");
}
System.out.println();
// 打印所有的value
// values()是将map中的value放在collect的一个集合中返回的
for(String s : m.values()){
System.out.print(s + " ");
}
System.out.println();
// 打印所有的键值对
// entrySet(): 将Map中的键值对放在Set中返回了
for(Map.Entry<String, String> entry : m.entrySet()){
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
System.out.println();
}
2.什么是Set?
不包含重复元素的集合。更正式地说,设置 不包含一对元素,并且最多包含一个 null 元素。Set 接口除了这些规定之外,还放置了其他规定 继承自 Collection 接口,在 all 的合约上 构造函数以及 add、equals 和 hashCode 方法的合约。其他继承方法的声明包括 为方便起见,此处也包括在内。
2.1Set的常见方法
注意:
1. Set
是继承自
Collection
的一个接口类
2. Set
中只存储了
key
,并且要求
key
一定要唯一
3. TreeSet
的底层是使用
Map
来实现的,其使用
key
与
Object
的一个默认对象作为键值对插入到
Map
中的
4. Set
最大的功能就是对集合中的元素进行去重
5.
实现
Set
接口的常用类有
TreeSet
和
HashSet
,还有一个
LinkedHashSet
,
LinkedHashSet
是在
HashSet
的基础
上维护了一个双向链表来记录元素的插入次序。
6. Set
中的
Key
不能修改,如果要修改,先将原来的删除掉,然后再重新插入
7. TreeSet
中不能插入
null
的
key
,
HashSet
可以。
8. TreeSet
和
HashSet
的区别
![](https://img-blog.csdnimg.cn/4d06353e1c5f4131b13772f87277d08c.png)
2.2TreeSet的使用案例
import java.util.TreeSet;
import java.util.Iterator;
import java.util.Set;
public static void TestSet(){
Set<String> s = new TreeSet<>();
// add(key): 如果key不存在,则插入,返回ture
// 如果key存在,返回false
boolean isIn = s.add("apple");
s.add("orange");
s.add("peach");
s.add("banana");
System.out.println(s.size());
System.out.println(s);
isIn = s.add("apple");
// add(key): key如果是空,抛出空指针异常
//s.add(null);
// contains(key): 如果key存在,返回true,否则返回false
System.out.println(s.contains("apple"));
System.out.println(s.contains("watermelen"));
// remove(key): key存在,删除成功返回true
// key不存在,删除失败返回false
// key为空,抛出空指针异常
s.remove("apple");
System.out.println(s);
s.remove("watermelen");
System.out.println(s);
Iterator<String> it = s.iterator();
while(it.hasNext()){
System.out.print(it.next() + " ");
}
System.out.println();
}
3.哈希表
3.1 概念
顺序结构以及平衡树 中,元素关键码与其存储位置之间没有对应的关系,因此在 查找一个元素时,必须要经过关键 码的多次比较 。 顺序查找时间复杂度为 O(N) ,平衡树中为树的高度,即 O( ) ,搜索的效率取决于搜索过程中 元素的比较次数。理想的搜索方法:可以 不经过任何比较,一次直接从表中得到要搜索的元素 。 如果构造一种存储结构,通过某种函 数 (hashFunc) 使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快 找到该元素 。
当向该结构中:插入元素根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放搜索元素对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若 关键码相等,则搜索成功 该方式即为哈希( 散列 ) 方法, 哈希方法中使用的转换函数称为哈希 ( 散列 ) 函数,构造出来的结构称为哈希表 (Hash Table)( 或者称散列表 )
3.2实现代码
package 哈希;
import java.util.Arrays;
public class HashBuck {
//链表节点
static class Node {
public int key;
public int val;
public Node next;
public Node(int key, int val) {
this.key = key;
this.val = val;
}
}
public Node[] array;
public int usedSzie; //存放了多少个数据
public static final float DEFAULT_LOAD_FACTOR = 0.75f;
public HashBuck(){
array = new Node[10];
}
public void put(int key,int val){
int index = key%array.length;
//遍历index下标的链表 是否更新value 不存在 进行 头插法 插入数据
Node cur = array[index];
while (cur!= null){
if(cur.key == key){
//更新value
cur.val = val;
return;
}
cur = cur.next;
}
//cur == null 链表遍历完成 没有找到这个key
Node node = new Node(key, val);
node.next = array[index];
array[index] = node;
usedSzie++;
if(doLoadFactor()> DEFAULT_LOAD_FACTOR){
//扩容
resize();
}
}
private void resize(){
Node[] newArray = new Node[2*array.length];
//遍历原来的数组
for (int i = 0; i < array.length; i++) {
Node cur = array[i];
while (cur != null){
Node tmp = cur.next;
int newIndex = cur.key % newArray.length;//新数组的下标
//采用头插法 插入到新数组的 newIndex下标
cur.next = newArray[newIndex];
newArray[newIndex] = cur;
}
}
array =newArray;
}
public int get(int key){
int index = key%array.length;
//遍历index下标的链表 是否更新value 不存在 进行 头插法 插入数据
Node cur = array[index];
while (cur!= null){
if(cur.key == key){
//更新value
return cur.val;
}
cur = cur.next;
}
return -1;
}
private float doLoadFactor() {
return usedSzie*0.1f/array.length;
}
}
3.3和 java 类集的关系
1. HashMap
和
HashSet
即
java
中利用哈希表实现的
Map
和
Set
2. java
中使用的是哈希桶方式解决冲突的
3. java
会在冲突链表长度大于一定阈值后,将链表转变为搜索树(红黑树)
4. java
中计算哈希值实际上是调用的类的
hashCode
方法,进行
key
的相等性比较是调用
key
的
equals
方 法。所以如果要用自定义类作为 HashMap
的
key
或者
HashSet
的值,
必须覆写
hashCode
和
equals
方
法
,而且要做到
equals
相等的对象,
hashCode
一定是一致的。