Java中的TreeSet addAll()方法
TreeSet
类是 Set
接口的实现类之一,它是一个有序集合,可以根据元素的自然顺序或者根据构造函数所提供的 Comparator
进行排序。TreeSet中的元素不重复,和HashSet一样,对于重复元素只会保留一份。在 TreeSet 中还有一个 addAll(Collection<? extends E> c)
方法,它允许将指定集合中的所有元素添加到此TreeSet中。 然而,在使用 addAll()
方法的时候,用户需要特别留意元素的类型,否则会引发运行时异常。
addAll() 方法的用法
addAll(Collection<? extends E> c)
将指定集合中的所有元素添加到此TreeSet中。它返回一个布尔值代表是否添加了指定的集合。例如:
Set<String> set1 = new HashSet<String>();
set1.add("one");
set1.add("two");
set1.add("three");
TreeSet<String> set2 = new TreeSet<String>();
set2.add("four");
set2.add("five");
set2.add("six");
boolean res1 = set2.addAll(set1); // 将集合set1中的元素添加到set2中
System.out.println(res1); // true
boolean res2 = set2.addAll(set1); // 再次添加,返回false
System.out.println(res2); // false
System.out.println(set2); // 会输出 [four, one, six, three, two, five]
在这个示例中,我们首先新建一个 HashSet
,并向其中添加了三个字符串元素。接着,我们又新建了一个 TreeSet
,并向其中添加了三个字符串元素。接着,我们使用 set2.addAll(set1)
的方式将 set1
中的元素添加到 set2
中。该方法返回一个布尔值,代表操作是否成功。由于 set2
当前是空的,因此所有元素都会成功添加到 set2
中。同样的操作过程,我们再次将 set1
中的元素添加到 set2
中。由于 set2
中已经有了这些元素,所以这次操作将会失败,并且返回 false
。最后,我们打印出 set2
的元素集合,可以看到元素已经按照自然顺序进行了排序。
但值得注意的是,若 addAll(Collection<? extends E> c)
中的集合类型与TreeSet中不同,那么在添加元素时会引发Java的类型转换异常。 那么怎么解决这个问题呢?下面我们将探讨不同情况下该如何使用 addAll()
方法。
两个集合元素的类型一致
如果要将一个与 TreeSet 具有相同类型的 Collection
添加到 TreeSet
中,则应该在 addAll
方法中向将 Collections
实例传递给三个运算符 <>
。例如:
Set<Integer> set1 = new HashSet<>();
set1.add(1);
set1.add(2);
set1.add(3);
TreeSet<Integer> set2 = new TreeSet<>();
set2.add(4);
set2.add(5);
set2.add(6);
boolean res1 = set2.addAll(set1);
System.out.println(res1); // true
boolean res2 = set2.addAll(set1);
System.out.println(res2); // false
System.out.println(set2); // 会输出 [1, 2, 3, 4, 5, 6]
这里的 set2
是一个TreeSet<Integer>
,而 set1
中也只包含了整型数字。因此,我们在调用 addAll()
方法时不需要进行类型转换即可将整型数字元素添加到 set2
中。
两个集合元素的类型不一致
如果要将一个与 TreeSet 具有不同类型的 Collection
添加到 TreeSet
中,则需要进行类型转换。例如:
Set<Object> set1 = new HashSet<>();
set1.add(1);
set1.add(2);
set1.add(3);
TreeSet<Integer> set2 = new TreeSet<>();
set2.add(4);
set2.add(5);
set2.add(6);
boolean res1 = set2.addAll((Collection<? extends Integer>) set1);
System.out.println(res1); // true
boolean res2 = set2.addAll((Collection<? extends Integer>) set1);
System.out.println(res2); // false
System.out.println(set2); // 会输出 [1, 2, 3, 4, 5, 6]
这里,set1
是一个 HashSet<Object>
,虽然它包含了整型数字元素,但由于集合类型与 set2
不同,因此在调用 addAll()
方法时需要进行类型转换。我们使用 (Collection<? extends Integer>) set1
这种方式将 set1
转换为 Collection<? extends Integer>
,这样整型数字元素就可以成功添加到 set2
中了。
addAll() 方法的源代码
最后,我们来看一下 addAll()
方法在 TreeSet
中的源代码实现。
public boolean addAll(Collection<? extends E> c) {
Objects.requireNonNull(c);
boolean modified = false;
if (c instanceof SortedSet<?>) {
SortedSet<? extends E> s = (SortedSet<? extends E>)c;
Comparator<? super E> cc = (Comparator<? super E>)tree.comparator();
Comparator<? super E> sc = s.comparator();
if (cc==null && sc==null)
modified = addAll((SortedSet<E>)s);
else
modified = addAllUsingComparator(s, cc);
} else if (c instanceof Collection<?>) {
modified = standardAddAll((Collection<? extends E>)c);
} else {
for (E e : c)
modified |= add(e);
}
return modified;
}
其中,第 3 行代码对输入参数 c
进行了判空处理。接下来,该方法会根据 c
的类型分别进行处理:
- 如果
c
的类型是SortedSet<? extends E>
,即输入的集合类型是有序集合,并且具有和TreeSet
相同的排序算法,则调用TreeSet.addAll(SortedSet<E> s)
方法进行添加。这个方法实际上就是不断调用add()
方法进行元素逐个添加的过程。 - 如果
c
的类型是Collection<? extends E>
,即输入的集合类型是集合类,并且具有相同的元素类型,则调用TreeSet.standardAddAll(Collection<? extends E> c)
方法进行添加。这个方法实际上也是不断调用add()
方法进行元素逐个添加的过程。 - 如果
c
的类型不是有序集合也不是其他类型的集合(即元素类型与TreeSet
不同),则调用add(E e)
方法将集合中的元素逐个添加到TreeSet
中。
结论
TreeSet
中的 addAll()
方法是一个很方便的集合操作方法,可以将另一个集合中的元素逐个添加到当前 TreeSet
中。但需要特别注意的是,在使用 addAll()
方法的时候,用户需要特别留意元素的类型,否则会引发运行时异常。如果要将不同类型的集合添加到 TreeSet
中,则需要进行类型转换。同时,该方法的源代码也为我们提供了使用时的参考。