目录
Set 集合是一个不能包含重复元素的集合。Set接口只包含从Collection继承的方法,并添加了禁止重复元素的限制。Set还为equals和hashCode操作的行为添加了更强的约定,允许对Set实例进行有意义的比较,即使它们的实现类型不同。如果两个Set实例包含相同的元素,则它们是相等的。
Java平台包含三种通用的集合实现:HashSet、TreeSet 和 LinkedHashSet。
如果想要通过一个集合创建另一个包含相同元素的集合,但要消除所有重复项。可通过以下代码实现:
- // c 表示原有集合
- Collection<Type> noDups = new HashSet<Type>(c);
如果使用的是JDK8或更高版本,可以使用流操作:
- c.stream()
- .collect(Collectors.toSet()); // no duplicates
如果使用的是 TreeSet,则通过以下方式进行设置:
- Set<String> set = people.stream()
- .map(Person::getName)
- .collect(Collectors.toCollection(TreeSet::new));
下边程序使用了 LinkedHashSet,它保留了原始集合的顺序,同时删除了重复的元素:
Collection<Type> noDups = new LinkedHashSet<Type>(c);
下面是一个泛型方法,它封装了前面的用法,返回一个与传递的泛型类型相同的Set。
- public static <E> Set<E> removeDups(Collection<E> c) {
- return new LinkedHashSet<E>(c);
- }
下面的程序打印出参数列表中所有不同的单词。
- import java.util.Arrays;
- import java.util.Set;
- import java.util.stream.Collectors;
-
- public class FindDups {
- public static void main(String[] args) {
- args = "i came i saw i left".split(" ");
- Set
distinctWords = Arrays.asList(args).stream() - .collect(Collectors.toSet());
- System.out.println(distinctWords.size() + " distinct words: " + distinctWords);
- }
- }
执行结果:
4 distinct words: [left, came, saw, i]
注意,代码总是通过接口类型(Set)引用集合,而不是通过其实现类型。这是一种强烈推荐的编程实践,因为它提供了仅通过更改构造函数来更改实现的灵活性。
在上面的例子中,Set的实现类型是HashSet,它不保证Set中元素的顺序。如果希望程序按字母顺序打印单词列表,只需将Set的实现类型从HashSet更改为TreeSet。更改后执行结果如下:
4 distinct words: [came, i, left, saw]
Set 同样适合于批量操作,假设 s1 和 s2 是 Set 集合。以下是批量操作的作用:
s1.containsAll(s2) — 如果s2是s1的子集,返回trues1.addAll(s2) — 将s1变换为s1和s2的并集s1.retainAll(s2) — 把s1变换成s1和s2的交集s1.removeAll(s2) — 将s1转换为s1和s2的差集如果要非破坏性地(不修改集合)计算两个集合的并、交或集差,调用方必须在调用批量操作之前复制一个集合。下边是习惯用法:
- Set<Type> union = new HashSet<Type>(s1); // copy set
- union.addAll(s2); // 并集
-
- Set<Type> intersection = new HashSet<Type>(s1);
- intersection.retainAll(s2); // 交集
-
- Set<Type> difference = new HashSet<Type>(s1);
- difference.removeAll(s2); // 差集
回到FindDups程序,需要统计参数列表中只出现过一次的单词和出现过多次的单词,同时不重复打印任何单词。
这个效果可以通过生成两个Set 集合来实现,一个包含参数列表中的每个单词,另一个只包含重复的单词。只出现一次的单词是这两个集合的集合差,我们知道如何计算。下面是结果程序的实现:
- import java.util.HashSet;
- import java.util.Set;
-
- public class FindDups {
-
- public static void main(String[] args) {
- args = "i came i saw i left".split(" ");
- Set<String> uniques = new HashSet<String>();
- Set<String> dups = new HashSet<String>();
- for (String a : args) {
- if (!uniques.add(a)) {
- dups.add(a);
- }
- }
- // Destructive set-difference
- uniques.removeAll(dups);
- System.out.println("Unique words: " + uniques);
- System.out.println("Duplicate words: " + dups);
- }
- }
程序执行结果:
Unique words: [left, came, saw]
Duplicate words: [i]
计算对称集合差
什么是对称集合差?即元素包含在两个指定集合中的任意一个,但不同时包含在两个集合中的元素集合。下面的代码非破坏性地计算两个集合的对称集差。
- Set<Type> symmetricDiff = new HashSet<Type>(s1);
- symmetricDiff.addAll(s2); // 1-先求并集
- Set<Type> tmp = new HashSet<Type>(s1);
- tmp.retainAll(s2); // 2-然后求交集
- symmetricDiff.removeAll(tmp); // 3-最后求差集