java中的HashSet和TreeSet的区别,以及HashSet导致的内存泄漏详解

Set集合中的元素是无序的,不可重复的。这个接口下有两个常用集合的实现,HashSet和TreeSet。

HashSet

HashSet底层用的是哈希表,它把对象根据其哈希值存放到对应的区域里。由于这种特性,两个在不同区域的对象会被认为不相同的。
所以如果对象要存放到Hash集合里面,则需要重写对象的hashCode方法,让相等的对象的hashCode的值也相等。

TreeSet

TreeSet采用的数据结构是红黑树,我们可以让它按指定规则对其中的元素进行排序。它又是如何判断两个元素是否相同呢?除了用equals方法检查两个元素是否相同外,还要检查compareTo方法是否返回为0。
所以如果对象要存放到Tree集合里,需要在重写compareTo时,把相同的对象的比较值定为0,防止相同的元素被重复添加进集合中。

面试分享:
HashSet在源代码内是如何实现不add相同的元素的?

理论上我们都知道,它的内部是根据equals()返回值和hashCode()的值是否相同两个方法来判断两个对象是否相同的。而源代码上,HashSet内部用了一个HashMap作存储,add()方法内是调用了map的put()方法,map的put()方法会检查这个键是否已存在,若是则返回该键之前的值,并更新成新值,若不是则返回null,但这个键是不会发生改变的。所以,在源代码里,HashSet的add()方法是根据map的put返回值来判断添加元素是否成功的。

public boolean add(E e ) {
        return map.put(e, PRESENT)== null;
    }

HashSet导致的内存泄漏

延伸一下,hash集合在操作不当的情况下,有可能造成内存泄漏。
考虑这样的情况,把一个对象存储进hashSet集合后,修改这个对象中参与计算hash的变量的值,这时这个对象的hash值也会随之改变,那么这么对象可以正常地被删除吗?下面用代码试一下:

先自定一个MPoint类,其中有两个变量,x和y,其中x参与计算hash值
public class MPoint {
     private int x;
     private int y;

     public MPoint() {
     }

     public MPoint( int x, int y) {
            this. x = x;
            this. y = y;
     }

     public int getX() {
            return x;
     }

     public void setX(int x) {
            this. x = x;
     }

     public int getY() {
            return y;
     }

     public void setY(int y) {
            this. y = y;
     }

     @Override
     public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + x; //x参与计算hash值
            return result;
     }

     @Override
     public boolean equals(Object obj) {
            if ( this == obj)
                 return true;
            if ( obj == null)
                 return false;
            if (getClass() != obj.getClass())
                 return false;
           MPoint other = (MPoint) obj;
            if ( x != other. x)
                 return false;
            if ( y != other. y)
                 return false;
            return true;
     }
}

主函数类测试,在一个HashSet集合中添加三个元素,mp1、mp2、mp3,并进行其中属性的修改和输出测试
public class HashSetTest {

     public static void main(String[] args) {
           HashSet<MPoint> set = new HashSet<MPoint>();
           MPoint mp1 = new MPoint(1, 6);
           MPoint mp2 = new MPoint(2, 7);
           MPoint mp3 = new MPoint(1, 6);

            set.add( mp1);
            set.add( mp2);
            set.add( mp3);
            set.add( mp1);

           System. out.println( set.size()); // 结果为2

            mp1.setX(3);
            set.remove( mp1);
           System. out.println( set.size()); // 结果还是为2,说明没有删除成功
           System. out.println( set.contains( mp1)); // 结果为false,修改了参与计算hash值的变量,其对象不能再被找到
           System. out.println( set.remove( mp1)); // 结果为false,修改了参与计算hash值的变量,其对象不能被删除

            mp2.setY(2);
           System. out.println( set.contains( mp2)); // 结果为true,没有修改关键属性的对象可以被找到
           System. out.println( set.remove( mp2)); // 结果为true,没有修改关键属性的对象可以被删除
           System. out.println( set.size()); // 结果还是为1
     }
}

输出结果如下:

2
2
false
false
true
true
1

可以看出已经发生了内存泄漏了,mp1对象不能被正常删除。

总结:Java中也有内存泄漏发生,这个知识点或许在面试中会很有用,一定要能说出内存泄漏发生的场景这样的代码细节,才算是真正碰到过。。

  • 14
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值