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

来源:互联网 发布:软件系统质量指标 编辑:程序博客网 时间:2024/06/16 11:04
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     }}

输出结果如下:

22falsefalsetruetrue1

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

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

1 0
原创粉丝点击