文章目录
一.映射
1.为什么要用映射?
在真正学习HashMap之前我们先了解下map的基本概念,在java中map通常表示一种映射关系。
集是一个集合,它可以快速地查找现有地元素,但是,要查看一个元素,需要有要查找元素的精确副本,这不是一种非常通用的查找方式。通用的方式是我们知道某些键的信息,并想要查找与之对应的元素。 映射(map)数据结构就是为此设计的。
2.什么是映射?
映射(map)用来存放键/值对,如果我们提供了键,就可以找到相应的值。
二.HashMap基础学习
1.何为HashMap?
HashMap是对键进行Hash来存储数据的一种数据结构的实现,它主要实现了Map接口和继承了骨架实现,使用它我们可以对数据进行快速存储和查找。
2.构造方法
构造方法 | 描述 |
---|---|
HashMap() | 构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。 |
HashMap(int initialCapacity) | 构造一个空的 HashMap具有指定的初始容量和默认负载因子(0.75)。 |
HashMap(int initialCapacity, float loadFactor) | 构造一个空的 HashMap具有指定的初始容量和负载因子。 |
HashMap(Map<? extends K,? extends V> m) | 构造一个新的 HashMap与指定的相同的映射 Map 。 |
3.基本操作方法
方法 | 描述 |
---|---|
void clear() | 从这张地图中删除所有的映射。 |
Object clone() | 返回此 HashMap实例的浅拷贝:键和值本身不被克隆。 |
boolean containsKey(Object key) | 如果此映射包含指定键的映射,则返回 true 。 |
V get(Object key) | 返回到指定键所映射的值,或 null如果此映射包含该键的映射。 |
boolean isEmpty() | 如果此地图不包含键值映射,则返回 true 。 |
Set keySet() | 返回此地图中包含的键的Set视图。 |
V put(K key, V value) | 将指定的值与此映射中的指定键相关联。 |
V remove(Object key) | 从该地图中删除指定键的映射(如果存在)。 |
V replace(K key, V value) | 只有当目标映射到某个值时,才能替换指定键的条目。 |
int size() | 返回此地图中键值映射的数量。 |
4.使用例子
Employee.java
package JavaHeXinJiShu;
public class Employee
{
private String name;
private double salary;
public Employee(String name)
{
this.name = name;
salary = 0;
}
public String toString()
{
return "[name=" + name + ", salary=" + salary + "]";
}
}
example9_6.java
package JavaHeXinJiShu;
import java.util.HashMap;
import java.util.Map;
public class example9_6 {
public static void main(String[] args)
{
Map<String, Employee> staff = new HashMap<>();
staff.put("144-25-5464", new Employee("Amy Lee"));
staff.put("567-24-2546", new Employee("Harry Hacker"));
staff.put("157-62-7935", new Employee("Gary Cooper"));
staff.put("456-62-5527", new Employee("Francesca Cruz"));
// 打印hashmap中的所有键值对
System.out.println(staff);
//删除键名为567-24-2546的键值对
staff.remove("567-24-2546");
// 覆盖456-62-5527原有键值对
staff.put("456-62-5527", new Employee("Francesca Miller"));
// 查看键为157-62-7935的值
System.out.println(staff.get("157-62-7935"));
//遍历所有键值对
staff.forEach((k, v) ->
System.out.println("key=" + k + ", value=" + v));
}
}
二.下面我们通过分析上面的例子来深入学习
1.构造方法分析
Map<String, Employee> staff = new HashMap<>();
通过上面的语句我们构建了一个HashMap的实例对象,我们知道默认对象的负载因子为0.75,容量为16.
2.走进put方法(源码分析)
staff.put("144-25-5464", new Employee("Amy Lee"));
我们下面来看看,上面语句到底做了些什么?
put的源代码为:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
我们可以知道,它的返回类型和值的类型一致,参数为一对键值。
方法体里只是放回了调用putVal方法的返回值。
上面pubVal中传入了五个参数:
hash(key),key,value,false,true
其中hash方法为:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
它的作用是计算并返回key的哈希值。
下面我们把例子带入:
首先,我们向put中传进key=“144-25-5464” 一个字符串。value=new Employee(“Amy Lee”) 一个对象。
然后,将key哈希过的值,key,value,false,true传入putVal方法中。
下面我们来进入putVal方法:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; //定义节点数组tab
Node<K,V> p; //定义节点p
int n, i;
//初始化tab
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//如果要加入的键值对中的键不在其中,添加之
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
//否则覆盖之
else {
Node<K,V> e;
K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
//如果是覆盖存储的返回覆盖后的value值
if (e != null) {
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;//新存储的返回null
}
也就是说:
staff.put("144-25-5464", new Employee("Amy Lee"));
上面的语句完成了将key=“144-25-5464”,value= new Employee(“Amy Lee”)的键值对存入我们的HashMap对象中且如果HashMap中原本存在此键对应的value值返回此value对象,否则返回null。
3.看看get方法
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
原理很简单,就不解析了。
上面主要解析了put方法,还有其他方法的探索我们可以在这个基础上来进行。