Java Set操做

2021年09月15日 阅读数:9
这篇文章主要向大家介绍Java Set操做,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。
Set:无顺序,不包含重复的元素

HashSet:为快速查找设计的Set。存入HashSet的对象必须定义hashCode()。  html

TreeSet: 保存次序的Set, 底层为树结构。使用它能够从Set中提取有序的序列。  java

LinkedHashSet:具备HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序)。因而在使用迭代器遍历Set时,结果会按元素插入的次序显示。算法

HashSet:安全

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

public class Main {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        //分别向books集合中添加两个A对象,两个B对象,两个C对象
        set.add(new A());
        set.add(new A());

        set.add(new B());
        set.add(new B());

        set.add(new C());
        set.add(new C());
        System.out.println(set);
        System.out.println(set.size());
        for(Iterator iter = set.iterator(); iter.hasNext();){
            Object value =  iter.next();
            System.out.println(value);
        }
    }
}

//类A的equals方法老是返回true,但没有重写其hashCode()方法。不能保证当前对象是HashSet中的惟一对象
class A
{
    public boolean equals(Object obj)
    {
        return true;
    }
}

//类B的hashCode()方法老是返回1,但没有重写其equals()方法。不能保证当前对象是HashSet中的惟一对象
class B
{
    public int hashCode()
    {
        return 1;
    }
}

//类C的hashCode()方法老是返回2,且有重写其equals()方法
class C
{
    public int hashCode()
    {
        return 2;
    }
    public boolean equals(Object obj)
    {
        return true;
    }
}

Result:数据结构

[com.qhong.B@1, com.qhong.B@1, com.qhong.C@2, com.qhong.A@677327b6, com.qhong.A@1540e19d]
5
com.qhong.B@1
com.qhong.B@1
com.qhong.C@2
com.qhong.A@677327b6
com.qhong.A@1540e19d
View Code

B类的hashcode值同样,可是没有定义equals,因此存储了两个对象,可是对象由于hashcode同样,存储位置也同样,两个对象按链表方式存储。ide

C类中hashcode值同样,equals也同样,因此只能存储一个值。工具

A类中没有定义hashcode,因此存储位置不同,这里尚未进行equals对比,同样存储两个对象。性能

LinkedHashSetthis

import java.util.Iterator;
import java.util.LinkedHashSet;

public class Main {
    public static void main(String[] args) {
        LinkedHashSet books = new LinkedHashSet();
        books.add("Java1");
        books.add("Java2");
        System.out.println(books);

        //删除 Java
        books.remove("Java1");
        //从新添加 Java
        books.add("Java1");
        System.out.println(books);
        for(Iterator iter = books.iterator(); iter.hasNext();){
            Object value =  iter.next();
            System.out.println(value);
        }
    }
}

Result:es5

[Java1, Java2]
[Java2, Java1]
Java2
Java1
View Code

元素的顺序老是与添加顺序一致,LinkedHashSetTest是HashSet的子类,所以它不容许集合元素重复.

TreeSet:

import java.util.TreeSet;

public class Main {
    public static void main(String[] args) {
        TreeSet nums = new TreeSet();
        //向TreeSet中添加四个Integer对象
        nums.add(5);
        nums.add(2);
        nums.add(10);
        nums.add(-9);

        //输出集合元素,看到集合元素已经处于排序状态
        System.out.println(nums);

        //输出集合里的第一个元素
        System.out.println(nums.first());

        //输出集合里的最后一个元素
        System.out.println(nums.last());

        //返回小于4的子集,不包含4
        System.out.println(nums.headSet(4));

        //返回大于5的子集,若是Set中包含5,子集中还包含5
        System.out.println(nums.tailSet(5));

        //返回大于等于-3,小于4的子集。
        System.out.println(nums.subSet(-3 , 4));
    }
}

Result:

[-9, 2, 5, 10]
-9
10
[-9, 2]
[5, 10]
[2]
View Code

与HashSet集合采用hash算法来决定元素的存储位置不一样,TreeSet采用红黑树的数据结构来存储集合元素。

TreeSet支持两种排序方式: 天然排序、定制排序

TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,而后将集合元素按升序排序,即天然排序。

经过ComparaTor接口能够实现定制排序。

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class Main {
    public static void main(String[] args) {
        TreeSet ts = new TreeSet(new Comparator()
        {
            //根据M对象的age属性来决定大小
            public int compare(Object o1, Object o2)
            {
                M m1 = (M)o1;
                M m2 = (M)o2;
                return m1.age > m2.age ? -1 : m1.age < m2.age ? 1 : 0;
            }
        });
        ts.add(new M(5));
        ts.add(new M(-3));
        ts.add(new M(9));
        System.out.println(ts);
        for(Iterator iter = ts.iterator(); iter.hasNext();){
            Object value =  iter.next();
            System.out.println(value);
        }
    }
}
class M
{
    int age;
    public M(int age)
    {
        this.age = age;
    }
    public String toString()
    {
        return "M[age:" + age + "]";
    }
}

Result:

[M[age:9], M[age:5], M[age:-3]]
M[age:9]
M[age:5]
M[age:-3]
View Code

EnumSet:

import java.util.EnumSet;

public class Main {
    public static void main(String[] args) {
        //建立一个EnumSet集合,集合元素就是Season枚举类的所有枚举值
        EnumSet es1 = EnumSet.allOf(Season.class);
        //输出[SPRING,SUMMER,FALL,WINTER]
        System.out.println(es1);

        //建立一个EnumSet空集合,指定其集合元素是Season类的枚举值。
        EnumSet es2 = EnumSet.noneOf(Season.class);
        //输出[]
        System.out.println(es2);
        //手动添加两个元素
        es2.add(Season.WINTER);
        es2.add(Season.SPRING);
        //输出[SPRING,WINTER]
        System.out.println(es2);

        //以指定枚举值建立EnumSet集合
        EnumSet es3 = EnumSet.of(Season.SUMMER , Season.WINTER);
        //输出[SUMMER,WINTER]
        System.out.println(es3);

        EnumSet es4 = EnumSet.range(Season.SUMMER , Season.WINTER);
        //输出[SUMMER,FALL,WINTER]
        System.out.println(es4);

        //新建立的EnumSet集合的元素和es4集合的元素有相同类型,
        //es5的集合元素 + es4集合元素 = Season枚举类的所有枚举值
        EnumSet es5 = EnumSet.complementOf(es4);
        //输出[SPRING]
        System.out.println(es5);
    }
}
enum Season
{
    SPRING,SUMMER,FALL,WINTER
}

Result:

[SPRING, SUMMER, FALL, WINTER]
[]
[SPRING, WINTER]
[SUMMER, WINTER]
[SUMMER, FALL, WINTER]
[SPRING]
View Code

Set集合类应用场景:

1) HashSet的性能老是比TreeSet好(特别是最经常使用的添加、查询元素等操做),由于TreeSet须要额外的红黑树算法来维护集合元素的次序。只有当须要一个保持排序的Set时,才应该使用TreeSet,不然都应该使用HashSet
2) 对于普通的插入、删除操做,LinkedHashSet比HashSet要略慢一点,这是由维护链表所带来的开销形成的。不过,由于有了链表的存在,遍历LinkedHashSet会更快
3) EnumSet是全部Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值做为集合元素
4) HashSet、TreeSet、EnumSet都是"线程不安全"的,一般能够经过Collections工具类的synchronizedSortedSet方法来"包装"该Set集合。
SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));

http://www.cnblogs.com/LittleHann/p/3690187.html