Java基础-集合框架-1、集合关系及概念
1. 集合类继承关系
2. 初始容量 加载因子
初始容量 | 加载因子 | ||||
---|---|---|---|---|---|
ArrayList | 0 | 1.5X | |||
LinkedList | |||||
Vector | 2X | ||||
HashMap | |||||
CurrentHashMap |
3. List,Set,Map 三者的区别
Java 容器分为 Collection 和 Map 两大类,Collection 集合的子接口有 Set、List、Queue 三种子接口。我们比较常用的是 Set、List,Map 接口不是 collection 的子接口。
Collection 集合主要有 List 和 Set 两大接口
1. List:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个 null 元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。
2. Set:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一
个 null 元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及
TreeSet。
Map 是一个键值对集合,存储键、值和之间的映射。 Key 无序,唯一;value 不要求有序,允许重复。Map 没有继承于 Collection 接口,从 Map 集合中检索元素时,只要给出键对象,就会返回对应的值对象。可以存 null 键、null 值
Map 的常用实现类:HashMap、TreeMap、HashTable、LinkedHashMap、ConcurrentHashMap(不可以存 null 键、null 值)
4. 遍历 List
有哪些不同的方式?每种方法的实现原理是什么?Java 中 List 遍历的最佳实践是什么?
遍历方式有以下几种:
for 循环遍历,基于计数器。在集合外部维护一个计数器,然后依次读取每一个位置的元素,
当读取到最后一个元素后停止。迭代器遍历,Iterator。Iterator 是面向对象的一个设计模式,目的是屏蔽不同数据集合的特
点,统一遍历集合的接口。Java 在 Collections 中支持了 Iterator 模式。foreach 循环遍历。foreach 内部也是采用了 Iterator 的方式实现,使用时不需要显式声明
Iterator 或计数器。优点是代码简洁,不易出错;缺点是只能做简单的遍历,不能在遍历过程中操作数据集合,例如删除、替换。
最佳实践:
Java Collections 框架中提供了一个 RandomAccess 接口,用来标记 List 实现是否支持 Random Access。
如果一个数据集合实现了该接口,就意味着它支持 Random Access,按位置读取元素的平均
时间复杂度为 O(1),如 ArrayList。
如果没有实现该接口,表示不支持 Random Access,如 LinkedList。
推荐的做法就是,支持 Random Access 的列表可用 for 循环遍历,否则建议用 Iterator 或 foreach 遍历。
5. elementData 加上 transient
ArrayList 中的数组定义如下:
private transient Object[] elementData;
再看一下 ArrayList 的定义:
1 |
|
可以看到 ArrayList 实现了 Serializable 接口,这意味着 ArrayList 支持序列化。transient 的作用是说不希望 elementData 数组被序列化,重写了 writeObject
实现:
1 |
|
每次序列化时,先调用 defaultWriteObject() 方法序列化 ArrayList 中的非 transient 元素,然后遍历 elementData,只序列化已存入的元素
,这样既加快了序列化的速度,又减小了序列化之后的文件大小。即size为0时,不会走序列化流程
6. List 和 Set 的区别
List , Set 都是继承自 Collection 接口
List 特点:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个 null 元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。
Set 特点:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个 null 元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及
TreeSet。
List 支持 for 循环,也就是通过下标来遍历,也可以用迭代器,但是 set 只能用迭代,因为他无
序,无法用下标来取得想要的值。
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List 可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起
其他元素位置改变
7. Array 和 List 之间转换
Array 转 List: Arrays. asList(array)
List 转 Array:List 的 toArray() 方法
注意避坑,详情请见 Java基础-集合框架-2、集合类的坑
8. comparable 和 comparator 的区别
comparable 接口实际上是出自 java.lang 包,它有一个 compareTo(Object obj) 方法用来排序
comparator 接口实际上是出自 java.util 包,它有一个 compare(Object obj1, Object obj2) 方法用来排序
一般我们需要对一个集合进行简单自定义排序时,我们就要重写 compareTo 方法或 compare 方法,
当我们需要对某一个集合进行复合自定义排序时,比如一个 song 对象中的歌名和歌手名分别采用一种排序方法的话,我们可以使用内部比较器 (重写 compareTo 方法) 或者使用多个 Comparator 类 (重写 compare 方法) 来实现歌名排序和歌星名排序,复合 comparator 时可以使用两个参数版的 Collections.sort(List<T> list, Comparator<? super T> c)
。
8.1. compareTo 方法
实体类实现 comparable 接口,重写 compareTo 方法,来构成内部比较器
8.2. compare 方法
比较器类实现 comparator 接口,重写 compare 方法,来构成外部比较器,使用多态的思想,比较方式更加灵活
8.3. Collections.sort
Collections 是一个工具类,sort 是其中的静态方法,是用来对 List 类型进行排序的,它有两种参数形式:
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
1.对于 String 或 Integer 这些已经实现 Comparable 接口的类来说,可以直接使用 Collections.sort 方法传入 list 参数来实现默认方式(正序)排序;
2.如果不想使用默认方式(正序)排序,可以通过 Collections.sort 传入第二个参数类型为 Comparator 来自定义排序规则;
3.对于自定义类型 (如本例子中的 Emp),如果想使用 Collections.sort 的方式一进行排序,可以通过实现 Comparable 接口的 compareTo 方法来进行,如果不实现,则参考第 2 点;
4.jdk1.8 的 Comparator 接口有很多新增方法,其中有个 reversed() 方法比较实用,是用来切换正序和逆序的
9. 迭代器 Iterator
9.1. 迭代器原理
Iterator 接口提供遍历任何 Collection 的接口。我们可以从一个 Collection 中使用迭代器方法来获取迭代器实例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允许调用者在迭代过程中移除元素。因为所有 Collection 接继承了 Iterator 迭代器
增强 for 循环底层也是用了迭代器
9.2. Iterator 怎么使用?有什么特点?
Iterator 使用代码如下:
1 |
|
Iterator 的特点是只能单向遍历,但是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException 异常。
9.3. iterator() Iterator Iterable 关系
9.4. ListIterator
10. 如何边遍历边移除 Collection 中的元素?
边遍历边修改 Collection 的唯一正确方式是使用 Iterator.remove() 方法,如下:
一种最常见的错误代码如下:运行以上错误代码会报 ConcurrentModificationException 异常。这是因为当使用 foreach(for(Integer i : list)) 语句时,会自动生成一个 iterator 来遍历该 list,但同时该 list 正在被 Iterator.remove() 修改。Java 一般不允许一个线程在遍历 Collection 时另一个线程修改它。
11. Iterator 和 ListIterator 有什么区别?
Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。
Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。
ListIterator 实现 Iterator 接口,然后添加了一些额外的功能,比如添加一个元素、替换一个元
素、获取前面或后面元素的索引位置。
12. RandomAccess 接⼝
1 |
|
查看源码我们发现实际上 RandomAccess 接⼝中什么都没有定义。所以,在我看来
RandomAccess 接⼝不过是⼀个标识罢了。标识什么? 标识实现这个接⼝的类具有随机访问功能。
在 binarySearch() ⽅法中,它要判断传⼊的 list 是否 RamdomAccess 的实例,如果是,调
⽤ indexedBinarySearch() ⽅法,如果不是,那么调⽤ iteratorBinarySearch() ⽅法
1 |
|
private static final int BINARYSEARCH_THRESHOLD = 5000;
ArrayList 实现了 RandomAccess 接⼝, ⽽ LinkedList 没有实现。因为 ArrayList 底层是数组,⽽ LinkedList 底层是链表。数组天然⽀持随机访问,时间复杂度为 O(1),所以称为快速随机访问。链表需要遍历到特定位置才能访问特定位置的元素,时间复杂度为 O(n),所以不⽀持快速随机访问。
ArrayList 实现了 RandomAccess 接⼝,就表明了他具有快速随机访问功能。 RandomAccess 接⼝只是标识,并不是说 ArrayList 实现 RandomAccess 接⼝才具有快速随机访问功能的!
13. Serializable 接口
[[001-基础知识专题-关键字和接口-1、Serializable接口]]