这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
1、集合的引入
数组、集合是对多个数据进行存储操作的,简称容器。
在引入集合使用我们更多的使用的是数组。
1.1 数组的特点
- 数组一旦指定了长度,那么长度就被确定了,不可以更改了;
- 数组一旦声明了类型以后,数组中只能存放这个类型的数组,数组中只能存放同一种类型的数据。
1.2 数组的缺点
- 数组一旦指定了长度,那么长度就被确定了,不可以更改;
- 删除,增加元素,效率低;
- 数组中实际元素的数量是没有办法获取的,没有提供对应的方法或者属性来获取;
- 数组存储:有序,可重复;对于无序的,不可重复的数组不能满足要求
集合的引入正是为了解决了以上的缺点。
分类
集合主要包括两种
Collection
和Map
两种,Collection
存储着对象的集合,而Map
存储着键值对(两个对象)的映射表。
参考博客
2、Collection
2.1 List
存储的元素是有序的、可重复的
- ArrayList:
Object[]
数组; - Vector:
Object[]
数组,线程安全; - LinkedList:双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环)
2.1.1 说说它们的区别?从何说?
从何说:从底层组成,数据结构的特性说
2.1.2 ArrayList 和 LinkedList 的区别
- 底层数据结构:
ArrayList
底层使用的是Object
数组;LinkedList
底层使用的是双向链表
数据结构(JDK 1.6 之前为双向循环链表,JDK 1.7 后取消了循环); - 效率:
ArrayList
查找快(支持快速随机访问)、插入删除慢;LinkedList
查找慢、插入删除快; - 内存空间占用:
ArrayList
的空间浪费主要体现,在 list 列表的结尾会预留一定的容量空间,而LinkedList
的空间花费主要在它的每一个元素都需要消耗比ArrayList
更多的空间,存放直接前驱和直接后继以及数据本身。 - 是否保证线程安全:
ArrayList
和LinkedList
都是不同步的,也就是不保证线程安全;
2.1.3 ArrayList 的扩容机制
从添加元素开始,判断是否扩容,默认扩容至原容器的 1.5倍左右(oldCapacity为奇数时,进行 oldCapacity >> 1)
以无参构造的
ArrayList
为例分析
ArrayList
的成员变量和三个构造函数
1 | arduino复制代码 public class ArrayList<E> extends AbstractList<E> |
- ArrayList 的 add() 方法
1 | arduino复制代码 /** |
- ensureCapacityInternal(int minCapacity) 方法
minCapacity = size + 1,意思就是新增元素时,该容器至少的容量大小是多于当前元素个数的 1 个
1 | typescript复制代码 // 第一次调用时,此时 elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA, |
- ensureExplicitCapacity(int minCapacity) 方法
1 | scss复制代码 // 第一次调用时,此时 minCapacity = 10,当前 elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA,长度为0 |
* 当我们要 add 进第 1 个元素到 ArrayList 时,elementData.length 为 0 (因为还是一个空的 list),因为执行了 `ensureCapacityInternal()` 方法 ,所以 minCapacity 此时为 10。此时,`minCapacity - elementData.length > 0`成立,所以会进入 `grow(minCapacity)` 方法。
* 当 add 第 2 个元素时,minCapacity 为 2,此时 e lementData.length(容量)在添加第一个元素后扩容成 10 了。此时,`minCapacity - elementData.length > 0` 不成立,所以不会进入 (执行)`grow(minCapacity)` 方法。
* 添加第 3、4···到第 10 个元素时,依然不会执行 grow 方法,数组容量都为 10。
- grow(int minCapacity)方法
1 | arduino复制代码 /** |
* 当 add 第 1 个元素时,oldCapacity 为 0,经比较后第一个 if 判断成立,newCapacity = minCapacity(为 10)。但是第二个 if 判断不会成立,即 newCapacity 不比 MAX\_ARRAY\_SIZE 大,则不会进入 `hugeCapacity` 方法。数组容量为 10,add 方法中 return true,size 增为 1。
* 当 add 第 11 个元素进入 grow 方法时,newCapacity 为 15,比 minCapacity(为 11)大,第一个 if 判断不成立。新容量没有大于数组最大 size,不会进入 hugeCapacity 方法。数组容量扩为 15,add 方法中 return true,size 增为 11。
* 以此类推······
2.1.4 System.arraycopy 和 Arrays.copyOf 的区别
System.arraycopy()
将一个数组复制到目标数组中,目标数组长度必须足够,否则报错ArrayIndexOutOfBoundsException
1 | csharp复制代码 // src:源数组;srcPos:源数组起始下标;dest:目标数组;destPos:目标数组起始下标;length:要复制的源数组的长度 |
Arrays.copyOf
返回一个包含源数组所有元素的新数组,长度自定义
1 | csharp复制代码 public static int[] copyOf(int[] original, int newLength) |
System.arraycopy()
需要原数组
和目标数组
,将原数组拷贝到自定义的数组中,而且可以选择原数组的拷贝起点、长度、放在目标数组的位置;Array.copyOf()
是系统自动在内部新建了一个数组,并返回该数组。(源码内部调用的仍是System.arraycopy()
)
2.2 Set
存储的元素是无序的、不可重复的
- HashSet:(无序、唯一)基于
HashMap
实现的,底层采用HashMap
来保存元素,线程不安全,可以存储null
值; - LinkedHashSet:
LinkedHashSet
是HashSet
的子类,并且其内部是通过LinkedHashMap
来实现的,能够按添加顺序遍历; - TreeSet:(有序、唯一)红黑树(自平衡的排序二叉树)
2.3 Comparable 和 Comparator 区别
- 它们都是接口,都必须实现并重写相应方法才能使用,目的是为了
自定义排序
; Comparable
接口是排序的元素(一般为自定义类)实现的,并重写compareTo(T t)
方法,在方法中自定义排序方式;
+ `compareTo(T t)` 返回的是 `int` 类型,一般记忆为:`this`的值 大于 参数的值,return 1;`this`的值 小于 参数的值,return -1;**升序**
+ `Integer`、`String` 等类都实现了 `Comparable` 接口;
1 | typescript复制代码 public class Person implements Comparable<Person>{ |
1 | csharp复制代码 /** |
1 | ini复制代码 ============= 输出 ============ |
Comparator
接口需自定义实现类,或在排序时使用匿名类实现,并重写compare(T t1,T t2)
方法
+ `compare(T t1,T t2)` 返回的是 `int` 类型,一般记忆为: `p1`的值 大于 `p2`的值,return 1;`p1`的值 小于 `p2`的值,return -1;**升序**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
csharp复制代码 /**
* 排序是实现 Comparator 接口,自定义排序
*/
public static void comparatorTest(){
System.out.println();
System.out.println("+++++++ comparatorTest ++++++++");
List<Person> personList = new ArrayList<>();
personList.add(new Person(3,"小贱",22));
personList.add(new Person(8,"小红",5));
personList.add(new Person(7,"小华",15));
personList.add(new Person(9,"小明",20));
// 定制排序
Collections.sort(personList, new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
if(p1.getNum() > p2.getNum()){
return -1;
} else if(p1.getNum() < p2.getNum()){
return 1;
}
return 0;
// 因为 num 定义为了 Integer 类型,这里也可以使用 Integer 定义的 compareTo 方法
// return p1.getNum().compareTo(p2.getNum()); // 默认升序
}
});
Iterator iterator = personList.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
1
2
3
4
5
6
ini复制代码 ========= 输出 =============
+++++++ comparatorTest ++++++++
Person{num=9, name='小明', age=20}
Person{num=8, name='小红', age=5}
Person{num=7, name='小华', age=15}
Person{num=3, name='小贱', age=22}
本文转载自: 掘金