概述
双列集合:用来存储键值对的集合。
- interface Map<K,V> : K(key)键 ,V(value)值
- 将键映射到值的对象,不能出现重复的键,每个键最多可以映射到一个值
1、Map和Collection没有继承关系。
2、Map集合以key和value的方式存储数据:键值对
key和value都是引用数据类型。
key和value都是存储对象的内存地址。
key起到主导的地位,value是key的一个附属品。
Map的基本方法
方法名 | 说明 |
---|---|
V put(K key,V value) | 设置键值对 |
V remove(Object key) | 删除元素 |
void clear() | 清空集合 |
boolean containsKey(Object key) | 判断键是否存在,存在则返回true |
boolean containsValue(Object value) | 判断值是否存在,存在则返回true |
boolean isEmpty() | 判断集合是否为空 |
int size() | 获取集合元素个数 |
import java.util.HashMap;
import java.util.Map;
public class Map01 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("STU001","Andy");
map.put("STU002","Jack");
map.put("STU003","Tom");
map.put("STU004","Bob");
map.put("STU004","Smith");//设置(修改)
//如果键不存在,则表示添加元素。如果键存在,则表示设置值。
//删除
System.out.println(map.remove("STU003")); //Tom
//判断是否包含
System.out.println(map.containsKey("STU003")); //false
System.out.println(map.containsKey("STU004")); //true
System.out.println("-----------------------");
System.out.println(map.containsValue("Tom")); //false
System.out.println(map.containsValue("Smith")); //true
System.out.println("-----------------------");
System.out.println(map.isEmpty());//判断集合是否为空 false
map.clear();//清空集合
System.out.println(map.isEmpty()); //true
System.out.println(map); //{}
}
}
Map集合的获取功能
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class map_get {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("STU001","Andy");
map.put("STU002","Jack");
map.put("STU003","Tom");
map.put("STU004","Bob");
//get通过键获取值
System.out.println(map.get("STU003"));
System.out.println("------------------");
//keySet 获取所有键的Set集合
Set<String> keySet = map.keySet();
System.out.println(keySet);
//values 获取所有值的Collection集合
Collection<String> values = map.values();
System.out.println(values);
//entrySet 获取所有键值对对象的Set集合
Set<Map.Entry<String, String>> es = map.entrySet();
//Map集合通过entrySet()方法转换成的这个Set集合,Set集合中元素的类型是 Map.Entry<K,V>
//Map.Entry和String一样,都是一种类型的名字,只不过:Map.Entry是静态内部类,是Map中的静态内部类
System.out.println(es);
//[STU001=Andy, STU003=Tom, STU002=Jack, STU004=Bob]
for (Map.Entry<String, String> entry:es){
System.out.println("key:"+entry.getKey()+" "+"value:"+entry.getValue());
}
/*
key:STU001 value:Andy
key:STU003 value:Tom
key:STU002 value:Jack
key:STU004 value:Bob
*/
}
}
哈希表
通过 数组 + 链表 实现的一种数据结构
哈希表的构造方法的参数是一个长度为16个元素的数组,通过哈希值 % 16 的值,作为头节点在数组中选择对应的位置,就形成了哈希表。
HashMap
底层源码
public class HashMap{
// HashMap底层实际上就是一个数组。(一维数组)
Node<K,V>[] table;
// 静态的内部类HashMap.Node
static class Node<K,V> {
final int hash; // 哈希值(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数/算法,可以转换存储成数组的下标。)
final K key; // 存储到Map集合中的那个key
V value; // 存储到Map集合中的那个value
Node<K,V> next; // 下一个节点的内存地址。
}
}
特点
-
1、无序,不可重复。
为什么无序? 因为不一定挂到哪个单向链表上。
不可重复是怎么保证的? equals方法来保证HashMap集合的key不可重复。
如果key重复了,value会覆盖。 -
2、放在HashMap集合key部分的元素其实就是放到HashSet集合中了。
所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法。 -
3、HashMap集合的默认初始化容量是16,默认加载因子是0.75
这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组以二叉树开始扩容。
重点,记住:HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,
这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。
注意
1.向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法! equals方法有可能调用,也有可能不调用。 拿put(k,v)举例,什么时候equals不会调用?
k.hashCode()方法返回哈希值, 哈希值经过哈希算法转换成数组下标。 数组下标位置上如果是null,equals不需要执行。 拿get(k)举例,什么时候equals不会调用? k.hashCode()方法返回哈希值, 哈希值经过哈希算法转换成数组下标。 数组下标位置上如果是null,equals不需要执行。
4.假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了 纯单向链表。
这种情况我们成为:散列分布不均匀。什么是散列分布均匀?
假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的, 是散列分布均匀的。假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题? 不行,因为这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。 也是散列分布不均匀。散列分布均匀需要你重写hashCode()方法时有一定的技巧。