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及测试相关推荐

  1. 自己手写HashMap——红黑树的Java实现

    0.引言 (1)HashMap简单介绍 你好,这篇文章是<自己手写HashMap>的第一篇. 在java7之前,HashMap是用数组(hash桶)+链表的形式实现的,大概的原理就是对ke ...

  2. 手写HashMap,快手面试官直呼内行

    手写HashMap?这么狠,面试都卷到这种程度了? 第一次见到这个面试题,是在某个不方便透露姓名的Offer收割机大佬的文章: 这--我当时就麻了,我们都知道HashMap的数据结构是数组+链表+红黑 ...

  3. 面试表演了手写HashMap,把面试官给秀到了

    手写HashMap?这么狠,面试都卷到这种程度了? 第一次见到这个面试题,是在某个不方便透露姓名的Offer收割机大佬的文章: 手写HashMap,快手一面卒 这--我当时就麻了,我们都知道HashM ...

  4. Java手写Hashmap(HashMap的基本用法)

    一:引言 HashMap是Map的实现类,其方法都可以继承Map,不用手写,本篇只是为了了解底层代码和复习java基础敲得码 二:上码 package cn.wyj.two;public class ...

  5. 手写Hashmap第二版

    在第一版的基础上,如果需要实现线程安全,需要在增加.删除.扩容的时候加锁.扩容的时候,需要对整个数组加锁.增加和删除的时候,只需要对链表的头结点进行加锁.具体实现代码如下: package www.d ...

  6. 内含扩容源码的面试题,目标是手写HashMap!

    基础知识 说说List.Set.Map三者的区别 List(对付顺序的好帮手):List接口存储一组不唯一(可以用多个元素引用相同的对象),有序的对象. Set(注重第一无二的性质):不允许重复的集合 ...

  7. 【福利分享】阿里面试官叫我手写HashMap,我两分钟就给他整出来了!!!

    文章目录

  8. 手写一个简单的HashMap,搞定挑剔面试官

    作者:编程十二 链接:https://www.jianshu.com/p/1be0e957baf2 前言 今天去面试啊,聊得差不多的时候面试官突然问我会手写HashMap吗?这我哪能怂啊,好死不死的面 ...

  9. 手写迷你版HashMap

    最近看了一些面试题,感觉网上好像有挺多公司比较喜欢让面试者手写HashMap,说实话理解JDK HashMap源码还是很需要时间的.打开看了一下HashMap源代码,将近2400行代码,前两位作者乃大 ...

最新文章

  1. Cleaver快速制作网页PPT
  2. Effective C++ 11 在operator=中处理“自我赋值” 笔记
  3. 谈谈阿里所谓的“要性”
  4. 关于[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object的报错分析
  5. Settings【学习笔记05】
  6. linux-文件的查找-find
  7. #51CTO学院四周年#感谢有你,一路相伴
  8. 我来做百科(第七天)
  9. 如何删除git本地分支
  10. 汽车底盘线控与动力学域控制技术
  11. linux audit 源码分析,audit初探
  12. 基数树结构---radix_tree
  13. oracle11g64跟32,plsql32 位连接oracle11g64位方法
  14. 联想服务器pe进系统还原,传授联想如何一键还原系统
  15. 绿色商业包装材料在行业中的重要地位
  16. 正则改造VS Code里React类组件的自定义snippet
  17. mac电脑装虚拟机遇到的坑!
  18. 免费漫画大全隐私协议
  19. NimotionStudio软件如何用?Studio软件的使用方法、操作流程和常见功能
  20. 我国计算机科学的发展源头,计算机科学与技术概论论文正稿.doc

热门文章

  1. android studio中 实战演练——天气预报 程序
  2. OSPF路由协议详解与实战演练
  3. 地铁涨价,新出台的javase计算每月乘坐地铁消费金额
  4. Java 8中Collectors.toMap空指针异常源码分析
  5. 快捷给UE4项目改名
  6. Power Supply 文件节点和电池服务属性对照
  7. 十进制快速转换为二进制
  8. 信安数学/网安数学——证明形如4k-1的素数有无限个
  9. Linux基本常用命令|ubuntu获取root权限
  10. 计算机英语反思总结怎么写,英语考试反思总结(精选10篇)