HashSet
参考文章:
https://blog.csdn.net/tingzhiyi/article/details/52152487
https://blog.csdn.net/xingjiyuan26/article/details/49514631
https://blog.csdn.net/a724888/article/details/80295328
-
定义
底层实现是采用哈希表,把对象根据哈希值存放到对应的区域,所以如果把对象存放到hashset中,则需要重写hashcode方法。 -
基本方法
hashset.clear():从此 set 中移除所有元素。
hashset.remove(Object o):如果指定元素存在于此 set 中,则将其移除。
hashset.isEmpty():如果此 set 不包含任何元素,则返回 true。
hashset.contains(Object o):如果此 set 包含指定元素,则返回 true。
hashset.size():返回此 set 中的元素的数量(set 的容量)。
- 特点:
- 可添加元素类型:基本类型和自定义对象都可以。
- 非线程安全
- 保存的数据是无序的
- 实现set接口,元素不可重复
- 如何判断筛选重复元素
首先,根据equals和hashcode两个方法的返回值是否相同来判断两个对象是否相同,而源代码上,由于hashset内部是根据hashmap作存储的,使用map的put方法来判断键是否存在(存在返回键值,不是返回空)
public boolean add(E e ) {
return map.put(e, PRESENT)== null;
}
- HashSet导致的内存泄漏
也就是当你保存一个对象,也重写了他的hashcode,然后当你存储该对象到你的hashset集合中时,你再修改了你的hashcode时,你会发现,你无法调用set的remove方法删除该对象,最终导致内存泄漏,看以下代码细节
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;
}
//重写hashcode
@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;
}
}
主函数测试
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
}
}
HashTree
原文链接https://blog.csdn.net/u012050154/article/details/51459679
-
定义
底层是基于红黑树实现的,源码使用TreeMap保存数据, -
常见的两种用法
- 基本类型
public class TreeSetTest {
public static void main(String[] args) {
Set<String> set = new TreeSet<String>();
set.add("abc");
set.add("xyz");
set.add("rst");
System.out.println(set);//可以直接输出
Iterator itSet = set.iterator();//也可以遍历输出
while(itSet.hasNext())
System.out.print(itSet.next() + "\t");
System.out.println();
}
}
- 自定义对象(只需要记得在自定义对象中实现Comparable方法即可)
//自定义数据类型,并在自定义的数据类型中实现CompareTo方法
class Teacher implements Comparable {
int num;
String name;
Teacher(String name, int num) {
this.num = num;
this.name = name;
}
public String toString() {
return "学号:" + num + " 姓名:" + name;
}
public int compareTo(Object o) {
Teacher ss = (Teacher) o;
int result = num > ss.num ? 1 : (num == ss.num ? 0 : -1);
if (result == 0) {
result = name.compareTo(ss.name);
}
return result;
}
}
public class TreeSetTest {
public static void main(String[] args) {
Set<Teacher> treeSet = new TreeSet<Teacher>();
treeSet.add(new Teacher("zhangsan", 2));
treeSet.add(new Teacher("lisi", 1));
treeSet.add(new Teacher("wangwu", 3));
treeSet.add(new Teacher("mazi", 3));
System.out.println(treeSet);//直接输出
Iterator itTSet = treeSet.iterator();//遍历输出
while(itTSet.hasNext())
System.out.print(itTSet.next() + "\t");
System.out.println();
}
}
- 特点
- 不允许null值
- 不允许重复
- 有序的
- 筛选重复数据方法
依靠Comparable接口返回的是不是0来判断。
LinkedHashSet(HashSet的扩展)
-
定义:底层是 数组 + 单链表 + 红黑树 + 双向链表
-
特点:
- LinkedHashSet 存储元素是无序的,但是由于双向链表的存在,迭代时获取元素的顺序等于元素的添加顺序,注意这里不是访问顺序
- LinkedHashSet并没有自己的方法,所有的方法都继承自它的父类HashSet,因此,对LinkedHashSet的所有操作方式就好像对HashSet操作一样。
- 如何维护插入顺序
LinkedHashSet使用LinkedHashMap对象来存储它的元素,插入到LinkedHashSet中的元素实际上是被当作LinkedHashMap的键保存起来的。
LinkedHashMap的每一个键值对都是通过内部的静态类Entry<K, V>实例化的。这个 Entry<K, V>类继承了HashMap.Entry类。
这个静态类增加了两个成员变量,before和after来维护LinkedHasMap元素的插入顺序。这两个成员变量分别指向前一个和后一个元素,这让LinkedHashMap也有类似双向链表的表现。
总结
- 都是不可重复的,hashset无序,treeset有序
- LinkedHashSet就是hashset的可维护插入顺序版本
- 去重的内部实现方式不同
- treeset允许null