Set介绍
Set——
存储不重复的key,不存储映射的value。(和map相比)
Set
用于存储不重复的元素集合,它主要提供以下几个方法:
-
将元素添加进
Set<E>
:boolean add(E e)
-
将元素从
Set<E>
删除:boolean remove(Object e)
-
判断是否包含元素:
boolean contains(Object e)
例子:
public class Main {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
System.out.println(set.add("abc")); // true
System.out.println(set.add("xyz")); // true
System.out.println(set.add("xyz")); // false,添加失败,因为元素已存在
System.out.println(set.contains("xyz")); // true,元素存在
System.out.println(set.contains("XYZ")); // false,元素不存在
System.out.println(set.remove("hello")); // false,删除失败,因为元素不存在
System.out.println(set.size()); // 2,一共两个元素
}
}
Set
实际上相当于只存储key、不存储value的Map
。我们经常用Set
用于去除重复元素。
因为放入Set
的元素和Map
的key类似,都要正确实现equals()
和hashCode()
方法,否则该元素无法正确地放入Set
。
并且Set
接口并不保证有序,而SortedSet
接口则保证元素是有序的:
-
HashSet
是无序的,因为它实现了Set
接口,并没有实现SortedSet
接口; -
TreeSet
是有序的,因为它实现了SortedSet
接口。
HashSet 和 TreeSet 比较
public static void main(String[] args) {
Set<String> hashSet = new HashSet<>();
Set<String> treeSet = new TreeSet<>();
hashSet.add("apple");
hashSet.add("banana");
hashSet.add("pear");
hashSet.add("orange");
for (String s : hashSet) {
System.out.print(s + " ");
}
System.out.println();
treeSet.add("apple");
treeSet.add("banana");
treeSet.add("pear");
treeSet.add("orange");
for (String s : treeSet) {
System.out.print(s + " ");
}
}
output:
banana orange apple pear
apple banana orange pear
使用TreeSet
和使用TreeMap
的要求一样,添加的元素必须正确实现Comparable
接口,如果没有实现Comparable
接口,那么创建TreeSet
时必须传入一个Comparator
对象。传入Comparator对象。
Queue
队列(Queue
)是一种经常使用的集合。Queue
实际上是实现了一个先进先出(FIFO:First In First Out)的有序表。它和List
的区别在于,List
可以在任意位置添加和删除元素,而Queue
只有两个操作:
-
把元素添加到队列末尾;
-
从队列头部取出元素。
在Java的标准库中,队列接口Queue
定义了以下几个方法:(注意这是两个返回值不同的方法)
-
int size()
:获取队列长度; -
boolean add(E)
/boolean offer(E)
:添加元素到队尾; -
E remove()
/E poll()
:获取队首元素并从队列中删除; -
E element()
/E peek()
:获取队首元素但并不从队列中删除。
对于具体的实现类,有的Queue有最大队列长度限制,有的Queue没有。注意到添加、删除和获取队列元素总是有两个方法,这是因为在添加或获取元素失败时,这两个方法的行为是不同的。我们用一个表格总结如下:
例如,假设我们有一个队列,对它做一个添加操作,如果调用add()
方法,当添加失败时(可能超过了队列的容量),它会抛出异常:
Queue<String> q = ...
try {
q.add("Apple");
System.out.println("添加成功");
} catch(IllegalStateException e) {
System.out.println("添加失败");
}
如果我们调用offer()
方法来添加元素,当添加失败时,它不会抛异常,而是返回false
:
Queue<String> q = ...
if (q.offer("Apple")) {
System.out.println("添加成功");
} else {
System.out.println("添加失败");
}
poll()
和peek()
为例来说说“获取并删除”与“获取但不删除”的区别。对于Queue
来说,每次调用poll()
,都会获取队首元素,并且获取到的元素已经从队列中被删除了:
public class Main {
public static void main(String[] args) {
Queue<String> q = new LinkedList<>();
// 添加3个元素到队列:
q.offer("apple");
q.offer("pear");
q.offer("banana");
// 从队列取出元素:
System.out.println(q.poll()); // apple
System.out.println(q.poll()); // pear
System.out.println(q.poll()); // banana
System.out.println(q.poll()); // null,因为队列是空的
}
}
output
apple
pear
banana
null
如果用peek()
,因为获取队首元素时,并不会从队列中删除这个元素,所以可以反复获取:
public class Main {
public static void main(String[] args) {
Queue<String> q = new LinkedList<>();
// 添加3个元素到队列:
q.offer("apple");
q.offer("pear");
q.offer("banana");
// 队首永远都是apple,因为peek()不会删除它:
System.out.println(q.peek()); // apple
System.out.println(q.peek()); // apple
System.out.println(q.peek()); // apple
}
}
从上面的代码中,我们还可以发现,LinkedList
即实现了List
接口,又实现了Queue
接口,但是,在使用的时候,如果我们把它当作List,就获取List的引用,如果我们把它当作Queue,就获取Queue的引用。