集合部分知识总结
集合部分知识点总结。
集合接口框架
集合中的类
深入分析
ArrayList源代码分析
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
构造方法摘要
构造方法 | 摘要 |
---|---|
ArrayList() | 构造一个初始容量为 10 的空列表。 |
ArrayList(Collection<? extends E> c) | 构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。 |
ArrayList(int initialCapacity) | 构造一个具有指定初始容量的空列表。 |
以调用无参构造方法的方式创建ArrayList对象代码示例解析
首先先把这几个成员变量列出来放在这哈,我看代码习惯是把成员变量先拿出来一些放到我的便签里记录记录一下都是做什么用的,这个方法挺好省时要不然看都一半你都想不起来这变量是啥。
long serialVersionUID
int DEFAULT_CAPACITY =0;
Object[] EMPTY_ELEMENTDATA = {};
Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
Object[] elementData;
int size
1.ArrayList list = new ArrayList();
创建ArrayList时,调用ArrayList类中的无参构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
由此可知:当调用无参的构造方法创建ArrayList()对象时,
会将常量DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值
给成员变量elementData
DEFAULTCAPACITY_EMPTY_ELEMENTDATA的源码:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
这是一个空的Object []类型的常量
elementData的源码:
transient Object[] elementData;
这是一个引用类型:Object[] 类型的变量
小结:使用无参构造方法创建ArraList对象时,实际
上是在底层创建了一个空Object类型的数组,并
且该数组的地址是不可变得;并且将该数组的地
址复制给elementData变量,用它来记录该数组。
2.使用add(E e)方法进行添加元素时:
add(E e)方法的源码:
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
就是说当添加数据时,会执行add(e, elementData, size);
e是要添加的对象,elementData是ArrayList集合底层维护的数组,
size是成员变量,既然是成员变量,那么就有初始值,因为size的
数据类型是int,所以size的初始值为0;
所以说:添加第一个数据的时候,调用add(e, elementData, size)方法,传入的
参数就是:add(要添加的对象,底层数组,0);
现在进入到add(e, elementData, size);方法:
源码:
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
进入方法执行后,会首先判断s的值与底层数组的长度是否相等,因为底层数组目前的
长度为0,而传入的参数s就是成员变量size,也是0;索引就会执行elementData = grow();
因为elementData 也是一个Object[]类型的成员变量,所以可以断定grow()方法的返回值
就是一个Object[]类型的数据。现在进入grow()方法:
源码:
private Object[] grow() {
return grow(size + 1);
}
返回值是grow(size + 1)的返回值,现在进入grow(size + 1)方法:
源码:
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
调用Arrays.copyOf()方法,这是一个扩容机制,参数1:elementData是底层数组,
将底层数组当做参数传递给copyOf()方法,参数2是新数组的长度,返回值是扩容后的数组,
在这里使用的是elementData来接受新数组的地址,那么集合的底层数组就不在是之前的那
个空的数组了,而是扩容后的新数组。那么newCapacity(minCapacity))是什么,现在进入
newCapacity(minCapacity))的方法:
源码:
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
第一次执行的时候:
oldCapacity:旧数组的长度;因为现在是往里面存第一个元素,也就是第一次往里面存数据,所以
旧数组的长度是0
newCapacity :新数组的长度;是旧数组的1.5倍,因为目前旧数组长度为0,那么新数组的长度也是0;
那么:newCapacity - minCapacity = -1<0;
那么执行:return Math.max(DEFAULT_CAPACITY, minCapacity);
返回值就是:DEFAULT_CAPACITY;而DEFAULT_CAPACITY是ArrayList的成员变量,初始值为10;
因此,第一次执行的时候,newCapacity(int minCapacity)的返回结果就是10;
那么Arrays.copyOf(elementData,newCapacity(minCapacity))就是:
(elementData,10);
所以grow(int minCapacity)方法里面返回的就是长度为10的Object类型的数组:elementData;
所以grow()方法返回的就是长度为10的Object数组:elementData;
所以在add(E e, Object[] elementData, int s)方法中:
elementData = grow();就是把grow()的返回值长度为10的新数组的地址复制给elementData;
elementData[s] = e;就是把要存放的元素,放在0索引上
size = s + 1;保证成员变量size的自增,以保证再次存放元素时,存放的是下一索引位置的
元素,因为在add()方法中调用private void add(E e, Object[] elementData, int s)方法时,
int s 的值,传递的就是size;
至此,添加第一个元素执行完毕;
执行网private void add(E e, Object[] elementData, int s)方法后,返回true,告知调用者,
添加元素成功;
小结:调用无参构造方法创建一个ArrayList对象后,底层的数组长度是为0的,只有当调用add()方法添加
元素时,才会扩容为10;当没存满10个元素时,此时那么在此调用add()方法,那么就会再次调用:
private void add(E e, Object[] elementData, int s),当执行到:
if (s == elementData.length)时,s是小于数组长度的,那么就会直接执行:
elementData[s] = e;,将要添加的元素添加到数组里面去
当存满10个元素,再次存储下一元素时,size的值是10,那么s的值就是10,那么s == 数组长度;
那么就会继续执行grow()方法:
进入grow()方法后,就会执行grow(int minCapacity),传递的参数是size+1,那么就是10+1 = 11;
进入grow(int minCapacity)方法后,就会执行Arrays.copyOf(elementData,newCapacity(minCapacity));
那么就会执行newCapacity(minCapacity)),参数值为11;
进入newCapacity(minCapacity))方法后:
oldCapacity(数组长度) = 10;
newCapacity = 15;
那么就会计算:newCapacity - minCapacity <= 0;结果为:15-11>0,因此为:false;
那么就会返回执行:
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
那么就会计算:newCapacity - MAX_ARRAY_SIZE <= 0,
MAX_ARRAY_SIZE:常量:值大约为int类型的最大值;
因此,只要newCapacity在int范围内,就会执行:
return newCapacity;
那么: Arrays.copyOf(elementData,newCapacity(minCapacity))执行完毕,就是讲旧数组的元素拷贝到新数组中,并且
新数组的长度+原来数组长度的1/2;此时是第一次扩容,新数组的长度为15;
最终新数组返回给add(E e, Object[] elementData, int s),赋值为elementData变量;
然后执行:elementData[s] = e
那么就将要添加的对象添加在了第11号的位置,也就是10索引的位置;
以后再次添加,知道最后一个索引存上时,再次存储就再次扩容。
————————————————
版权声明: 部分引用CSDN博主「山那边的Joy」的原创文章,
原文链接:https://blog.csdn.net/deskDopa/article/details/82026157
Linked List代码分析(待完善)
HashSet
构造方法摘要
构造方法 | 摘要 |
---|---|
HashSet() | 构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75 |
HashSet(Collection<? extends E> c) | 构造一个包含指定 collection 中的元素的新 set。 |
HashSet(int initialCapacity) | 构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。 |
HashSet(int initialCapacity, float loadFactor) | 构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。 |
HashSet储存引用对象怎么保证唯一
1. 重写equals()方法
2. 重写hashcode()方法
3. 如果x.equals(y)为true 则必有 x.hashcode()=y.hashcode()
TreeSet
TreeSet的两种排序思维
**1.自然排序:**使对象类实现Compairble接口,并且重写Comparble()方法。
**2.比较器方法:**在TreeSet集合的构造器传入实现了Compairtor接口的匿名内部类,重写Compairto()方法。
Map集合
双列集合这最重要的就是Map集合的两种循环遍历方式,应为每种还可以有两种形式(增强for循环的形式、Iterator迭代器迭代形式),所以我常分为有四种形式。
集合中常见面试题
1.LInkedList与ArrayList区别:
- 数据结构不同 ,LInkLIst为链表结构;ArrayList为数组结构。
- 效率不同 ,Linklist 增删效率高; ArrayList 查询效率高。
2.数组与集合的区别:
1.区别一:数组定长 而集合是变长
2.区别二:数组既可以储存基本数据类型也可以储存引用数据类型;
而集合只能存储引用数据类型,若存储基本数据类型则会自动装箱。
3.Set的集合与List集合区别
三点区别:
List 有序、有索引、可重复
set无序、无索引、不可以重复
(此处的有序指的是怎么存就怎么取而不是存储过程中的排序;)