一个Set是一个不能包含重复元素的集合。它映射了数学意义上的集合抽象。Set接口只是在继承自Collecton接口的方法基础之上加上不允许元素重复的限制。Set也对equals和hashCode的行为规约施加了更强的限制,使得Set实例允许进行有意义的比较,即使他们的具体实现不同。两个集合实例相等(equal)如果它们包含相同的元素。
一个Set是一个不能包含重复元素的集合。它映射了数学意义上的集合抽象。Set接口只是在继承自Collecton接口的方法基础之上加上不允许元素重复的限制。Set也对equals和hashCode的行为规约施加了更强的限制,使得Set实例允许进行有意义的比较,即使他们的具体实现不同。两个集合实例相等(equal)如果它们包含相同的元素。
下面是Set接口的API实现:
public interface Set<E> extends Collection<E> {
// Basic operations
int size();
boolean isEmpty();
boolean contains(Object element);
// optional
boolean add(E element);
// optional
boolean remove(Object element);
Iterator<E> iterator();
// Bulk operations
boolean containsAll(Collection<?> c);
// optional
boolean addAll(Collection<? extends E> c);
// optional
boolean removeAll(Collection<?> c);
// optional
boolean retainAll(Collection<?> c);
// optional
void clear();
// Array Operations
Object[] toArray();
<T> T[] toArray(T[] a);
}
Java平台提供了通用的Set实现:HashSet、TreeSet和LinkedHashSet。HashSet将元素存储在哈希表(hash table)中,它是最高效的Set实现,但是它无法确定迭代顺序。TreeSet将元素存储在红黑树中,元素按照值进行排序,它比HashSet稍慢。LinkedHashSet是HashSet的链表实现,它保持元素插入的顺序,但是访问性能比HashSet和TreeSet差。
这里有一个简单但是有用的Set使用场景。假设你有一个Collection c,你想创建另外一个Collection,但必须去除重复元素(只保留一个)。下面一行代码就实现了你的要求:
Collection<Type> noDups = new HashSet<Type>(c);
这里有另外一个变种实现,能保证原始集合中元素的顺序:
Collection<Type> noDups = new LinkedHashSet<Type>(c);
下面是一个泛化的封装了上面这行代码的方法:
public static <E> Set<E> removeDups(Collection<E> c) {
return new LinkedHashSet<E>(c);
}
Set接口的基本操作
size方法返回Set中元素的数目。isEmpty方法判断Set是否为空。add方法往Set中添加指定元素,如果该元素不存在于集合中,并且返回一个布尔值标识元素是否成功添加。类似地,remove方法从Set中移除指定元素,如果该元素存在于集合中,并且返回一个布尔值标识是否成功移除。iterator方法返回Set的迭代器。
下面的程序接受一组单词作为参数args,打印出任何重复的单词、所有不重复单词的数目以及不重复单词列表。
import java.util.*;
public class FindDups {
public static void main(String[] args) {
Set<String> s = new HashSet<String>();
for (String a : args)
if (!s.add(a))
System.out.println("Duplicate detected: " + a);
System.out.println(s.size() + " distinct words: " + s);
}
}
现在用下面的命令运行程序:
java FindDups i came i saw i left
程序输出如下:
Duplicate detected: i
Duplicate detected: i
4 distinct words: [i, left, saw, came]
注意上面的代码始终通过接口类型(Set)来引用对应的集合,而不是引用具体实现类型(HashSet)。这是强烈推荐使用的编程实践,因为这使得我们能更灵活地切换集合具体实现,只需要改变构造器函数。如果用来存储集合的变量或者用来传递给其他方法的参数被声明为集合具体实现类型而不是集合接口类型,那么这些变量或参数也必须随着具体实现的改变而改变。
前面例子中Set的具体实现类型是HashSet,它不能保证Set中元素的顺序。如果你想让程序按照字母顺序打印出单词,只需要把Set的具体实现类型从HashSet修改为TreeSet。仅仅修改前面一行代码:
Set<String> s = new TreeSet<String>();
将产生下面的输出:
java FindDups i came i saw i left
Duplicate detected: i
Duplicate detected: i
4 distinct words: [came, i, left, saw]
Set接口批量操作(bulk operation)
批量操作尤其适用于Set。当执行批量批量操作相当于执行集合代数意义上的运算。假设s1和s2都是Set。下面是各种批量操作:
- s1.containsAll(s2) — 如果s2是s1的子集,返回true,否则返回false
- s1.addAll(s2) — 得到的是s1和s2的并集
- s1.retainAll(s2) — 得到的是s1和s2的交集
- s1.removeAll(s2) — 得到的是s1和s2的差集(s1-s2,即所有s1中有但是s2中没有的元素的集合)
为了计算两个集合的并、交、差集而不修改这两个集合,调用者必须先拷贝一份,然后再调用bulk opertaion,比如下面:
Set<Type> union = new HashSet<Type>(s1);
union.addAll(s2);
Set<Type> intersection = new HashSet<Type>(s1);
intersection.retainAll(s2);
Set<Type> difference = new HashSet<Type>(s1);
difference.removeAll(s2);
上面代码的结果集类型是HashSet。
让我们再次回顾之前的FindDups程序。假设你想知道哪些单词只出现一次,哪些单词出现不止一次,但是又不想重复打印单词。那么这种效果可以用两个Set来实现,一个Set包含参数列表中的所有单词,另外一个Set只包含重复出现的单词。那么只出现一次的单词就是这两个Set的差集。下面是代码实现:
import java.util.*;
public class FindDups2 {
public static void main(String[] args) {
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, saw, came]
Duplicate words: [i]
还有一个不那么常见的集合代数操作,那就是对称集合差集 —— 由两个集合中的元素组成,但元素不能包含于两个集合的交集中。下面的代码实现了这种效果:
Set<Type> symmetricDiff = new HashSet<Type>(s1);
symmetricDiff.addAll(s2); // 并集
Set<Type> tmp = new HashSet<Type>(s1);
tmp.retainAll(s2)); // tmp成了交集
symmetricDiff.removeAll(tmp);
Set接口的数组操作
Set接口的数组操作与前面的Collection接口的数组操作没有任何不同。
相关推荐
本章节主要讲解Java集合框架的上半部分,包括集合框架的概述、Collection接口、List接口、Set接口、Map接口等。 Java学习系列(八):Java面向对象之集合框架详解(下) 本章节主要讲解Java集合框架的下半部分,...
在Java集合框架中,Set接口代表了不包含重复元素的集合,比如HashSet。与ArrayList不同,Set不维护元素的特定顺序。HashSet的内部实现通常基于哈希表,因此它的添加、删除和查找操作通常具有O(1)的平均时间复杂度。...
在本Java视频教程中,讲师将深入讲解Java集合框架,包括基础概念、接口、类以及它们之间的关系。 首先,主要集合概述涵盖了三种基本类型:List、Set和Map。List是一个有序集合,允许元素重复,例如ArrayList和...
它是Java集合框架的顶级接口,位于`java.util`包下。所有的集合类,无论是List、Set还是Queue,都直接或间接地继承自Collection接口。Collection接口定义了基本的集合操作,如添加元素(add),删除元素(remove),检查...
Java集合框架包括接口和实现类,接口如List、Set和Queue,它们定义了操作集合的一系列方法。实现类则包括ArrayList、LinkedList、HashSet、HashMap等,每种类都有其特定的应用场景和性能特点。 1. List接口:List...
本教程将深入探讨两个核心概念:枚举类(Enum)和注解(Annotation),并结合Java集合框架中的API进行讲解。以下是这些主题的详细说明。 首先,让我们了解一下枚举类。在Java中,枚举是一种特殊的类,它定义了一组...
泛型是Java集合框架中的一个重要特性,它允许在编译时指定集合元素的具体类型,从而避免了运行时的类型转换错误。使用泛型的集合可以更安全地操作对象,同时提高了代码的可读性和可维护性。 #### 集合类的实例 - *...
4. **集合框架**:Java集合框架包括接口(如List、Set和Map)和实现(如ArrayList、HashSet和HashMap)。了解这些接口和实现的用法,以及它们之间的区别,是提高编程效率的关键。 5. **IO流**:Java的IO流系统用于...
6. **集合框架**:Java集合框架包括接口(如List、Set、Map)和实现类(如ArrayList、HashSet、HashMap),它们为存储和操作对象提供了便利。了解这些接口和类的用法,以及它们之间的关系,是进行复杂数据管理的关键...
1. **集合框架的体系结构**:Java集合框架包括一系列接口和类,它们之间存在层次关系。Collection接口是所有集合的父接口,提供了基本的操作,如添加、删除、查找元素等。Set接口扩展了Collection,不允许重复元素,...
集合(Collection)是Java集合框架的基础,它是一个容器,可以存储多个对象。集合具有添加、删除元素以及访问和更新元素的能力。集合的体系结构由多种接口和实现类组成,它们之间有层次关系,使得程序员可以根据具体...
本教程的"Java实例 - 集合输出源代码+详细指导教程.zip"旨在帮助开发者深入理解Java集合框架,并通过实例源代码进行实践学习。 集合框架包括接口、类和算法,主要由以下部分组成: 1. **接口**:这些是集合的抽象...
4. **集合框架**:Java集合框架是管理对象的容器,包括List、Set、Map等接口和它们的实现类。这些例题会教你如何使用ArrayList、LinkedList、HashSet、HashMap等,以及如何遍历和操作集合。 5. **IO流**:Java的...
4. **集合框架**:Java集合框架是存储和管理对象的核心工具,包括List、Set、Map等接口和实现类。学习这部分可以帮助开发者有效地组织和操作数据。 5. **IO流与NIO**:Java的输入输出流(IO)和新IO(NIO)库提供了...
Java集合框架提供了一系列接口和类,如List、Set、Map,用于存储和操作对象。ArrayList和LinkedList是List的实现,HashSet和HashMap是Set和Map的实现。 7. **IO流** Java的输入/输出系统基于流的概念,分为字节流...
5. **集合框架**:Java集合框架包括List、Set、Map接口及其实现类,如ArrayList、LinkedList、HashSet、HashMap等,以及迭代器(Iterator)的使用。 6. **IO流**:Java的输入/输出流(IO流)系统用于读写文件、网络...