手写HashMap及测试
MyHashMap:
/*
手动实现一个HashMap(根据JDK 1.7, 数组 + 链表)规定key和value都不能是null,但是JDK 1.7允许key和value为null。*/public class MyHashMap<K, V> {// 属性private static final int MAX_CAPACITY = 1 << 30; // 即最大容量为2^30private static final int DEFAULT_CAPACITY = 1 << 4; // 初始默认容量为16private static final double DEFAULT_LOAD_FACTOR = 0.75; // 默认加载因子为0.75private Node<K, V>[] table; // 哈希表底层数组private int size; // 元素个数private double loadFactor; // 加载因子// 阈值 = 数组长度 * 加载因子(如果调用无参构造,则阈值=16*0.75=12。如果传递了初始容量,则这个等式不一定成立,例如initialCapacity=30,则底层数组长度为32,阈值也等于30)private int threshold;// 节点内部类private static class Node<K, V> {int hash;K key;V value;Node<K, V> next;Node(int hash, K key, V value, Node<K, V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}@Overridepublic String toString() {return key + "=" + value;}}// 构造方法public MyHashMap() {this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);}public MyHashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);}@SuppressWarnings("unchecked")public MyHashMap(int initialCapacity, double loadFactor) {if (initialCapacity < 0 || initialCapacity > MAX_CAPACITY) {throw new IllegalArgumentException("initialCapacity=" + initialCapacity);}if (loadFactor <= 0 || Double.isNaN(loadFactor)) {// Double.isNaN():如果参数不是一个double类型的数字,则返回true,否则返回falsethrow new IllegalArgumentException("loadFactor=" + loadFactor);}// 参数都合法this.loadFactor = loadFactor;int length = tableSizeFor(initialCapacity); // 计算哈希表底层数组的长度// table = new Node<K, V>[length]; // 此行代码错误,没有"泛型数组"这个概念,不能newtable = (Node<K, V>[]) new Node[length]; // 注意这种写法。方法最上面加一个注解,就不再有警告this.threshold = initialCapacity;}// 求大于等于cap的最小的2的n次幂private int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return n < 0 ? 1 : (n >= MAX_CAPACITY ? MAX_CAPACITY : n + 1);}/*** 根据指定的key来获取对应的value* @param key 指定的key* @return 对应的value* 时间复杂度为O(1)*/public V get(K key) {int hash = hash(key);int index = indexFor(hash, table.length);for (Node<K, V> node = table[index]; node != null; node = node.next) {if (hash == node.hash && (key == node.key) || key.equals(node.key)) {return node.value;}}return null;}private int hash(K key) {// 这就是JDK 1.8源码int h;return key == null ? 0 : (h = key.hashCode()) ^ (h >>> 16);}private int indexFor(int hash, int length) {// returnn hash % length;return hash & (length - 1); //位运算,效率高}/*** 向HashMap中添加元素对* @param key 待添加的key* @param value 待添加的value* @return 返回oldValue* 时间复杂度为O(1)*/public V put(K key, V value) {if (key == null) throw new NullPointerException();int hash = hash(key);int index = indexFor(hash, table.length);for (Node<K, V> node = table[index]; node != null; node = node.next) {if (hash == node.hash && (key == node.key || key.equals(node.key))) {// 有相同的元素,不添加,只更新valueV oldValue = node.value;node.value = value;return oldValue;}}// 没有相同元素,开始添加addNode(hash, key, value, index);return null;}private void addNode(int hash, K key, V value, int index) {if (size >= threshold) {// 需要扩容resize(table.length << 1); // 扩容为原来的2倍}Node<K, V> node = new Node<>(hash, key, value, table[index]); // 泛型类,等号右边的泛型可以不写table[index] = node;size++; // Caution!!!}@SuppressWarnings("unchecked")private void resize(int newLength) {if (table.length == MAX_CAPACITY) {// 达到最大,不再扩容threshold = Integer.MAX_VALUE; // size不会超过Integer.MAX_VALUE,即addNode()方法中的if永远为false,不再扩容。return;}// 进行扩容Node<K, V>[] newTable = (Node<K, V>[]) new Node[newLength];threshold = (int) (newLength * loadFactor);for (int i = 0; i < table.length; i++) {for (Node<K, V> node = table[i]; node != null;) {int hash = node.hash;int index = indexFor(hash, newLength); // 在新数组中的索引位置/*Node<K, V> newNode = new Node<>(hash, node.key, node.value, newTable[index]); // 相当于把节点重新复制了,需要额外空间,不推荐newTable[index] = newNode;*/Node<K, V> nextNode = node.next; // 记录下一个元素, Caution!!!node.next = newTable[index];newTable[index] = node;node = nextNode; // Caution!!!}}table = newTable;}/*** 根据指定的key删除元素,并返回value* @param key 指定的key* @return 对应的value* 时间复杂度为O(1)*/public V remove(K key) {if (key == null) return null; // 为了简单起见,假设key和value都不能为nullint hash = hash(key);int index = indexFor(hash, table.length);Node<K, V> prev = null;for (Node<K, V> node = table[index]; node != null; node = node.next) {if (hash == node.hash && (key == node.key || key.equals(node.key))) {if (prev == null) {// 第一个节点就是要删除的节点table[index] = node.next;} else {prev.next = node.next;}size--;return node.value;}prev = node;}return null;}/*** 获取HashMap元素的个数* @return HashMap元素的个数*/public int size() {return size;}/*** 判断HashMap是否为空* @return 如果为空,返回true;如果不空,返回false*/public boolean isEmpty() {return size == 0;}/*** 是否包含指定的key* @param key 指定的key* @return 如果包含指定的key,返回true;否则返回false*/public boolean containsKey(K key) {return get(key) != null;}/*** 清空哈希表*/public void clear() {for (int i = 0; i < table.length; i++) {table[i] = null;}size = 0; // Caution!!!}/*** 打印HashMap,需要重写toString()方法* @return HashMap的字符串形式*/@Overridepublic String toString() {if (size == 0) return "{}";StringBuffer sb = new StringBuffer();sb.append("{");for (int i = 0; i < table.length; i++) {for (Node<K, V> node = table[i]; node != null; node = node.next) {sb.append(node).append(", ");// append参数是String类型,说明这里node是String类型,所以要重写Node类的toString()方法}}sb.delete(sb.length() - 2, sb.length()); // 包左不包右return sb.append("}").toString();}
}
MyHashMapTest:
public class MyHashMapTest {public static void main(String[] args) {MyHashMap<String, String> map = new MyHashMap<>(2);map.put("刘强东", "章泽天");map.put("王宝强", "马蓉");map.put("文章", "马伊琍");map.put("贾乃亮", "李小璐");System.out.println(map); // {贾乃亮=李小璐, 刘强东=章泽天, 王宝强=马蓉, 文章=马伊琍}System.out.println(map.size()); // 4 扩容正常// V remove(K key)System.out.println(map.remove("henson_z")); // nullSystem.out.println(map.remove("文章")); // 马伊琍System.out.println(map); // {贾乃亮=李小璐, 刘强东=章泽天, 王宝强=马蓉}System.out.println(map.size()); // 3// V get(K key)// boolean containsKey(K key)System.out.println(map.get("刘强东")); // 章泽天System.out.println(map.get("邓超")); // nullSystem.out.println(map.containsKey("刘强东")); // trueSystem.out.println(map.containsKey("邓超")); // false// V put(K key, V value)System.out.println(map.put("邓超", "孙俪")); // nullSystem.out.println(map.put("王宝强", "")); // 马蓉System.out.println(map); // {邓超=孙俪, 贾乃亮=李小璐, 刘强东=章泽天, 王宝强=}System.out.println(map.size()); // 4// 判空System.out.println(map.isEmpty()); // falsemap.clear();System.out.println(map.isEmpty()); // true}
}
以上结果都经过本人实际测试,结果都正确。
结束语:如果本篇博客对您有帮助,请点赞、收藏或关注,您的鼓励是博主进步的动力,感谢支持,共同进步。
手写HashMap及测试相关推荐
- 自己手写HashMap——红黑树的Java实现
0.引言 (1)HashMap简单介绍 你好,这篇文章是<自己手写HashMap>的第一篇. 在java7之前,HashMap是用数组(hash桶)+链表的形式实现的,大概的原理就是对ke ...
- 手写HashMap,快手面试官直呼内行
手写HashMap?这么狠,面试都卷到这种程度了? 第一次见到这个面试题,是在某个不方便透露姓名的Offer收割机大佬的文章: 这--我当时就麻了,我们都知道HashMap的数据结构是数组+链表+红黑 ...
- 面试表演了手写HashMap,把面试官给秀到了
手写HashMap?这么狠,面试都卷到这种程度了? 第一次见到这个面试题,是在某个不方便透露姓名的Offer收割机大佬的文章: 手写HashMap,快手一面卒 这--我当时就麻了,我们都知道HashM ...
- Java手写Hashmap(HashMap的基本用法)
一:引言 HashMap是Map的实现类,其方法都可以继承Map,不用手写,本篇只是为了了解底层代码和复习java基础敲得码 二:上码 package cn.wyj.two;public class ...
- 手写Hashmap第二版
在第一版的基础上,如果需要实现线程安全,需要在增加.删除.扩容的时候加锁.扩容的时候,需要对整个数组加锁.增加和删除的时候,只需要对链表的头结点进行加锁.具体实现代码如下: package www.d ...
- 内含扩容源码的面试题,目标是手写HashMap!
基础知识 说说List.Set.Map三者的区别 List(对付顺序的好帮手):List接口存储一组不唯一(可以用多个元素引用相同的对象),有序的对象. Set(注重第一无二的性质):不允许重复的集合 ...
- 【福利分享】阿里面试官叫我手写HashMap,我两分钟就给他整出来了!!!
文章目录
- 手写一个简单的HashMap,搞定挑剔面试官
作者:编程十二 链接:https://www.jianshu.com/p/1be0e957baf2 前言 今天去面试啊,聊得差不多的时候面试官突然问我会手写HashMap吗?这我哪能怂啊,好死不死的面 ...
- 手写迷你版HashMap
最近看了一些面试题,感觉网上好像有挺多公司比较喜欢让面试者手写HashMap,说实话理解JDK HashMap源码还是很需要时间的.打开看了一下HashMap源代码,将近2400行代码,前两位作者乃大 ...
最新文章
- Cleaver快速制作网页PPT
- Effective C++ 11 在operator=中处理“自我赋值” 笔记
- 谈谈阿里所谓的“要性”
- 关于[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object的报错分析
- Settings【学习笔记05】
- linux-文件的查找-find
- #51CTO学院四周年#感谢有你,一路相伴
- 我来做百科(第七天)
- 如何删除git本地分支
- 汽车底盘线控与动力学域控制技术
- linux audit 源码分析,audit初探
- 基数树结构---radix_tree
- oracle11g64跟32,plsql32 位连接oracle11g64位方法
- 联想服务器pe进系统还原,传授联想如何一键还原系统
- 绿色商业包装材料在行业中的重要地位
- 正则改造VS Code里React类组件的自定义snippet
- mac电脑装虚拟机遇到的坑!
- 免费漫画大全隐私协议
- NimotionStudio软件如何用?Studio软件的使用方法、操作流程和常见功能
- 我国计算机科学的发展源头,计算机科学与技术概论论文正稿.doc