一、简介

除了List,平时在项目中用的比较多的还有Map,Map是存放键值对的数据结构,键不允许重复,如果键重复会覆盖之前的值。比较常用到的有: HashMap和HashTable。本文将会模仿Map的源码实现两个自定义的Map(一个是没有用hashcode的效率低的版本,一个是用hashcode优化版),只是帮助理解Map底层实现原理,实际项目中没有必要去自定义Map。

二、实现原理

Map是存放键值对的数据结构键不允许重复,如果新增的时候已经存放相同键的元素,那么会覆盖之前相同键的值。Map底层是通过操作Entry[]数组来实现的,Entry里面主要就是存放我们的键值对。

三、自定义Map(效率低,没有使用hashcode)

Entry类:存放键值对的对象

public class CustomEntry {/*** 键*/private Object key;/*** 值*/private Object value;public CustomEntry() {}public CustomEntry(Object key, Object value) {this.key = key;this.value = value;}public Object getKey() {return key;}public void setKey(Object key) {this.key = key;}public Object getValue() {return value;}public void setValue(Object value) {this.value = value;}
}
package wsh.version1;/*** 自定义Map(版本一,效率较低,需要循环遍历找相同的key)** @author weishihuai* @date 2018/9/27 21:25* 说明:* 1. Map是存放键值对(key-value)的数据结构* 2. Map键值不允许重复,如果重复会覆盖之前的值* 3. 根据键值可以找到对应的值*/
public class CustomMap {private CustomEntry[] array = new CustomEntry[1000];private int size;/*** 往Map中新增元素* 原理: 新建Entry对象,往数组中添加元素即可** @param key   键* @param value 值*/public void put(Object key, Object value) {CustomEntry customEntry = new CustomEntry(key, value);//判断是否存在相同的键,如果有相同的直接覆盖之前的值,size不用改变。for (int i = 0; i < size; i++) {if (array[i].getKey().equals(key)) {array[i].setValue(value);return;}}array[size] = customEntry;size++;}/*** 根据键找到相应的值* 原理: 循环遍历entry数组中的key集合,找到返回键对应的值,未找到返回null** @param key 键* @return 键对应的值*/public Object get(Object key) {for (int i = 0; i < size; i++) {if (array[i].getKey().equals(key)) {return array[i].getValue();}}return null;}/*** 判断是否存放指定key的元素* 原理: 循环遍历所有的key集合,找到返回true,找不到返回false** @param key 键* @return 是否包含对应的键的元素*/public boolean containsKey(Object key) {for (int i = 0; i < size; i++) {if (array[i].getKey().equals(key)) {return true;}}return false;}/*** 判断是否包含指定值的元素* 原理: 循环遍历所有的value集合,找到返回true,找不到返回false** @param value 值* @return 是否包含指定值的元素*/public boolean containsValue(Object value) {for (int i = 0; i < size; i++) {if (array[i].getValue().equals(value)) {return true;}}return false;}/*** 返回Map中元素的个数** @return Map的元素个数*/public int size() {return size;}/*** 判断Map是否为空** @return map是否为空*/public boolean isEmpty() {return size == 0;}}

四、部分方法详解

【a】Entry类    主要属性就是键值对

public class CustomEntry {/*** 键*/private Object key;/*** 值*/private Object value;
}

【b】 put(Object key, Object value) {} 往Map插入新的元素

1. 实现原理:   通过传递的参数新建Entry对象,往数组中添加元素即可。

2. 注意需要判断是否存在相同的键,如果有相同的直接覆盖之前的值,size不用改变。

//判断是否存在相同的键,如果有相同的直接覆盖之前的值,size不用改变。for (int i = 0; i < size; i++) {if (array[i].getKey().equals(key)) {array[i].setValue(value);return;}}

这里是直接遍历整个数组查找是否有相同key的元素。

【c】get(Object key) {} 根据键取出对应的值

1. 实现原理:  循环遍历Entry数组中的key集合,找到返回键对应的值,未找到返回null。

2. 注意查找是否有相同key采用的是equals方法。

【d】containsKey(Object key) {}    判断Map是否存放某个键

1. 实现原理: 循环遍历所有的key集合,找到返回true,找不到返回false。

【e】containsValue(Object value) {} 判断是否包含指定值的元素

1. 实现原理:  循环遍历所有的value集合,找到返回true,找不到返回false。

【f】isEmpty(){}   判断Map是否为空

1. 实现原理:  return size == 0;

通过上面的代码可以看到,都是循环整个数组去遍历,如果数组过大,显然效率并不是很好,那么通过查看HashMap的源码可以看到,有一种优化方案,就是使用hashcode和equals()方法来优化,这样就不用整个数组遍历。

测试:

package wsh.version1;/*** 测试自定义Map** @author weishihuai* @date 2018/9/27 21:30*/
public class TestCustomMap {public static void main(String[] args) {CustomMap customMap = new CustomMap();/***********************************put(key,value)*************************************/customMap.put("aaa", "zhangsan");customMap.put("bbb", "lisi1");customMap.put("bbb", "lisi2");customMap.put("ccc", "wangwu");/*******************************get(key)***********************************************///zhangsanSystem.out.println(customMap.get("aaa"));/************************************size()********************************************///3System.out.println(customMap.size());/**********************************get(key)********************************************///lisi2System.out.println(customMap.get("bbb"));/*************************************containsKey(key)*********************************///trueSystem.out.println(customMap.containsKey("aaa"));//falseSystem.out.println(customMap.containsKey("123"));/************************************containsValue(value)******************************///trueSystem.out.println(customMap.containsValue("zhangsan"));//falseSystem.out.println(customMap.containsValue("123"));/************************************isEmpty()*****************************************///falseSystem.out.println(customMap.isEmpty());}}

五、HashMap简介

HashMap也是存放键值对的结构,HashMap底层是通过数组+链表的方式实现的,通过键的hashcode确定在数组的某个位置,如果多个hashcode相同,那么这些都会存放到数组里面对应的一条链表上,链表里面存放是我们的Entry对象,即键值对。画了个简图:

六、自定义HashMap(效率高,使用hashcode)

package wsh.version2;import java.util.LinkedList;/*** 自定义Map(通过hashcode/equals方法实现,效率高)** @author weishihuai* @date 2018/10/1 16:05* <p>* 说明:* Map底层结构是通过(数组 + 链表)实现,只是帮助理解一下Map大概实现原理,没必要自定义Map*/
public class CustomMap {private LinkedList[] array = new LinkedList[1000];private int size;/*** 往Map中插入键值对元素** @param key   键* @param value 值*/public void put(Object key, Object value) {CustomEntry customEntry = new CustomEntry(key, value);int hashCode = key.hashCode();//根据hashCode计算出在bucket[]数组中的位置int index = hashCode % 1000;if (null == array[index]) {//空链表LinkedList linkedList = new LinkedList();linkedList.add(customEntry);array[index] = linkedList;} else {//不是空链表,拿出链表进行遍历,如果键值相同,值会被覆盖LinkedList list = array[index];for (int i = 0; i < list.size(); i++) {CustomEntry customEntry2 = (CustomEntry) list.get(i);if (customEntry2.getKey().equals(key)) {//键值重复直接覆盖customEntry2.setValue(value);return;}}array[index].add(customEntry);}size++;}/*** 根据key找到对应的值** @param key 键* @return 值* <p>* 原理: 根据key的hashCode计算出位置,如果该位置对应的链表不为空,循环遍历链表,找到对应的key,返回对应的值。如果未找到则返回null*/public Object get(Object key) {int index = key.hashCode() % 1000;if (null != array[index]) {LinkedList list = array[index];for (int i = 0; i < list.size(); i++) {CustomEntry customEntry = (CustomEntry) list.get(i);if (customEntry.getKey().equals(key)) {return customEntry.getValue();}}}return null;}public int size() {return size;}
}

七、部分方法详解

【a】 put(Object key, Object value) {}   往HashMap中插入新的键值对

1. 实现原理:通过 key.hashCode()计算出hashcode, 然后根据某种规则算出一个索引值,这个索引对应在bucket[]数组中的位置

a. 如果索引对应元素是一个空链表,那么新建一个LinkedList,把键值对存放到链表中。

b. 如果索引对应元素不是一个空链表,那么直接通过数组【index】取出链表对象,遍历链表,如果键值相同,值会被覆盖,并将键值对加入到已有的链表中。

【b】get(Object key) {} 根据key找到对应的值

1. 实现原理:  根据key的hashCode计算出位置,如果该位置对应的链表不为空,循环遍历链表,找到对应的key,返回对应的值。如果未找到则返回null。

八、测试

package wsh.version2;/*** 测试自定义Map** @author weishihuai* @date 2018/10/01 16:30*/
public class TestCustomMap {public static void main(String[] args) {CustomMap customMap = new CustomMap();customMap.put("name", "zhangsan");customMap.put("name", "lisi");//lisiSystem.out.println(customMap.get("name"));//1System.out.println(customMap.size());}}

九、总结

以上通过两个示例以及一些常用方法的讲解,相信可以帮助大家理解一下HashMap底层大概实现的原理,这里只实现了部分方法,有些方法有些问题暂时没有考虑。本文是作者在学习HashMap底层实现原理的一些总结以及方法讲解,仅供大家学习参考,一起学习一起进步。

Java集合Collection之实现原理解读(Map)相关推荐

  1. 【腾讯面试题】Java集合:List、Set以及Map

    Java集合:List.Set以及Map 概述 Collection接口 List:有序,可重复 ArraysList Vector LinkedList Set:无序,唯一 HashSet Link ...

  2. Java集合框架总结(5)——Map接口的使用

    Java集合框架总结(5)--Map接口的使用 Map用于保存具有映射关系的数据(key-vlaue).Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回fa ...

  3. Java集合Collection接口中的常用方法演示

    Java集合Collection接口中的常用方法演示 添加 add(Objec tobj) 和 addAll(Collection coll) 获取有效元素的个数 int size() 清空集合 vo ...

  4. Java集合框架概述(四)——Map体系集合与底层实现原理

    一.Map父接口 1.概要 方法 描述 public interface Map<K,V> 将键映射到值的对象. 一个映射不能包含重复的键; 每个键可以映射到最多一个值.  将键映射到值的 ...

  5. java 先进先出的map_「 深入浅出 」java集合Collection和Map

    本系列文章主要对java集合的框架进行一个深入浅出的介绍,使大家对java集合有个深入的理解. 本篇文章主要具体介绍了Collection接口,Map接口以及Collection接口的三个子接口Set ...

  6. Java—一篇读懂java集合(Collection/Map)及Lambda表达式

    集合简介   在集合类之前,我们使用数组存储,数组既可以存储基本数据类型的值,也可以存储对象(对象的引用变量),但是集合只能存储对象.   Java集合类似于一种容器,将同类型的对象(实际为对象引用) ...

  7. 考考基础部分,谈谈Java集合中HashSet的原理及常用方法

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:工匠初心 cnblogs.com/LiaHon/p/1125 ...

  8. Java集合Collection与List的关系、常见用法

    关系树 [java] view plain copy print? ---|Collection: 单列集合 ---|List: 有存储顺序, 可重复 ---|ArrayList: 数组实现, 查找快 ...

  9. java集合Collection常用方法详解

    前言 出去面试的时候,对java的集合框架考察的知识点还是蛮多的.除了基础的集合常见API使用,对集合底层的实现原理以及数据结构等也有很多考察方面.而自己对这方面知之甚少,特地抽空进行研究和学习一下. ...

  10. Java 集合Collection常见知识点汇总~

    看了一些所谓大公司的JAVA面试问题,发现对于JAVA集合类的使用都比较看重似的,而自己在这方面还真的是所真甚少,抽空也学习学习吧. java.util包中包含了一系列重要的集合类,而对于集合类,主要 ...

最新文章

  1. java无阻塞执行脚本,JAVA调用Shell脚本-及阻塞的解决方法
  2. 简述JavaME,JavaSE,JavaEE
  3. 并发编程6 锁进程队列
  4. forward 和redirect
  5. android hook截取其他程序的按钮事件_Hook技术
  6. LeetCode 1418. 点菜展示表(哈希map)
  7. Python打印某范围内的素数
  8. 树莓派添加USB外接硬盘
  9. C# 客户端时间校准
  10. pandas项目中使用的一些代码总结
  11. HTML输入=“文件”接受属性文件类型(CSV)
  12. 张宇1000题高等数学 第十三章 多元函数微分学
  13. Everything本地文件检索 快速搜索/共享神器
  14. 目标检测M2Det论文总结
  15. 导航网站合集 | 你想要的资源它都有
  16. python入门教材 52pj_PJzhang:python基础入门的7个疗程-five
  17. java实现缩放图像、切割图像、图像类型转换、彩色转黑白、文字水印、图片水印等
  18. 广州软博前端实习生面经
  19. 使用ROS开源代码和激光雷达进行小车的定位导航
  20. 企业微信服务商扫码登录

热门文章

  1. mmlspark-101: TrainClassifier
  2. 阿里云云计算助理工程师认证(ACA)50个资源合集和备考题库
  3. 阿里云云计算 33 PolarDB的优势 产品架构
  4. 2021-09-03101. 对称二叉树
  5. pyqt5 窗口设置圆角_pyqt5-30. QWidget设置圆角,使用qss设置失败
  6. python中如何导入图片_python如何导入图片
  7. html显示实时时间_珠海体育场LED显示屏实时播放比赛时间
  8. 聚类算法 optics
  9. 【Codeforces Round #291 (Div. 2) C】Watto and Mechanism【Trie树、Hash】
  10. QT重装系统后的程序异常处理与Windows奔溃导致无法开机的资料备份