目录
4.Collection接口的方法(List、Set实现类通用)
A. ArrayList list = new ArrayList<>();
前言
前面说了java的基本类型和引用类型,有说到数组的相关内容和其内存图,但是数组的用处非常局限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。获取数据中实际元素的个数的需求,数组没有现成的属性或方法可用。数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
于是这次要说到以后要经常用到的java容器———集合。
提示:以下是本篇文章正文内容,下面案例可供参考
一、集合的家族谱
Java的容器主要分为2个大类,即Collection和Map。Collection代表着集合,类似数组,只保存一个数字。而Map则是映射,保留键值对两个值。
List 、Queue、 Set、 Map 这四个的区别。
A.List(对付顺序的好帮手): 存储的元素是有序的、可重复的。
B.Set (注重独一无二的性质):存储的元素是无序的、不可重复的。
C.Queue (实现排队功能的叫号机):按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。
D.Map (用 key 来搜索的专家) :使用键值对(key-value)存储,类似于数学上的函数 y=f(x),“x” 代表 key,“y” 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。
1.List
2. Set
3.Map
4.Collection接口的方法(List、Set实现类通用)
List接口和Set接口下的实现类都实现了Collection接口,所以类的方法用法命名都差不多,更多细节可查看API文档 Java 8 中文版 - 在线API手册 - 码工具 (matools.com)
5.Map接口实现方法(Map实现类通用)
更多细节可查看API文档 Java 8 中文版 - 在线API手册 - 码工具 (matools.com)
二、ArrayList底层原码的详解
1.前菜
在说ArrayList底层原码之前要说两个重要的点:
A.集合里面装的都是引用类型的数据,所以即使是add()或者put()方法装入8种基本类型数据之前,基本数据会自动装箱,变成包装类型(引用类型),就可以放入到集合中。
B.集合有一个好兄弟,叫做泛型(< Integer >),因为集合可以添加任意引用类型的数据,在现实的业务中不够严谨,所以需要好兄弟泛型来限制可以加入到集合的数据的数据类型。泛型里面也是不允许出现基本类型的,都是引用类型。
没加泛型前:
加入泛型后
2.底层原码解析
示例代码
public class ArrayListType {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);//第一次循环第一次添加数据时,集合扩容到默认值10
}
list.add(10);//添加第11个元素时不够装了,集合扩容1.5倍到15
System.out.println(list);
}
}
(1)概况
(2)源码用到的常量和变量
elementDate : 就是底层代表集合的一个对象数组 Object[ ] elementData 操作集合就是操作这个对象数组
DEFAULTCAPACITY_EMPTY_ELEMENTDATA:就是一个空的对象数组,在new 一个空的ArrayList时,将这个值赋给elementDate,还有扩容的时候判断是否为空数组,是否走默认扩容
DEFAULT_CAPACITY:就是空的ArrayList在添加第一个数据的时候,先扩容到默认初始容量为10 ,要用到这个数值。
size:集合中元素的个数。
modCount:记录操作集合的次数。
minCapacity:先提前加入一个元素后集合元素的个数。(size+1)
(3)分步源码详解
public class ArrayListType {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);//第一次循环第一次添加数据时,集合扩容到默认值10
}
list.add(10);//添加第11个元素时不够装了,集合扩容1.5倍到15
System.out.println(list);
}
}
A. ArrayList<Integer> list = new ArrayList<>();
这一步创建一个空的数组。debug进去
将 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 这个空的对象数组赋值给集合的本体elementDate。
B.第一层for循环 list.add(0);
将0这个基本类型自动装箱。(Integer.valueof(0) )
将要加入的数据 e , 集合数组 elementData, 集合包含数据的个数 size 传入内层add 方法
s就是传入的size , s == elementDate.length 代表集合包含数据的个数等于集合的最大容量了,需要扩容了。进入grow()方法返回一个扩容后的对象数组。
size+1 得到 minCapacity(当作加入数据后集合数据的个数)传入内层grow()
将minCapacity参数引入new Capacity()方法,返回一个整形数据,代表扩容后集合的容量。
注意:new Capacity()方法要么返回默认容量大小10,或扩容后的1.5倍原容量。
这个方法中先得到oldCapacity老集合的容量,以倍数扩容的newCapacity新容量,进入if模块,发现新容量newCapacity小于加入数据的集合数据的个数minCapacity,返回默认容量DEFAULT_CAPACITY。
通过Arrays数组工具类的copyOf()方法,复制原数组到一个扩大到长度为10的一个副本数组,最后将副本数组赋值给elementData,替代原数组。
将得到扩容后的数组返回,将数据插入数组,集合数据个数size+1。
数据添加成功,外出add()返回ture。
C.剩下九层循环list.add(i);1~9
除了不用进入到扩容的方法两层grow()和new capacity(minCapacity),步骤都跟第一层一样。
D. list.add(10);
这一步前面已经添加了10个数据了,达到了集合容量10,要进行扩容。
这一步与步骤B不同的是new capacity(minCapacity)方法不一样,其他的步骤都一样。
不一样的步骤如下:
新容量newCapacity大于添加数据后集合数据的个数,走倍数扩容。
3.总结