文章目录
1. 集合简介
集合就是存放对象的容器。定义了对多个对象进行操作的常用方法。可实现数组的功能。
相比于数组来说,集合有以下的区别:
- 数组长度固定,集合长度不固定
- 数组可以存储基本类型和引用类型,集合只能存储引用类型(对象)
- 集合中存放的都是对象的引用,而非对象本身。所以我们称集合中的对象就是集合中对象的引用。对象本身还是存放在堆内存中
- 数组存储的元素必须是同一个数据类型,集合存储的对象可以是不同数据类型的
Java集合类主要由两个根接口 Collection
和 Map
派生出来的。
其中, Collection
又派生出了三个子接口,其关系如下图所示:
- List代表了有序可重复集合,可直接根据元素的索引来访问
- Set代表无序不可重复集合,只能根据元素本身来访问
- Queue是队列集合
Map
接口的派生关系如下:
下面对 Collection
和 Map
接口的一些重要实现类和接口进行介绍,涉及到实现类的具体方法时,该文章仅列出具体的函数签名,就不一一对各个函数进行实验和举例了。
2. Collection体系
Collection
接口定义的方法中常用的如下:
方法签名 | 功能 |
---|---|
boolean add(Object obj) | 添加一个对象 |
boolean addAll(Collection c) | 将一个集合中的所有对象添加到此集合中 |
void clear() | 清空此集合中的所有对象 |
boolean contains(Object o) | 检查此集合中是否包含o对象 |
boolean equals(Object o) | 比较此集合是否与指定对象相等 |
boolean isEmpty() | 判断此集合是否为空 |
boolean remove(Object o) | 在此集合中移除o对象 |
int size() | 返回此集合中的元素个数 |
Object[ ] toArray() | 将此集合转换成数组 |
Collection
接口的实现类都实现了其方法,所以上述方法在任何一个实现类中都可以使用。
2.1 List
List
接口也定义了一系列常用的方法,一般的常用方法如下,
方法签名 | 功能 |
---|---|
void add(int index, Object o) | 在index位置插入对象o |
boolean addAll(int index, Collection c) | 将一个集合中的元素添加到此集合中的index位置 |
Object get(int index) | 返回集合中指定位置的元素 |
List subList(int fromIndex, int toIndex) | 返回 [fromIndex, toIndex) 之间的集合的元素 |
list.indexOf() | 获取某个元素所在索引 |
List
接口的实现类有 ArrayList, Vector, LinkedList
,其中最重要的当属 ArrayList
了,下面我们分别对其进行讲解。
2.1.1 ArrayList
ArrayList
的底层是用数组进行实现的,这就导致了其随机查询的速度快,增加删除元素的速度慢的特点。
其默认初始的数组大小是10个,当元素逐渐增长导致空间不够的时候,数组长度每次增长为原来的1.5倍,注意,如果没有向其中添加任何元素,那么其容量为0。
ArrayList
是线程不安全的,没有同步方法,如果需要进行多线程访问必须自己实现访问同步,即创建示例如下:
List<String> list = Collections.synchronizedList(new ArrayList<>());
ArrayList
的常用方法列表如下:
方法签名 | 功能 |
---|---|
void sort(Comparator<? super E> c) | 对 ArrayList 的元素进行排序,动态改变数组,无返回值 |
Object clone() | 复制一份 ArrayList |
E set(int index, E element) | 替换 ArrayList 中指定索引的元素 |
Object[] toArray() | 将 ArrayList 转为数组 |
boolean retainAll(Collection<?> c) | 保留 ArrayList 中在指定集合中也存在的那些元素 |
void removeRange(int fromIndex, int toIndex) | 删除 [fromIndex, toIndex) 之间的元素 |
2.1.2 LinkedList
LinkedList
的底层是用链表进行实现的,因此其查询的速度慢,但是删除和增加元素的速度快。
LinkedList
也是线程不同步的,如果需要进行多线程访问必须自己实现访问同步,即创建示例如下:
List<String> list = Collections.synchronizedList(new LinkedList<>());
LinkedList
实现了 Queue
接口,可作为队列使用, 实现了 List
接口,可进行列表的相关操作,实现了 Deque
接口,可作为队列使用,实现了 Cloneable
接口,可实现克隆,实现了 java.io.Serializable
接口,即可支持序列化,能通过序列化去传输。
LinkedList
常用方法如下:
方法签名 | 功能 |
---|---|
boolean offer(E e) | 向链表末尾添加元素,返回是否成功,成功为 true,失败为 false |
boolean offerFirst(E e) | 头部插入元素,返回是否成功,成功为 true,失败为 false。 |
boolean offerLast(E e) | 尾部插入元素,返回是否成功,成功为 true,失败为 false。 |
E poll() | 删除并返回第一个元素 |
E peek() | 返回第一个元素 |
E removeFirst() | 删除并返回第一个元素。 |
E removeLast() | 删除并返回最后一个元素 |
2.1.3 Vector
Vector
底层也是数组实现的,当然也就拥有了其查询速度快,增加删除元素慢的特点。
但是, Vector
与 ArrayList
不同的地方在于, Vector
的运行速度比 ArrayList
慢一些,其访问是线程安全的。
2.2 Set
Set
里面的元素是无序的,这也就导致里面的元素是没有下标的,无法靠下标的索引来进行定位,Set
集合的特点是不保存重复的元素,在 Set
接口的实现类中,我们重点来看一下 HashSet
这个实现类。
HashSet
的存储结构是哈希表,实现方式是数组与红黑树的结合,HashSet
里面的元素可以包含 null
元素,HashSet
同样不是线程安全的,如果需要多线程进行访问,那么可以使用下面的方法进行创建:
HashSet<String> s = Collections.synchronizedSet(new HashSet<>());
HashSet
的数组初始容量为16,负载因子默认为0.75,即如果元素达到当前总容量的0.75则自动增加容量。同时,该实现类也可以重写 hashCode
和 equals
方法, hashCode
方法是哈希表存储的映射方式,重写后则采用重写的映射方式,equals
是判断是否重复的方法,如果其返回为 true
,则认为是重复的。
HashSet
内的元素无法进行索引,所以如果想要访问其中的元素,可以对其进行迭代遍历,其经常使用的方法如下:
方法签名 | 功能 |
---|---|
boolean contains(Object o) | 如果此set包含指定的元素,则返回 true |
int size() | 返回此集合中的元素数(基数) |
boolean remove(Object o) | 如果存在,则从该集合中移除指定的元素 |
2.3 Queue
Queue
是一种先进先出的数据结构,其下面还有一个接口 Deque (双端队列),它的实现类有多种,包括LinkedList(链表)实现,ArrayDeque(数组实现的双端队列),PriorityQueue(优先级队列)等,其主要使用的方法为:
方法签名 | 功能 |
---|---|
boolean add(E e) | 增加一个元素 |
boolean offer(E e); | 增加一个元素到队尾并返回true |
E poll() | 将队首的元素删除,并返回该元素 |
E peek() | 返回队首的元素,但不进行删除操作 |
2.4 Collection工具类
Collection
除了存储意外,还实现了一些集合常用的方法,可以方便的对集合进行操作。
方法签名 | 功能 |
---|---|
void shuffle(List<?> list) | 打乱list集合元素的顺序 |
<T> void sort(List<T> list); | 排序 |
<T> void sort(List<T> list, Comparator<T> c) | 根据指定的规则进行排序 |
<T> int binarySearch(List<T> list,T key) | 以二分查找法查找元素 |
<T> void copy (List<T> dest,List<T> src) | 拷贝集合中的元素 |
<T> int fill(List<T> list, T obj) | 使用指定的元素填充集合 |
<T> int binarySearch(List<T> list,T key) | 以二分查找法查找元素 |
<T> void swap(List<?> list, int i, int j) | 交换集合中指定的位置的元素 |
<T> void max/min(Collection<T> coll) | 根据默认的自然排序获取最大/最小值 |
3. Map 体系
Collection
父接口需要掌握的基本就是上面的几个实现类和接口,接下来我们对 Map
父接口的重要实现类进行介绍, Map
存储的特点是 Key-Value 形式的方式进行存储,其中存储的元素无序、无下标,Key 不能重复,Value可以重复。
Map
父接口下的实现类有很多,它们都是线程不安全的,比如:
- HashMap: 使用哈希表实现的散列表,提供常数时间的插入和检索操作
(
O
(
1
)
)
(O(1))
(O(1)),不保证遍历顺序,即迭代器返回元素的顺序是不确定的,允许存储
null
键和null
值。 - TreeMap: 基于红黑树实现的有序映射,按照键的自然顺序或者自定义比较器进行排序,查找、插入、删除操作的时间复杂度为
O
(
l
o
g
n
)
O(log n)
O(logn),不允许存储
null
键,但可以存储null
值。 - WeakHashMap: 基于哈希表实现的弱键映射,键是使用弱引用存储的,当键没有强引用时,在垃圾回收时可能被自动移除,适用于临时缓存和内存敏感的应用场景,允许存储
null
键和null
值。 - LinkedHashMap: 基于哈希表和双向链表实现,保持元素的插入顺序或者访问顺序,遍历顺序可以是插入顺序或者访问顺序,查找操作的时间复杂度为 O ( 1 ) O(1) O(1) ,允许存储 null 键和 null 值。
- IdentityHashMap: 基于哈希表实现的特殊映射,通过引用相等性比较键和值,键和值的相等性是基于对象的内存地址而不是
equals
方法,不保证遍历顺序,允许存储null
键和null
值。
除此之外还有一些类型的 Map
,我们这里主要了解 HashMap
。
3.1 HashMap
HashMap
是一个散列表,它存储的内容是键值对(key-value)映射,HashMap
是线程不安全的,如果想要创建一个线程安全的 HashMap
,那么可以使用下面的语句初始化:
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
HashMap
常用的方法如下:
方法签名 | 功能 |
---|---|
V put(K key, V value) | 将键/值对添加到 hashMap 中 |
void clear() | 删除 hashMap 中的所有键/值对 |
int size() | 计算 hashMap 中键/值对的数量 |
boolean remove(Object key, Object value) | 删除 hashMap 中指定键 key 的映射关系 |
boolean containsKey(Object key) | 检查 hashMap 中是否存在指定的 key 对应的映射关系 |
boolean containsValue(Object value) | 检查 hashMap 中是否存在指定的 value 对应的映射关系 |
V get(Object key) | 获取指定 key 对应对 value |
V getOrDefault(Object key, V defaultValue) | 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值 |
Set<Map.Entry<K,V>> entrySet() | 返回 hashMap 中所有映射项的集合集合视图。 |
Set<K> keySet() | 返回 hashMap 中所有 key 组成的集合视图 |
Collection<V> values() | 返回 hashMap 中存在的所有 value 值 |