文章目录
- 暂时跳过的部分:
- Ch.IX 集合:
- 9.1 Java集合框架:
- Java集合简介:
- 集合与数组的区别:
- 常用的集合の层次结构:
- 集合框架中的接口简介:
- Collection接口简介:
- Map接口简介:
- 集合迭代器简介:
- 9.2 Collection系列集合:
- List系列:
- List中Iterator的失效问题:
- LinkedList的查找问题:
- 对于ArrayList与Vector:
- Set系列:
- hashCode() 设计原则:
- 复习 Comparator & Comparable:
- HashSet:
- HashSet特点:
- 构造:
- 方法:
- LinkedHashSet:
- 特点:
- TreeSet:
- PriorityQueue:
- 集合使用的选择:
- 9.3 Map系列集合:
- 映射视图:
- HashTable:
- 特点:
- HashMap:
- 特点:
- HashMap & HashTable 的空间与时间的折中:
- HashMap & HashTable的比较:
- TreeMap:
- 构造器:
- 常用的方法:
- WeakHashMap:
- 特点:
- LinkedHashMap:
- IdentityHashMap:
- 9.4 集合视图:
- 轻量级集合包装器:
- 子视图:
- 不可修改的视图:
- 同步视图:
- 受查视图:
- 9.5 算法:
- 排序算法:
- 二分查找:
- 简单算法:
- 集合与数组的转换:
暂时跳过的部分:
- Collection接口: API的使用测试与重点记忆
- 各个集合的API接口, 还没有正式熟悉并上手使用
- 各个集合中的API需要确定比较常用的重点
- 9.6遗留的集合, ALL, 全部跳过
Ch.IX 集合:
这里就相当于C++ の STL 库中的容器…各种各样的容器…
所以, 一切从简!
9.1 Java集合框架:
Java集合简介:
首先来一张Java集合的全家福:
与C++ の STL库相似, Java的集合框架中也是将接口与实现分离, 用户只需要学会使用接口而无需关系内部实现的过程
反正就当做是C++STL容器用啦
集合与数组的区别:
根据需求选用合适的容器
常用的集合の层次结构:
Collection 接口:
对象的集合(单列集合)
- List 接口:元素按进入先后有序保存,可重复
- LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
- ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
- Vector 接口实现类 数组, 同步, 线程安全
- Stack 是Vector类的实现类
- Set 接口: 仅接收一次,不可重复,并做内部排序
- HashSet 使用hash表(数组)存储元素
- LinkedHashSet 链表维护元素的插入次序
- TreeSet 底层实现为二叉树,元素排好序
- EnumSet 一个专为枚举设计的集合类
Map 接口:
键值对的集合 (双列集合)
- Hashtable 接口实现类, 同步, 线程安全
- HashMap 接口实现类 ,没有同步, 线程不安全-
- LinkedHashMap 双向链表和哈希表实现
- WeakHashMap
- IdentifyHashMap
- TreeMap 红黑树对所有的key进行排序
集合框架中的接口简介:
集合有两个基本的接口: Collection & Map 而后每个由此派生出的容器有扩充了自己的接口
所以, 总的来说, 接口的数量是非常多的…
Collection接口简介:
Collection接口中的方法概览
API中抠出来的各个方法的介绍
其实也不多, 几个常用的方法都在里头了:
Map接口简介:
从API中抠出的方法:
集合迭代器简介:
Iterator接口:
public interface Iterator<E>
{
E next();
boolean hasNextO;
void remove0;
default void forEachRemaining(Consumer<? super E> action);
}
API中的Iterator接口, 确实也就这4个…
Java的迭代器和C++有大不同:
- C++迭代器可以笼统的看做是智能指针, 指向容器中的一个元素, 可以解引用得到元素, 也可以移动迭代器使其指向下一个元素
- 而Java迭代器则类似于InputStream输入流, 读取元素的方法是
从流中获取一个元素
如果想要理解为一个指针的话, 可以理解为指向两个元素之间的指针, 当迭代器越过元素时返回它
就此, 理解一下Iterator接口中的几个方法:
- next()
相当于从流中获取一个元素
, 调用这个方法, 迭代器会向后移动一位, 而后返回越过的元素 - hasNext()
判断迭代器的后头还有没有元素, 即流是否为空
- remove()
删除上次调用next()时返回的元素
注意, 这个方法与next() 有相互依赖性, 如果remove之前没有调用next将被视作非法, 抛出IllegalStateException异常
如删除两个相邻的元素:
//错误的用法:
it.remove();
it.remove0;// Error!
//相反地, 必须先调用 next 越过将要删除的元素
it,remove() ;
it.next();
it.remove(); // OK
创建迭代器对象:
public static void main(String[] args) {
ArrayList<Character> arrL= new ArrayList<>();
String str= new String("F@ck♂you");
for(int i=0;i<str.length();i++){
arrL.add(str.charAt(i));
}
System.out.println(arrL);
Iterator<Character> iter= arrL.iterator(); //调用容器对象的工厂方法
for(;iter.hasNext();){
System.out.printf("%c",iter.next());
}
}
输出结果:
[F, @, c, k, ♂, y, o, u]
F@ck♂you
9.2 Collection系列集合:
这里在Copy一下上头的常用集合养养眼:
Collection 接口:
对象的集合(单列集合)
- List 接口:元素按进入先后有序保存,可重复
- LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
- ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
- Vector 接口实现类 数组, 同步, 线程安全
- Stack 是Vector类的实现类
- ArrayDeque 底层为数组的双端队列
- Set 接口: 仅接收一次,不可重复,并做内部排序
- HashSet 使用hash表(数组)存储元素
- LinkedHashSet 链表维护元素的插入次序
- TreeSet 底层实现为二叉树,元素排好序
- EnumSet 一个专为枚举设计的集合类
- PriorityQueue 优先队列
Map 接口:
键值对的集合 (双列集合)
- Hashtable 接口实现类, 同步, 线程安全
- HashMap 接口实现类 ,没有同步, 线程不安全-
- LinkedHashMap 双向链表和哈希表实现
- WeakHashMap
- TreeMap 红黑树对所有的key进行排序
- IdentifyHashMap
List系列:
需要注意的是, Java中所有的链表都是双向链表(double linked)
三个主要的链表类的对比:
- ArrayList:
底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素 - LinkedList
底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素 - Vector:
底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素
List链表有专用的迭代器ListIterator, 实现List的特有的功能:
public interface ListIterator<E> extends Iterator<E> ;
其继承自Iterator接口, 并扩展了其功能:
当操作list链表时, 需要使用ListIterator完成特定的操作
可以当做是在List中移动的光标, 通过add()函数添加内容, 通过remove() + next() 来删除光标前头的元素
反正就像使用C++STL的容器一样, 每个容器都有其特定的Iterator ,就这么用吧…
书中说到的是使用ListIterator的add函数完成对向LinkedList对象的中间插入元素的操作…
但是LinkedList是有专门向中间插入元素的函数add() 的…
这里有一点迷惑…
其他有迷惑的方法自行看API
List中Iterator的失效问题:
Java中同样也存在Iterator的失效:
首先先来回顾一下C++中的迭代器失效:
注意: 为了防止迭代器失效,凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素
向容器中添加或删除元素都有可能会是迭代器失效, 即失效后的迭代器不在指向任何元素(类似野指针).
对于vector & string : 添加元素后(如用push_back()), 如果导致储存空间被重新分配, 则原先指向容器的所有迭代器都会失效; 否则只是插入位置后的元素的迭代器会失效;删除元素之后的迭代器都会失效;
对于deque: 向任何位置插入&添加元素都会导致迭代器全面失效对于list & forward_list: 不论怎么插&删, 迭代器都是有效的
Java List的迭代器就比较脆弱了, 如果一个迭代器修改了List对象 , 会导致其他迭代器直接失效, 此时如果使用了那些失效了的迭代器, 会抛出ConcurrentModificationException异常:
List<String> list = . .
ListIterator<String> iterl = list.listlteratorO ;
ListIterator<String> iter2 = list.listlteratorO ;
iterl.nextO;
iterl.remove0;
iter2.next(); // throws ConcurrentModificationException
LinkedList的查找问题:
上头说道, LinkedList底层是用链表实现的, 所以与C++链表相似, 在元素查找上, 这玩意的效率很低, 是线性查找
但是get()函数做了一点优化, 如果查找的位置大于size/2, 则从链表的尾部开始查找
对于ArrayList与Vector:
上头说道, Vector的效率低, 而ArrayList的效率高, 但是二者的底层使用的都是数组, 造成这种差异的主要原因是: Vector的线程安全性
为了保证线程安全, Vector需要在同步操作上耗费较多的时间, 而ArrayList完全没有这个问题
所以在不需要线程安全的情况下优先使用ArrayList, 而不是Vector
Set系列:
hashCode() 设计原则:
首先先了解一下hashCode的设计原则, 下头有很多集合底层使用的都是散列表, 想要用他们储存元素, 需要对应的类实现hashCode()接口
在了解了Java散列表的储存规则后, 这里可以设计的更好:
参考之前的笔记:
几个比较核心的设计原则:
- 一定要让那些我们认为相同的对象返回相同的hashCode值
- 尽量让那些我们认为不同的对象返回不同的hashCode值,否则,就会增加冲突的概率。
- 尽量的让hashCode值散列开(两值用异或运算可使结果的范围更广)
复习 Comparator & Comparable:
后头很多集合会自动排序, 需要类支持Comparator 或 Comparable 接口
到这里这俩已经有一点遗忘了, 复习一下, 同时明确这俩的区别:
Comparable:
如果自定义类需要比较, 则可以通过实现Comparable接口:
public interface Comparable<T>
{
public int compareTo(T o);
//接口中只有这一个方法
}
该接口对实现它的每个类的对象强加一个整体排序。 这个排序被称为类的自然排序 ,类的compareTo
方法被称为其自然比较方法 。
Comparator:
如果需要对某个类进行排序,而该类本身不支持排序(即没有实现Comparable接口),那么就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可
也就是说,可以通过新建一个比较器类, 并实现Comparator接口,然后通过这个比较器对类进行排序
@FunctionalInterface
public interface Comparator<T>
{
int compare(T o1, T o2);
boolean equals(Object obj);
//其中 equals为Object的方法,不算入内,所以Comparator可以作为函数式接口
//还有其他的方法, 但都是static 或是 default, 所以只需要实现这两个
}
区别:
Comparable 相当于类的内部比较器
, 而Comparator相当于外部比较器
所以Comparable被称作自然排序, 而Comparator被称作定制排序
HashSet:
HashSet特点:
- 底层数据结构采用哈希表实现
- 元素无序且唯一
- 线程不安全
- 效率高
- 可以存储null元素
- 元素的具有唯一性
注意元素唯一性是依靠是否良好的重写hashCode()和equals()方法来保证的
如果没有重写这两个方法,则无法保证元素的唯一性
构造:
- **HashSet() **
构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)。 - HashSet(Collection<? extends E> c)
构造一个包含指定集合中的元素的新集合。 - **HashSet(int initialCapacity) **
构造一个新的空集合; 背景HashMap实例具有指定的初始容量和默认负载因子(0.75)。 - **HashSet(int initialCapacity, float loadFactor) **
构造一个新的空集合; 背景HashMap实例具有指定的初始容量和指定的负载因子。
方法:
- **boolean add(E e) **
将指定的元素添加到此集合(如果尚未存在)。 - **void clear() **
从此集合中删除所有元素。 - **Object clone() **
返回此 HashSet实例的浅层副本:元素本身不被克隆。 - **boolean contains(Object o) **
如果此集合包含指定的元素,则返回 true 。 - **boolean isEmpty() **
如果此集合不包含元素,则返回 true 。 - **Iterator iterator() **
返回此集合中元素的迭代器。 - **boolean remove(Object o) **
如果存在,则从该集合中删除指定的元素。 - **int size() **
返回此集合中的元素数(其基数)。 - **Spliterator spliterator() **
在此集合中的元素上创建late-binding和故障快速 Spliterator 。
LinkedHashSet:
特点:
- 底层数据结构采用链表和哈希表共同实现:
根据元素hashCode值来决定元素存储位置,但它同时使用链表维护元素的次序 - 线程不安全
- 效率高
但由于需要维护元素插入顺序, 所以性能上略微低于HashSet, 但是顺序访问时速度比HashSet更快
暂时用的较少, 具体API看说明文档
TreeSet:
数集底层储存的数据结构为红黑树(R-B Tree), 与C++中set的储存结构相同, 所以用起来也差不多
- 不允许出现键值重复
- 所有的元素都会被自动排序
- 效率比散列表要稍慢
很少有能够达到时间复杂度为o(1)的常量时间的算法
PriorityQueue:
这玩意的特点与C++的优先队列priority_queue基本相同
注意, 添加自定义类对象时, 自定义类需要满足Comparable接口或是Comparator对象
而后根据这两个比较出对象之间的优先级
关于优先级:
优先队列中, 通常使用1
表示最优先, 所以最小的元素是最优先的!
集合使用的选择:
根据不同的需求选择合适的集合, 会使得程序开发的效率更高, 性能更优
9.3 Map系列集合:
map系列和C++相似, 保存了元素的键-值
关系, 用于排序比较的是键
, 而值
不作为比较
Map系列集合的共有特点:
- 内部储存的是
键-值
映射, 其中键&值可以是任意类 - Map接口提供有三种查询内部元素的方法, 被称作
映射视图
映射视图:
之前说到, Map中提供了3中访问元素的方法, 即三种映射视图
:
Set<K> keySet(); //返回键集
Collection<V> values(); //返回值集
Set<Map.Entry<K, V>> entrySet(); //返回键-值映射集
知道有着三个东西就OK, 到时候使用时再看API
HashTable:
特点:
- 与HashMap相同,Hashtable 底层使用的也是散列表,它存储的内容是键值对(key-value)映射。
- Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。
- Hashtable 的函数都是同步的,这意味着它是线程安全的。
- Hashtable 的key、value都不可以为null。
- Hashtable中的映射不是有序的。
HashMap:
特点:
- HashMap是一个散列表,它存储的内容是键值对(key-value)映射。
- HashMap继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
- HashMap的实现是不同步的,这意味着它线程不安全
- HashMap的key,value都可以是null
- HashMap中的映射不是有序的
HashMap & HashTable 的空间与时间的折中:
//HashTable构造函数:
Hashtable()
//构造一个新的,空的散列表,默认初始容量(11)和负载因子(0.75)。
Hashtable(int initialCapacity)
//构造一个新的,空的哈希表,具有指定的初始容量和默认负载因子(0.75)。
Hashtable(int initialCapacity, float loadFactor)
//构造一个新的,空的哈希表,具有指定的初始容量和指定的负载因子。
//HashMap构造函数:
HashMap()
//构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。
HashMap(int initialCapacity)
//构造一个空的 HashMap具有指定的初始容量和默认负载因子(0.75)。
HashMap(int initialCapacity, float loadFactor)
//构造一个空的 HashMap具有指定的初始容量和负载因子。
可以看到, HashMap & HashTable的构造函数非常相似, 其中都有一个初始容量
& 负载因子
:
这两个参数对 HashMap & HashTable 性能影响较大
- 初始容量是哈希表在创建时的容量, 提升初始容量可以减少hash表的冲突, 但是会增大空间开销
- 加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度, 表明对现有空间的使用程度
通常默认加载因子是0.75
这是在时间和空间成本上寻求一种折衷, 加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数 Hashtable 操作中,包括 get 和 put 操作,都反映了这一点)
合理的设置这俩可以在一定程度上优化性能
但是一般很少注意这个玩意
HashMap & HashTable的比较:
TreeMap:
- 底层使用R-B Tree储key, 与TreeSet相似
所以在查找上的时间复杂度为o( log(n) ) - 它存储类似于HashMap的键值对
- 只允许不同的键, 但可以有相同的值
- 不允许有null键, 但可以有多个null值。
- 元素排列顺序与添加顺序不相同
所以不允许使用下标(索引) 访问元素 - 添加元素后会自动对其排序
所以必须实现Comparator或Comparable接口 - 线程不安全
构造器:
- **TreeMap() **
使用其键的自然排序构造一个新的空树状图 - TreeMap(Comparator<? super K> comparator)
构造一个新的,空的树图,按照给定的比较器排序 - **TreeMap(Map<? extends K,? extends V> m) **
构造一个新的树状图,其中包含与给定地图相同的映射,根据其键的 自然顺序进行排序 - **TreeMap(SortedMap<K,? extends V> m) **
构造一个包含相同映射并使用与指定排序映射相同顺序的新树映射
常用的方法:
- void clear():
它从地图中删除所有键值对。 - void size():
返回此映射中存在的键值对的数量。 - void isEmpty():
如果此映射不包含键 - 值映射,则返回true。 - boolean containsKey(Object key):
'true'
如果地图中存在指定的键,则返回。 - 布尔的containsValue(对象键):
它返回'true'
如果一个指定的值被映射到地图上的至少一个键。 - Object get(Object key):
它检索value
指定的映射key
,如果此映射不包含键的映射,则返回null。 - Object remove(Object key):
它从地图中删除指定键的键值对(如果存在)。 - 比较器比较器():
它返回用于对此映射中的键进行排序的比较器,如果此映射使用其键的自然顺序,则返回null。 - Object firstKey():
它返回树映射中当前的第一个(最少)键。 - Object lastKey():
它返回树映射中当前的最后一个(最大)键。 - Object ceilingKey(Object key):
返回大于或等于给定键的最小键,如果没有这样的键则返回null。 - Object higherKey(Object key):
返回严格大于指定键的最小键。 - NavigableMap descendingMap():
它返回此映射中包含的映射的逆序视图。
WeakHashMap:
特点:
WeakHashMap大体上与HashMap相同, 但是:
-
WeakHashMap
是弱引用,而HashMap
是强引用
即当JVM内存不足时,HashMap
宁可抛出OutOfMemoryError
异常也不会回收其相应的没有被引用的对象,而我们的WeakHashMap
则会回收存储在其中但没有引用的对象
这里的没有被引用表示的是: WeakHashMap中储存的元素以键值对的形式存在, 当键仍然存在, 但已经没有映射的值时, 这个元素就有可能被销毁释放内存
LinkedHashMap:
继承自HashMap, 很多特征与方法都与HashMap重复
而其在设计上由于LinkedHashSet相似, 具有插入有序性, 所以这里不做记录了
IdentityHashMap:
这玩意与HashMap的区别就是, 其使用System.identityHashCode计算哈希散列码
此方法得出散列码的方式是通过对象的储存地址, 而不是对象的内容
所以, 其允许储存相同Key与相同value的元素, 其由于地址不同, 仍然被判定为不相等
并且, 在判定对象是否相等时, HashMap使用equals() , 而IdentityHashMap使用==
9.4 集合视图:
这玩意可以类比数据库中的概念:
类似于将集合类对象中的数据重新映射到一个数据集合中,但这个集合不是一个物理上存在的对象实体,而是对原集合类对象的再映射,数据物理地址未变,只是访问数据的接口变了
这玩意感觉用处不大, 先放着
下头仅仅是对书中的内容做一些摘录…
轻量级集合包装器:
List<String> strings = Arrays.asList("whz", "pyy");
List<String> whz = Collections.nCopies(100, "whz");
子视图:
//第一个索引包含在内,第二个索引则不包含在内。
List group2 = staff.subList(10,20);
SortedSet<E> subSet(E fromElement, E toElement);
SortedSet<E> tailSet(E fromElement);
SortedSet<E> headSet(E toElement);
不可修改的视图:
Collections还有几个方法,用于产生集合的不可修改视图
Collections.unmodifiableCollection();
Collections.unmodifiableList();
Collections.unmodifiableSet();
Collections.unmodifiableSortedSet();
Collections.unmodifiableNavigableSet();
Collections.unmodifiableMap();
Collections.unmodifiableSortedSet();
Collections.unmodifiableNavigableMap();
同步视图:
- 如果多个线程访问集合,就必须确保集合不会被意外地破坏。
- 类库的设计者使用视图机制来确保常规集合的线程安全,而不是实现线程安全的集合类。例如,
Collections
类的静态synchronizedMap
方法可以将任何一个映射表转换成同步方法访问的Map。
Map<String, Employee> employeeMap =
Collections.synchronizedMap(new HashMap<String, Employee>());
受查视图:
- 受查视图用来对泛型类型发生问题时提供调试支持
例如:
ArrayList<String> strings = new ArrayList<>();
ArrayList rawList = strings;
rawList.add(new Date());
这个错误的add命令在运行时检测不到。相反,只有在稍后的另一部分代码中调用get方法,将结果转化为String
时,这个类才会抛出异常。
- 受查视图可以探测到这个类问题,下面定义了一个安全列表
ArrayList<String> strings = new ArrayList<>();
List<String> stringList = Collections.checkedList(strings, String.class);
ArrayList rawList = (ArrayList) stringList;
rawList.add(new Date());
抛出java.lang.ClassCastException
异常
9.5 算法:
本部分介绍Java中较为实用的方法
类似于C++ STL算法, Java中也有大量的实用算法
总之, 这部分暂时就是针对各个算法的特性进行分类, 并对使用方法上做一定的学习
排序算法:
本部分介绍Collections.sort()算法 (注意是Collections, 而不是Collection)
Collection是前头容器类实现的一个接口, 而现在的Collections是一个Class, 其中包含大量算法
//API中的方法声明:
public static <T extends Comparable<? super T>> void sort(List<T> list);
public static <T> void sort(List<T> list,
Comparator<? super T> c);
可以看到其只支持List系列的对象 (因为Set系列自动排序, 所以不需要这玩意)
也是有两个版本, 分别支持自然排序
和定制排序
特性:
sort()排序使用一个稍微优化的归并排序算法,它快速且稳定,
- 使用优化的归并排序, 能够保证在o(nlog(n))范围内运行, 并在排序好的List上运行更快, 约o(n)
- 排序是稳定的, 对相等的元素不进行排序
- 被排序的List必须是可修改的, 但不能调整大小
- 所有元素都必须是可比较的
其中, 自然排序专有特性:
- 类必须支持Comparable接口
- 按照升序排列List
二分查找:
本部分介绍Collections.binarySearch() 二分查找算法
//API:
public static <T> int binarySearch(List<? extends Comparable<? super T>> list,
T key);
public static <T> int binarySearch(List<? extends T> list,
T key,
Comparator<? super T> c);
传入的对象也只是List, Set系列内部的红黑树可以快速查找, 不需要这玩意
特性:
- 使用之前必须对List升序排序, 否则结果是未定义的
无论是Comparable版本还是Comparator版本, binarySearch假定的情况都是List按照升序排列 - 返回值:
如果搜索到相应元素, 则返回其下标(索引), 其值>0, 便于判定
否则, 返回的是一个负值, 但是这个值可以计算出一个位置, 用于将查找的元素插入List中而不会破坏其有序性, 其计算公式
insertPoint=-returnValue-1;
//通常使用的方法是:
if(returnValue<0){
llist.add(-returnValue-1, element);
}
- 如果传入的List对象不支持随机访问(即没有实现RandomAccess接口), 则二分查找的性能会缩水为线性查找
即时间复杂度由o(log(n)) 变为 o(n)
简单算法:
本部分包括大量实现起来很简单的算法, 完全可以很快的手写出来, 但是使用API的话, 可以提升代码的可读性
常规数组操作:
- reverse-反转列表中元素的顺序。
- fill-覆盖列表中的每个元素,并具有指定的值。这个操作对于重新初始化一个列表非常有用。
- copy-接受两个参数,一个目标列表和一个源列表,并将源的元素复制到目标中,覆盖其内容。目标列表必须至少和源代码一样长。如果它较长,则目标列表中的其余元素不会受到影响。
- swap——在列表中的指定位置交换元素。
- addAll—将所有指定元素添加到集合中。要添加的元素可以单独指定,也可以作为数组来指定。
集合与数组的转换:
数组转化为集合:
可以使用Arrays.asList包装器:
//API:
@SafeVarargs
public static <T> List<T> asList(T... a);
集合转化为数组:
可以使用toArray方法, 这里以Set为例:
//API:
Object[] toArray();
<T> T[] toArray(T[] a);