HashSet內存泄漏

最近在梳理自己所學的Java知識,但是在看到HashSet的時,閒着無聊所做的測試發現原本以爲的成員不會重複的HashSet卻成功添加了重複的元素。代碼如下:

package com.holmes.learn.jihe;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class MyData {
	
	public static void main(String[] args) {
		
		Set<MyData> set = new HashSet<>();
		
		MyData data1 = new MyData("1", "1");
		MyData data2 = new MyData("2", "2");
		MyData data3 = new MyData("1", "1");
		
		set.add(data1);
		set.add(data2);
		set.add(data3);
		
		Iterator<MyData> iterator = set.iterator();
		while (iterator.hasNext()) {
			MyData data = iterator.next();
			System.out.println(data + " " + data.hashCode());
		}
		
		System.out.println();
		
		data1.setId("4");
		set.add(data1);
		
		iterator = set.iterator();
		while (iterator.hasNext()) {
			MyData data = iterator.next();
			System.out.println(data + " " + data.hashCode());
		}
		System.out.println();
		
		set.add(data3);
		
		iterator = set.iterator();
		while (iterator.hasNext()) {
			MyData data = iterator.next();
			System.out.println(data + " " + data.hashCode());
		}
	}

	
	private String id;
	private String name;
	
	public MyData(){
		
	}
	public MyData(String id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		MyData other = (MyData) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	
	@Override
	public String toString() {
		return "MyData [id=" + id + ", name=" + name + "]";
	}
}

MyData是一個包含id和name兩個String類型的成員變量的類,並通過兩個變量的值重寫了equals和hashCode方法。第一步向HashSet中插入三個元素:

MyData data1 = new MyData("1", "1");
MyData data2 = new MyData("2", "2");
MyData data3 = new MyData("1", "1");

很顯然,根據HashSet的元素不重複可以知道第一次打印的結果爲:

MyData [id=2, name=2] 2561
MyData [id=1, name=1] 2529

data1與data3的值相同,顯然data3無法添加成功。

第二步改變data1的值,再次向set中添加data1,並打印結果,發現了添加成功了。

data1.setId("4");
set.add(data1);
MyData [id=2, name=2] 2561
MyData [id=4, name=1] 2622
MyData [id=4, name=1] 2622

第三步把之前data3再次添加進去,並打印結果,發現再次添加成功。

set.add(data3);
MyData [id=2, name=2] 2561
MyData [id=4, name=1] 2622
MyData [id=1, name=1] 2529
MyData [id=4, name=1] 2622

通過以上實驗發現,HashSet中竟然出現了重複元素。這是爲什麼呢?

看過jdk源碼的都知道,HashSet元素不重複的功能是通過HashMap的key唯一來實現的。再向HashSet中添加對象時,首先通過hashCode值來判斷對象是否相同,如果不同,則直接向set中添加,相同則通過比較equals來比較元素是否相同。同時,hashSet通過hashCode的值來選擇將添加的元素放到相應的“位置”。

根據上述規則可以知道,代碼中,第一次向set中添加data1、data2與data3時,成功添加了data1和data2,但是在修改了data1的值後再次添加的過程中,向判斷元素的hashCode值對應的“位置”是否已有元素存在,顯然是沒有的,所以添加成功了。第三步中添加data3時,計算hashCode時發現“位置”已經被佔了,接着判斷equals的值,結果爲false,所以data3依然添加成功了。

總結:在使用HashSet存儲對象時,發生上述情況會導致內存泄漏,因此在使用時應當注意。當然,出現任何問題我們都能以我們所知道的知識來解釋問題、避免問題,並解決問題。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章