Home Java集合框架 之 Set接口
Post
Cancel

Java集合框架 之 Set接口

特点: 元素无序不可重复。包含以下几个常用的实现类:

Hashset

  • 基于HashMap实现
  • 线程不安全
  • 元素无序,不依赖其添加至集合的顺序
  • 允许null值

构造方法

默认方法为:

1
HashSet<E> nums = new HashSet<>();

也可以在构造时指定 容量(capacity)负载因子(loadFactor)

1
Hashset<E> nums = new HashSet<>(8, 0.5);

这样创建的便是一个容量为8,负载因子为0.5的HashSet了。负载因子用于控制HashSet扩容的时机,例如这里为0.5代表着当HashSet被填充至总容量的50%时便会扩容,当前集合中的所有元素会转移到新的HashSet中。两个参数的默认值分别为160.75

常用方法

方法描述
add(E e)添加指定元素
remove(E e)删除指定元素
addAll(Object o)求并集
retainAll(Object o)求交集
removeAll(Object o)求差集
containsAll(Object o)判断传入的集合是否为当前集合的子集
isEmpty()判断集合是否为空
size()返回集合中元素的个数
contains(E e)判断指定元素是否在当前集合中
clear()清空集合

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import java.util.*;

public class hashset{
    public static void main(String[] args){
        /* initialization */
        HashSet<Integer> nums1 = new HashSet<>();
        HashSet<Integer> nums2 = new HashSet<>();
        HashSet<Integer> res = new HashSet<>();
        Integer[] elements = {1, 9, 5, 6, 0, 2, 7, 3, 8, 4};

        for (Integer e: elements){
            nums1.add(e);
            if (e % 2 == 0){
                nums2.add(e);
            }
        }
        print("nums1:", nums1);
        print("nums2:", nums2);
        System.out.println("");

        /* add */
        nums1.add(3);
        nums2.add(3);
        print("add 3 to nums1:", nums1);
        print("add 3 to nums2:", nums2);

        /* remove */
        nums1.remove(4);
        print("remove 4 from nums1:", nums1);
        
        printBlank();

        print("nums1:", nums1);
        print("nums2:", nums2);
        System.out.println("");

        /* union */
        res.addAll(nums1);
        res.addAll(nums2);
        print("nums1 | nums2:", res);
        res.clear();

        /* intersection */
        res.addAll(nums1);
        res.retainAll(nums2);
        print("nums1 & nums2:", res);
        res.clear();

        /* difference */
        res.addAll(nums1);
        res.removeAll(nums2);
        print("nums1 - nums2:", res);
        res.clear();

        printBlank();

        /* isempty */
        print("isempty:", res.isEmpty());

        /* size */
        print("nums1 size:", nums1.size());

        /* contains */
        print("nusm1 contains 12:", nums1.contains(12));
        print("nums1 contains 1:", nums1.contains(1));
    }

    public static void print(String prompt, HashSet<Integer> nums){
        System.out.println(String.format("%-25s %-30s", prompt, nums));
    }

    public static void print(String prompt, boolean res){
        System.out.println(String.format("%-25s %-30s", prompt, res));
    }

    public static void print(String prompt, int num){
        System.out.println(String.format("%-25s %-30s", prompt, num));
    }

    public static void printBlank(){
        System.out.println("");
        System.out.println("------------------");
        System.out.println("");
    }
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
nums1:                    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
nums2:                    [0, 2, 4, 6, 8]               

add 3 to nums1:           [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
add 3 to nums2:           [0, 2, 3, 4, 6, 8]            
remove 4 from nums1:      [0, 1, 2, 3, 5, 6, 7, 8, 9]   

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

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

nums1 | nums2:            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
nums1 & nums2:            [0, 2, 3, 6, 8]               
nums1 - nums2:            [1, 5, 7, 9]                  

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

isempty:                  true                          
nums1 size:               9                             
nusm1 contains 12:        false                         
nums1 contains 1:         true

遍历方式

使用迭代器Iterator:

1
2
3
4
5
Iterator<Integer> iter = nums1.iterator();
System.out.print("iterate: ");
while (iter.hasNext()){
    System.out.print(iter.next() + " ");
}

输出:

1
iterate: 0 1 2 3 5 6 7 8 9  

关于无序性

在先前的叙述中我们反复提及了无序这个关键词,它的含义其实是:元素存入与取出的顺序不一致,并不是指依照元素的某些性质进行排序。验证Hashset无序性的示例代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.util.*;

public class hashset_iter {
    public static void main(String[] args){
        HashSet<Integer> nums1 = new HashSet<>();
        HashSet<Integer> nums2 = new HashSet<>();

        /* initialize nums1 */
        nums1.add(1);
        nums1.add(3);
        nums1.add(2);
        nums1.add(4);

        /* initialize nums2 */
        nums2.add(3);
        nums2.add(4);
        nums2.add(1);
        nums2.add(2);

        /* iterate nums1 */
        Iterator<Integer> iter1 = nums1.iterator();
        System.out.print("iterate nums1: ");
        while (iter1.hasNext()){
            System.out.print(iter1.next() + " ");
        }

        /* iterate nums2 */
        Iterator<Integer> iter2 = nums2.iterator();
        System.out.print("\niterate nums2: ");
        while (iter2.hasNext()){
            System.out.print(iter2.next() + " ");
        }
    }
}

输出:

1
2
iterate nums1: 1 2 3 4 
iterate nums2: 1 2 3 4 

可以看到,两个集合中元素的存入与取出的顺序并不一致。

LinkedHashSet

  • 继承自HashSet,基于LinkedHashMap实现
  • 元素有序,通过链表维护元素的顺序
  • 线程不安全
  • 与HashSet相比,增删元素时的性能稍逊,迭代访问元素时的性能稍好
  • 允许null值

构造方法

与HashSet一致,同样包含容量负载因子这两个参数。默认构造方法如下:

1
LinkedHashSet<E> nums = new LinkedHashSet<>();

常用方法

与HashSet一致。略。

遍历方式

依旧使用Iterator,略。

关于有序性

与HashSet那一节中提到的无序性定义一样,这里的有序指的是:元素存入与取出的顺序一致。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.*;

public class linkedhashset_iter {
    public static void main(String[] args){
        /* initialization */
        LinkedHashSet<Integer> nums1 = new LinkedHashSet<>();
        Integer[] elements = {1, 3, 2, 4, 10, 6, 5};
        for (Integer e: elements){
            nums1.add(e);
        }

        /* iteration */
        Iterator<Integer> iter = nums1.iterator();
        System.out.print("iter: ");
        while (iter.hasNext()){
            System.out.print(iter.next() + " ");
        }
    }
}

输出:

1
iter: 1 3 2 4 10 6 5 

可见,与HashSet不同,LinkedHashSet的元素存入与取出的顺序是一致的,这也验证了其的有序性。

TreeSet

  • 是Set接口的子接口SortedSet的实现类
  • 基于TreeMap实现
  • 元素被排序,支持自然升序(默认)自定义排序(Comparator)
  • 性能比HashSet和LinkedHashSet差
  • 线程不安全
  • 不允许null值

构造方法

1
TreeSet<E> nums = new TreeSet<>();

此时集合中元素的排序方式为默认的升序排序

常用方法

增删、迭代的方法与HashSet中的一致。除此之外独有的方法如下:

方法描述
first()返回集合的第一个元素
last()返回集合的最后一个元素
higher(E e)
ceiling(E e)
返回大于指定元素的最小元素,没有符合要求的元素会抛异常
返回大于等于指定元素的最小元素,没有符合要求的元素会抛异常
lower(E e)
floor(E e)
返回小于指定元素的最大元素,没有符合要求的元素会抛异常
返回小于等于指定元素的最大元素,没有符合要求的元素会抛异常
pollFirst()
pollLast()
返回并删除集合的第一个元素
返回并删除集合的最后一个元素
headSet(E e, boolean b)返回指定元素之前的所有元素,返回类型为SortedSet
布尔值参数为true时包含边界,默认为false
tailSet(E e, boolean b)返回指定元素之后的所有元素,返回类型为SortedSet
布尔值参数为true时包含边界,默认为true
subSet(E e1, boolean b1, E e2, boolean b2)返回两个指定元素之间的所有元素,返回类型为SortedSet
布尔值参数为true时包含边界,b1默认为trueb2默认为false

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import java.util.*;

public class treeset {
    public static void main(String[] args){
        /* initialization */
        TreeSet<Integer> nums = new TreeSet<>();
        Integer[] elements = {1, 10, 4, 2, 3, 8, 7, 5, 9, 6};
        for (Integer e: elements){
            nums.add(e);
        }
        print("nums:", nums);
        System.out.println("");

        /* first */
        print("first:", nums.first());

        /* last */
        print("last:", nums.last());
        System.out.println("");

        /* higher & ceiling*/
        print("higher:", nums.higher(9));
        print("ceiling:", nums.ceiling(9));

        /* lower & floor */
        print("lower:", nums.lower(9));
        print("floor:", nums.floor(9));
        System.out.println("");

        /* poll */
        print("pollfirst:", nums.pollFirst());
        print("nums:", nums);
        print("polllast:", nums.pollLast());
        print("nums:", nums);
        System.out.println("");

        /* headset */
        print("headset 5:", nums.headSet(5));
        print("headset 5 true:", nums.headSet(5, true));
        System.out.println("");

        /* tailset */
        print("tailset 6:", nums.tailSet(6));
        print("tailset 6 false:", nums.tailSet(6, false));
        System.out.println("");

        /* subset */
        print("subset 3 7:", nums.subSet(3, 7));
        print("subset 3 false 7 false:", nums.subSet(3, false, 7, false));
        print("subset 3 true 7 true:", nums.subSet(3, true, 7, true));
    }

    public static void print(String prompt, TreeSet<Integer> nums){
        System.out.println(String.format("%-25s %-20s", prompt, nums));
    }   

    public static void print(String prompt, int num){
        System.out.println(String.format("%-25s %-20s", prompt, num));
    }

    public static void print(String prompt, SortedSet<Integer> nums){
        System.out.println(String.format("%-25s %-20s", prompt, nums));
    }
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
nums:                     [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

first:                    1                   
last:                     10                  

higher:                   10                  
ceiling:                  9                   
lower:                    8                   
floor:                    9                   

pollfirst:                1                   
nums:                     [2, 3, 4, 5, 6, 7, 8, 9, 10]
polllast:                 10                  
nums:                     [2, 3, 4, 5, 6, 7, 8, 9]

headset 5:                [2, 3, 4]           
headset 5 true:           [2, 3, 4, 5]        

tailset 6:                [6, 7, 8, 9]        
tailset 6 false:          [7, 8, 9]           

subset 3 7:               [3, 4, 5, 6]        
subset 3 false 7 false:   [4, 5, 6]           
subset 3 true 7 true:     [3, 4, 5, 6, 7] 

自定义排序

想要自定义TreeSet中元素的排序方式,可以使用Comparator,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.*;

public class treeset_comparator{
    public static void main(String[] args){
        TreeSet<Integer> nums = new TreeSet<>(new CustomComparator());
        Integer[] elements = {1, 10, 3, 5, 6, 2, 4, 8, 7, 9};
        for (Integer e: elements){
            nums.add(e);
        }
        System.out.println(nums);
    }
}

class CustomComparator implements Comparator<Integer>{
    @Override
    public int compare(Integer n1, Integer n2) {
        return n2 - n1;
    }
}

输出:

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

这里自定义的是降序排列的方式。

参考

This post is licensed under CC BY 4.0 by the author.