类型概览
Collection (List 、 Set 、 Queue) 和 Map
集合
Collection
类型在每个槽中只能保存一个元素,包含List 、 Set 、 Queue
集合(
Collection
) :一个独立元素的序列,这些元素都服从一条或多条规则。
List
必须以插入的顺序保存元素
Set
不能包含重复元素
Queue
只能在集合一端插入对象,并从另一端移除对象。按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
映射
Map
在每个槽中存放了两个元素,即键 (key)
和与之关联的值 (value)
映射(
Map
) : 一组成对的“键值对”对象,允许使用键来查找值。ArrayList
使用数字来查找对象,因此在某种意义上讲,它是将数字和对象关联在一起。map
允许我们使用一个对象来查找另一个对象,它也被称作关联数组(associative array
),因为它将对象和其它对象关联在一起;或者称作字典(dictionary
),因为可以使用一个键对象来查找值对象,就像在字典中使用单词查找定义一样。 pp 是强大的编程工具。
Collection
构建集合
public static void main(String[] args) {
buildCollection();
}
// 通过另一个collection构建
private static void buildCollection() {
Collection<String> collection =
new ArrayList<>();
collection.add("test");
Collection<String> collection1 =
new ArrayList<>(collection);
System.out.println("buildCollection with another collection: " + collection1);
}
不同的集合类型对于构建的方法都不相同
- ArrayList
调用Collection的toArray方法然后复制
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
-
LinkedList
调用内部的addAll
方法(转成数组,遍历添加) -
其他类型
没有每个类型都看,就看了下ArrayDeque,HashSet
,发现都是直接使用Collection
的for-in
遍历添加为什么没有先
toArray
再遍历数组呢?个人理解:一般来说,构建时都是添加同类型的集合,
List
类型对于toArray
方法都有自己的实现
ArrayDeque,HashSet
则是使用抽象父类(AbstractCollection
)的toArray
抽象父类中就是通过迭代器转换的,那我还不如直接用迭代器遍历新增,就没必要中间再用迭代器转一道数组了
addAll
/**
* 通过 Collections.addAll方法构建
*/
private static void buildCollectionWithAddAll() {
Collection<Integer> collection =
new ArrayList<>();
Collections.addAll(collection,1,2,3,4,5);
System.out.println("buildCollectionWith Collections.addAll method: " + collection);
Collection<Integer> anotherCollection =
new ArrayList<>(Arrays.asList(1,2,3));
collection.addAll(anotherCollection);
System.out.println("buildCollectionWith instance addAll method: " + collection);
}
工具类就是迭代器循环遍历添加了
public static <T> boolean addAll(Collection<? super T> c, T... elements) {
boolean result = false;
for (T element : elements)
result |= c.add(element);
return result;
}
Arrays.asList
/**
* 通过Arrays.asList构建
*/
private static void buildCollectionWithArrays() {
Collection<Integer> collection1 =
new ArrayList<>(Arrays.asList(1,2,3,4,5));
System.out.println("buildCollection with Arrays: " + collection1);
}
Collection方法概览
标注为since 1.8的都在接口中有default
默认实现
-
增
add,addAll -
删
remove,removeAll,removeIf(since 1.8,使用了函数式接口Predicate
),clear -
改
无,因为有的集合没有这个功能,例如Set,Queue
-
查
size,iterator(通过继承Iterable
接口)
没有获取某个元素的接口,因为不同集合获取元素的方法不一样(List
和Queue
),有的甚至没有获取元素的功能,例如Set
-
判断
isEmpty,contains,containsAll -
转数组
toArray(),toArray(T[] a) -
流相关(since 1.8)
stream,spliterator,parallelStream(通过继承Iterable
)
List
-
ArrayList ,擅长随机访问元素,但在 List 中间插入和删除元素时速度较慢。
-
LinkedList ,它通过代价较低的在 List 中间进行的插入和删除操作,提供了优化的顺序访问。 LinkedList 对于随机访问来说相对较慢
List 接口概览
这里列举一下比Collection多出来的
-
增
add,addAll
都增加了从某个索引后添加的方法 -
删
remove,removeAll
都增加了从某个索引后添加的方法 -
改
set,replaceAll(since 1.8) -
查
获取元素:get
获取索引:indexOf,lastIndexOf
遍历:iterator,listIterator -
排序
sort (since 1.8) -
切片
subList
listIterator
相比普通Iterator多了新增,修改的方法,还提供遍历元素的索引,还可以指定迭代器开始的位置
replaceAll
replaceAll就是通过listIterator修改每个元素,修改的方法由参数提供(函数式接口)
LinkedList
LinkedList 还添加了一些方法,使其可以被用作栈、队列或双端队列(deque)
- getFirst() 和 element() 是相同的,它们都返回列表的头部(第一个元素)而并不删除它,如果 List 为空,则抛出 NoSuchElementException 异常。 peek() 方法与这两个方法只是稍有差异,它在列表为空时返回 null 。
- removeFirst() 和 remove() 也是相同的,它们删除并返回列表的头部元素,并在列表为空时抛出 NoSuchElementException 异常。 poll() 稍有差异,它在列表为空时返回 null 。
- addFirst() 在列表的开头插入一个元素。
- offer() 与 add() 和 addLast() 相同。 它们都在列表的尾部(末尾)添加一个元素。
- removeLast() 删除并返回列表的最后一个元素
Queue
一头进,一头出
小坑:ArrayQueue
没有实现Queue
......
Queue接口概览
-
入队
offer -
出队
poll(返回null),remove(抛异常) -
查找队首元素
peek(返回null),element(抛异常)
PriorityQueue
两种方式实现排序
- 队列元素实现
Compable接口
- 构造时可以添加比较器(
Comparator
),用于对队列进行排序,在初始化或者入队出队时会重新进行排序
排序相关的基本都是这个套路,后面的TreeSet
,TreeMap
也是如此
Deque
双端队列(Double Ended Queue) 队首可进可出,队尾同样可进可出
所以通过Deque可以实现栈的功能
Deque方法概览
Deque 也继承Queue,但不建议使用Queue的方法
-
入队
addFirst,offerFirst
addLast,offerLast -
出队
removeFirst,pollFirst
pollFirst,pollLast
栈相关接口
-
入栈
push,通过队首入队实现(普通队列只能实现队尾入队,队首入队,其实就是插队。。) -
出栈
pop,通过队首出队实现
question:能不能队尾入队,队尾出呢?
Stack
堆栈是“后进先出”(LIFO)集合
Java 1.0 中附带了一个
Stack
类,结果设计得很糟糕(为了向后兼容,我们被迫一直忍受 Java 中的旧设计错误)。Java 6 添加了ArrayDeque
,其中包含直接实现堆栈功能的方法
Deque
接口就是Java6之后提出的
由于ArrayDeque中由很多其他的方法,所以为了使用一个更好的Stack,我们可以自己编写一个Stack,然后使用组合的方式使用Deque的实现
Set
接口概览
相比Collection,没有新增的接口
集合操作
- 交集 contains
- 并集 addAll
- 差集 removeAll
Set类型
- HashSet
- LinkedHashSet
- TreeSet
TreeSet中的元素需要实现Comparable接口
Map
-
HashMap
顺序不是插入顺序,因为 HashMap 实现使用了非常快速的算法来控制顺序 -
LinkedHashMap
在保持 HashMap 查找速度的同时按照键的插入顺序来排序 -
TreeMap
把所有的键按照比较规则来排序
key值必须实现Comparable
接口,或者构建TreeMap
时传入对应泛型类型的Comparator
例如key值是User
,就应该传入一个Comparator<? super User>
的比较器,Comparator<User>
或者Comparator<Object>
Map 接口概览
这里只列举常用的
-
增
put,putAll,putIfAbsent -
删
remove -
改
put,putAll,putIfAbsent,replace,merge -
查
获取元素:get,getOrDefault
遍历:entrySet,keySet,values
获取大小:size -
判断
isEmpty
since1.8
Java8中对Map接口新增了很多方法,用于封装一些常见的逻辑判断
-
增
putIfAbsent -
remove
增加了一个重载的remove,remove(Object key, Object value)
Object curValue = get(key);
default boolean remove(Object key, Object value) {
Object curValue = get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
}
-
改
- putIfAbsent
- merge
hashmap.merge(key, value, remappingFunction)
如果 key 对应的 value 不存在,则返回该 value 值,如果存在,根据映射函数修改原来的值
- replace
存在才修改,不存在返回null - replaceAll
遍历设值
-
查
getOrDefault
参考资料
《OnJava8》