0. 背景

HashMap 是Java中非常常用的一个集合类,它是一个Map接口的数据结构的实现。它的性能很好,查询对象只需要O(1)时间复杂度。
尽管HashMap使用非常方便,但是如果使用不当,可能会导致内存泄漏的。以下章节将介绍 HashMap可能导致内存泄漏的情况,以及如何解决该问题。

1. HashMap 的存储

首先,我们先明确HashMap的内部是如何存储数据的。
HashMap是一个用于存储Key-Value 对结构的集合类。Key可以是任意一个Java 的对象,甚至是可以是null。
那么,HashMap的内部是怎么存储KV对的呢?很简单,就是用数组存放的。内部定义了一个Node类表示每一个 KV对,然后使用一个数组来存放所有的Node对象。
每新增一个KV对,就会用Key 的hashCode 计算出一个数组的下标index。然后用Key 和Value创建一个Node对象,放在之前计算出的下标上。简言之,就是用一个数组存放数据,存放的位置是根据Key的hashCode计算出来的。这也就是它被称为HashMap的原因。(实际的实现要相对复杂,这里只是大致的原理)

2. HashMap使用不当导致的内存泄露

在上一章节,我们也简单分析了HashMap的存储原理,从它的原理分析有一个很重要的信息:
HashMap中数据在数组中的存放位置,是取决于Key对象的 hashCode() 方法的。
这个信息原本是没什么问题,可以我们做java 的都知道,一个对象的hashCode() 方法的值,一般来说都是和对象的内容相关的。那么,如果Key对象的成员取值变化了,它的hashCode() 基本上也会变化。
这样就会有问题了。
在存放进去的时候,和取出数值的时候,都是依赖Key 对象的hashCode计算的。
而hashCode 的计算,则是由类中定义的,根据hashCode的定义规范,重写hashCode方法之后,如果类的成员数值变化了,那么它的hashCode基本上也是会变化的。
设想一下,一个KV对被put 到HashMap中的时候,hashCode 是A,然后这个时候修改了Key的内容,使得它的hashCode变化了;那么接下来再用 get 方法获取这个 Value 时,会用这个Key重新计算hashCode,然后去内部查找,由于hashCode已经变化了,所以这时是找不到的。下面看一个例子:
/** * 定义一个类 */classPoint{intx;inty;@OverridepublicinthashCode(){// 重写hashCode实现。随便写的一个hashCode实现 return17+x*23+y+100;}}publicstaticvoidmain(String[]args){Pointp1=newPoint();p1.x=100;p1.y=100;Map