从头认识java-15.3 使用HashSet须要注意的地方

这一章节我们来讨论一下使用Set的各种实现须要注意的地方。

Set接口的经常使用实现类有:HashSet。TreeSet,LinkedHashSet

1.HashSet

大家对于HashSet的印象都是它能够去除反复的元素,每个元素都是唯一的,可是这里面有一个前提。就是必须重写equals和hashcode方法。

大家的印象大都是以下这个:

package com.ray.ch15;

import java.util.HashSet;

public class Test {
	public static void main(String[] args) {
		HashSet<Integer> set = new HashSet<Integer>();
		set.add(1);
		set.add(2);
		set.add(3);
		for (int i = 0; i < 10; i++) {
			set.add(i);
		}
		System.out.println(set);
	}
}

输出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

事实上当中的原因是,set里面的equals和hashcode方法都继承了Object里面的。而Object里面的这两个方法刚好能够比較easy的对照java的基础类型,甚至java里面大部分的类型。由于大部分类型都已经重写了equals和hashcode方法。

那么,我们以下自己定义一下自己的类型看看:

package com.ray.ch15;

import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Set;

public class Test<T> {
	public static <T> Set<T> fill(Set<T> set, Class<T> type)
			throws InstantiationException, IllegalAccessException,
			IllegalArgumentException, SecurityException,
			InvocationTargetException, NoSuchMethodException {
		for (int i = 0; i < 10; i++) {
			set.add(type.getConstructor(int.class).newInstance(i));
		}
		return set;
	}

	public static <T> void test(Set<T> set, Class<T> type)
			throws IllegalArgumentException, SecurityException,
			InstantiationException, IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {
		fill(set, type);
		fill(set, type);
		fill(set, type);
		System.out.println(set);
	}

	public static void main(String[] args) throws IllegalArgumentException,
			SecurityException, InstantiationException, IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {
		test(new HashSet<SetType>(), SetType.class);
		test(new HashSet<HashType>(), HashType.class);
	}
}

class SetType {
	private int id = 0;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public SetType(int i) {
		id = i;
	}

	@Override
	public String toString() {
		return id + "";
	}

	@Override
	public boolean equals(Object obj) {
		return obj instanceof SetType && (id == ((SetType) obj).id);
	}
}

class HashType extends SetType {

	public HashType(int i) {
		super(i);
	}

	@Override
	public int hashCode() {
		return getId();
	}
}

输出:

[6, 6, 3, 4, 9, 5, 2, 9, 1, 7, 5, 4, 1, 2, 9, 3, 7, 8, 8, 0, 2, 0, 4, 6, 7, 5, 0, 1, 3, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

解释一下上面的代码:

(1)fill方法:通过泛型和反射。往传进来的set里面加入数据

(2)test方法:我们通过多次的往set里面填充数据,看看set是否去重

(3)SetType:原始类型,仅仅是简单实现了equals方法和toString方法,toString这里通过输出id来表示对象

(4)HashType:继承SetType,再实现了hashCode方法

注意点:

(1)从输出我们能够看见。在main的第一个test方法,它的输出是许多的对象id,并且里面出现了许多的反复。这是由于我们没有重写hashCode方法,在Object的hashCode方法里面。每个对象返回的hashcode都不一样。jvm认定都是不同的对象,因此我们加入多少对象进去,就会显示多少对象

(2)接着上面的解释,以下的HashType重写了hashCode方法。以id为标准,从上面的代码我们能够看见,id很可能反复,因此jvm就会去重。

我们能够翻开HashSet的源码,看看add方法:

private transient HashMap<E,Object> map;
public boolean add(E e) {
	return map.put(e, PRESENT)==null;
    }

对象是放到map里面,我们在翻开map的put方法:

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

它里面是对照了hash值的,因此HashSet想实现去重,必须重写equals和hashcode方法。

(3)由于上面产生的值比較少,因此排序的特殊性没有表露出来。当我们添加创建的对象时。排序就会依照一定的顺序排列,而不是依照我们想象的顺序。

关于这样的排序,我们后面的章节会说到。

以下是样例代码:

package com.ray.ch15;

import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Set;

public class Test<T> {
	public static <T> Set<T> fill(Set<T> set, Class<T> type)
			throws InstantiationException, IllegalAccessException,
			IllegalArgumentException, SecurityException,
			InvocationTargetException, NoSuchMethodException {
		for (int i = 0; i < 20; i++) {
			set.add(type.getConstructor(int.class).newInstance(i));
		}
		return set;
	}

	public static <T> void test(Set<T> set, Class<T> type)
			throws IllegalArgumentException, SecurityException,
			InstantiationException, IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {
		fill(set, type);
		fill(set, type);
		fill(set, type);
		System.out.println(set);
	}

	public static void main(String[] args) throws IllegalArgumentException,
			SecurityException, InstantiationException, IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {
		test(new HashSet<HashType>(), HashType.class);
	}
}

class SetType {
	private int id = 0;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public SetType(int i) {
		id = i;
	}

	@Override
	public String toString() {
		return id + "";
	}

	@Override
	public boolean equals(Object obj) {
		return obj instanceof SetType && (id == ((SetType) obj).id);
	}
}

class HashType extends SetType {

	public HashType(int i) {
		super(i);
	}

	@Override
	public int hashCode() {
		return getId();
	}
}

输出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 16, 19, 18]

总结:这一章节主要讲述了使用HashSet须要注意的地方。

这一章节就到这里,谢谢。

-----------------------------------

文件夹

时间: 2024-08-29 16:57:05

从头认识java-15.3 使用HashSet须要注意的地方的相关文章

[Java]#从头学Java# Java大整数相加

重操旧业,再温Java,写了个大整数相乘先回顾回顾基本知识.算法.效率什么的都没怎么考虑,就纯粹实现功能而已. 先上代码: 1 package com.tacyeh.common; 2 3 public class MyMath { 4 5 public static String BigNumSum(String... n) { 6 int length = n.length; 7 StringBuilder result = new StringBuilder(); 8 //这里判断其实不需

从头认识java-15.3 使用HashSet需要注意的地方

这一章节我们来讨论一下使用Set的各种实现需要注意的地方. Set接口的常用实现类有:HashSet,TreeSet,LinkedHashSet 1.HashSet 大家对于HashSet的印象都是它可以去除重复的元素,每一个元素都是唯一的,但是这里面有一个前提,就是必须重写equals和hashcode方法. 大家的印象大都是下面这个: package com.ray.ch15; import java.util.HashSet; public class Test { public stat

Java 集合系列 16 HashSet

java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 Java 集合系列 05 Vector详细介绍(源码解析)和使用示例 Java 集合系列 06 Stack详细介绍(源码解析)和使用示例 Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和

java源码之HashSet

1,HashSet介绍 1)HashSet 是一个没有重复元素的集合.2)它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素.3)HashSet是非同步的.如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步.这通常是通过对自然封装该 set 的对象执行同步操作来完成的.如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来"包装" set.最好在创建时完成这一

Java编程中尽可能要做到的一些地方

最近的机器内存又爆满了,除了新增机器内存外,还应该好好review一下我们的代码,有很多代码编写过于随意化,这些不好的习惯或对程序语言的不了解是应该好好打压打压了. 下面是参考网络资源总结的一些在Java编程中尽可能要做到的一些地方. 1.尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面: 控制资源的使用,通过线程同步来控制资源的并发访问: 控制实例的产生,以达到节约资源的目的: 控制数据共享

研究 研究而已 java和.net的HashSet对比

今天,应为工作问题,测试了一下C#和java同意的代码功能执行情况,发现一个问题. HashSet.contains 方法对比,在java下面性能居然没有c#的高. 1 private static final Logger log = Logger.getLogger(NewClass.class); 2 3 public static void main(String[] args) { 4 for (int j = 0; j < 5; j++) { 5 HashSet<Integer&g

java集合框架(hashSet自定义元素是否相同,重写hashCode和equals方法)

/*HashSet 基本操作 * --set:元素是无序的,存入和取出顺序不一致,元素不可以重复 * (通过哈希值来判断是否是同一个对象) * ----HashSet:底层数据结构是哈希表, * 保证数据唯一性的方法是调用存入元素的hashCode()方法 * 和equals(Object obj)方法 * HashCode值相同,才会调用equals方法 * * */ 1 import java.util.HashSet; 2 import java.util.Iterator; 3 publ

java 15 -7 ListIterator 的特有方法

列表迭代器:  ListIterator listIterator():List集合特有的迭代器 该迭代器继承了Iterator迭代器,所以,就可以直接使用hasNext()和next()方法. 特有功能: A: boolean hasPrevious():判断是否有元素  B:Object previous():获取上一个元素 注意:ListIterator可以实现逆向遍历,但是必须先正向遍历,才能逆向遍历,所以一般无意义,不使用. 1 import java.util.ArrayList;

Java容器 HashMap与HashSet的学习

Java学习中,看到HashMap,HashSet类,本着不止要停留在用的层面( 很多公司面试都要问底层 ),学习了JDK源码,记录下笔记. 源码来自jdk1.7下的src.zip HashMap是一种键值对类型,它提供一种Key-Value对应保存的数据结构,实现了Map接口,其中key的值唯一,即一个key某一时刻只能映射到唯一的值. 看其中几个成员(没列全) static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 sta