集合(二)-HashSet和TreeSet和LinkedHashSet

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

  1. 定义
    底层实现是采用哈希表,把对象根据哈希值存放到对应的区域,所以如果把对象存放到hashset中,则需要重写hashcode方法。

  2. 基本方法

hashset.clear():从此 set 中移除所有元素。

hashset.remove(Object o):如果指定元素存在于此 set 中,则将其移除。

hashset.isEmpty():如果此 set 不包含任何元素,则返回 true。

hashset.contains(Object o):如果此 set 包含指定元素,则返回 true。

hashset.size():返回此 set 中的元素的数量(set 的容量)。
  1. 特点
  • 可添加元素类型:基本类型和自定义对象都可以。
  • 非线程安全
  • 保存的数据是无序的
  • 实现set接口,元素不可重复
  1. 如何判断筛选重复元素
    首先,根据equals和hashcode两个方法的返回值是否相同来判断两个对象是否相同,而源代码上,由于hashset内部是根据hashmap作存储的,使用map的put方法来判断键是否存在(存在返回键值,不是返回空)
public boolean add(E e ) {
        return map.put(e, PRESENT)== null;
    }
  1. 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

  1. 定义
    底层是基于红黑树实现的,源码使用TreeMap保存数据,

  2. 常见的两种用法

  • 基本类型
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();  
    }   
}  
  1. 特点
  • 不允许null值
  • 不允许重复
  • 有序的
  1. 筛选重复数据方法
    依靠Comparable接口返回的是不是0来判断。

LinkedHashSet(HashSet的扩展)

  1. 定义:底层是 数组 + 单链表 + 红黑树 + 双向链表

  2. 特点

  • LinkedHashSet 存储元素是无序的,但是由于双向链表的存在,迭代时获取元素的顺序等于元素的添加顺序,注意这里不是访问顺序
  • LinkedHashSet并没有自己的方法,所有的方法都继承自它的父类HashSet,因此,对LinkedHashSet的所有操作方式就好像对HashSet操作一样。
  1. 如何维护插入顺序
    LinkedHashSet使用LinkedHashMap对象来存储它的元素,插入到LinkedHashSet中的元素实际上是被当作LinkedHashMap的键保存起来的。

LinkedHashMap的每一个键值对都是通过内部的静态类Entry<K, V>实例化的。这个 Entry<K, V>类继承了HashMap.Entry类。

这个静态类增加了两个成员变量,before和after来维护LinkedHasMap元素的插入顺序。这两个成员变量分别指向前一个和后一个元素,这让LinkedHashMap也有类似双向链表的表现。

总结

  1. 都是不可重复的,hashset无序,treeset有序
  2. LinkedHashSet就是hashset的可维护插入顺序版本
  3. 去重的内部实现方式不同
  4. treeset允许null
发布了36 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/s_xchenzejian/article/details/101512987