hashmap怎么取值_【Java实践】HashMap使用不当导致内存泄漏问题

4832146fe9e77524ec558d7a996d5ed5.png

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已经变化了,所以这时是找不到的。下面看一个例子:

/**
* 定义一个类
*/
class Point{
    int x;
    int y;

    @Override
    public int hashCode() {
        // 重写hashCode实现。随便写的一个hashCode实现
        return 17 + x * 23 + y +100;
    }
}

public static void main (String[] args){
    Point p1 = new Point();
    p1.x = 100;
    p1.y = 100;

    Map<Point, String> map = new HashMap<>();
    map.put(p1, "Point p1");

    // 修改p1的内容,会导致hashCode 变化
    p1.x = 50;
    // result 是null
    String result = map.get(p1);
}

从以上例子中,可以看到,一个Point 类对象被当作Key放进了HashMap,但是后来却获取不到了。如果在外部已经没有对 p1 变量的引用的话,那么这个在HashMap内部存在的它曾经对应的Node,将会导致内存泄漏。它会因为HashMap的持有而无法被GC回收,但是用HashMap却也获取不到它。

总结一下,按照下面的场景使用HashMap的话,会出现内存泄漏的问题:

1. 作为Key的类有自定义的 hashCode() 方法,并且该方法的返回值受Key的类成员影响;

2. 在一个Key被put 到HashMap中以后,它的内部成员值发生了变化,并且导致了 hashCode 的结果变化;

3. 如何解决该问题?

从以上的分析可以知道,该问题的本质原因,在一个Key被放进HashMap 和从 HashMap 取出时,两次计算它的 hashCode 不一致导致的。所以要避免这个问题,就需要保证一个 Key 在 put 和 get 时,它的hashCode 保持不变。

具体实现为:

1. 保证作为HashMap 的Key 的对象是不可变的。也就是使用只读多对象来当作Key;

2. 如果要使用一个类当作HashMap的key,同时也要修改它的内容。那么可以重写hashCode,保证hashCode 的生成只和某些不可变的成员相关。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中可以使用HashMap的不同方法来取得HashMap中的值。具体如下: 方法一:使用keySet()方法获取键的集合,然后通过遍历集合获取对应的值。可以通过map.get(key)方法来获取对应的value。例如: ``` HashMap<String, Integer> map = new HashMap<>(); map.put("Kobe", 8); map.put("Jordan", 23); map.put("James", 6); map.put("Curry", 30); map.put("Durant", 35); Set<String> keySet = map.keySet(); for (String key : keySet) { System.out.println(key + " " + map.get(key)); } ``` 方法二:使用entrySet()方法获取键值对的集合,然后通过遍历集合获取每个键值对的key和value。可以通过entry.getKey()和entry.getValue()方法来获取对应的key和value。例如: ``` HashMap<String, Integer> map = new HashMap<>(); map.put("Kobe", 8); map.put("Jordan", 23); map.put("James", 6); map.put("Curry", 30); map.put("Durant", 35); Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); for (Map.Entry<String, Integer> entry : entrySet) { System.out.println(entry.getKey() + " " + entry.getValue()); } ``` 方法三:使用values()方法获取值的集合,然后通过遍历集合获取每个值。例如: ``` HashMap<String, Integer> map = new HashMap<>(); map.put("Kobe", 8); map.put("Jordan", 23); map.put("James", 6); map.put("Curry", 30); map.put("Durant", 35); Collection<Integer> values = map.values(); for (Integer value : values) { System.out.println(value); } ``` 以上就是使用HashMap取值的几种方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Java HashMap取值的几种方式](https://blog.csdn.net/qq_41473638/article/details/126928243)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值