什么HashMap
hash: 散列讲一个任意的长度通过某种(hash函授算法)算法转换成一个固定值。
java是通过移位实现的。
Map:地图x,y存储
总结:通过Hash出来的值,然后通过值定位到这个map然后value存储到这个map中HashMap
基本原理
存储数据格式: key : value
注意:
- key可以为空吗?Null 当成一个key来存储
- 如果hash key重复了value 会覆盖吗?
---会,然后把旧的数据(oldValue)返回 , key不重复返回null,需要注意的是key相同和 hash值相同的不同处。
面试:
HashMap 什么时候做扩容? put的时候,到达容量的3/4的时候会扩容。
长度:
一定是偶数,且都是当前长度的2倍(table.length*2)
hashMap的数据结构
entry[] 也就是数组 加链表 []数组结构 entry是链表结构。
源码分析
- 初始化参数介绍
- put方法分析
- get方法分析
- entry对象介绍
- 扩容源码分析
手写HashMap实现
- 需要自定接口Map<K,V>,内部接口Entry<K,V>接口,和put ,get ,size ; getKey , getValue 方法;
/** * 手写map接口 */ public interface Map<K,V> { public V put(K k,V v); public V get(K k); public int size(); public interface Entry<K,V>{ public K getKey(); public V getValue(); } }
-
自定HashMap<K,V>实现类Map<K,V>,内部实现类Entry<K,V>实现内部接口Entry<K,V>;
/** * Created by Administrator on 2018-11-22. */ public class HashMap<K, V> implements Map<K, V> { private static int defaultCapcityLength = 16;//初始化容量 private static double defaultLoadFactor = 0.75f; //负载因子 private Entry<K, V>[] table; //用来保存数据; private int size = 0; //默认大小从0开始 //无参构造函数 public HashMap() { this(defaultCapcityLength, defaultLoadFactor); } //构造函数 public HashMap(int length, double loadFactor) { //因为是静态的参数索引不能用this defaultCapcityLength = length; defaultLoadFactor = loadFactor; table = new Entry[defaultCapcityLength]; } //hash算法 public int hash(K k) { int length = defaultCapcityLength; int i = k.hashCode() % length; return i >= 0 ? i : -i; } //自定义创建entry对象的方法 public Entry<K, V> newEntry(K k, V v, Entry<K, V> next) { return new Entry<>(k, v, next); } public V find(K k, Entry<K, V> entry) { V v; if (k != null) { if (k == entry.getKey() || k.equals(entry.getKey())) { v = entry.getValue(); return v; } if (entry.next != null) { return find(k, entry.next); //递归查找数据 } } return null; } @Override public V put(K k, V v) { int index = hash(k); System.out.println("index:" + index); Entry<K, V> entry = table[index]; //1.先去表中同样的位置去取这个数据 if (entry == null) { //2.如果没有,就说明可以添加,创建一个entry 赋值给数组entry同样的位置 table[index] = newEntry(k, v, null); size++; } else { //3.如果有, if (entry.getKey().equals(k) || k == entry.getKey()) { //4.对比key是否相同,如果相同就覆盖, table[index] = newEntry(k, v, null); } else { //5.如果key 不同,就直接创建一个新的对象,把旧的对象放到他的下一个next里面 table[index] = newEntry(k, v, entry); size++; } } return table[index].getValue(); } @Override public V get(K k) { int index = hash(k); Entry<K, V> entry = table[index]; if (entry == null) { return null; } return find(k, table[index]); } @Override public int size() { return this.size; } /** * 内部实现类 * * @param <K> * @param <V> */ class Entry<K, V> implements Map.Entry<K, V> { K k; // key V v; // value Entry<K, V> next; //链表结构 //有参构造函数 public Entry(K k, V v, Entry<K, V> next) { this.k = k; this.v = v; this.next = next; } @Override public K getKey() { return k; } @Override public V getValue() { return v; } } }
不足之处(伸缩性角度)
key的hash值如果相同有关 get o(1)
1、伸缩性:
2、时间复杂度: 你的hash算法决定了你的效率
伸缩性角度:
每当hashmap扩容的时候需要去add entry 对象 需要重新遍历读取数据转到新的entry[] table 数组里面;
如果你知道你的hashmap存储多少值,最好先指定他的初始容量大小,防止在put的时候,再次扩容。