- 判断当前插入的元素位置是否为LinkedList的头节点;若不是的话,则将当前操作元素节点的next指为new的要插入进来的那个节点。(将操作节点的next指向新插入的元素的节点)
在某个位置插入某个元素的大概流程如图所示: 也就是说,我们通过LinkedList向某个位置插入一个数据,我们只需要改变两个数据节点的pre和next指向就完成了。
ArrayList和LinkedList的插入效率比较
ArrayList的add是尾部:效率ArrayList>LinkedList; ArrayList的add是头部:效率ArrayList<LinkedList
原因:ArrayList的内存空间连续,且不需要复制数组。LinkedList需要创建一个新的节点,前后引用进行重新排列。
我不知道你们有没有恍然大悟,但是我恍然大悟了。
接下来就是揭开扩容的真面目了,扩容和构造器有什么关系?
2.4 触发扩容以及扩容大小
上面说扩容机制是在add(e)时触发的,来看看add(e)的源码:
add(E e)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ensureCapacityInternal(int minCapacity) :确保插入元素容器最小的值; minCapacity:当前数组的长度+1
构造函数为空参数构造函数,则给minCapacity设置默认的值为minCapacity=DEFAULT_CAPACITY=10; 若不为空参数构造函数,则minCapacity=数组已存在数据size+1
private static final int DEFAULT_CAPACITY = 10;
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
ensureExplicitCapacity(int minCapacity) :判断是否需要扩容 elementData.length是现有数据的长度。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow(int minCapacity) :实现扩容
- 获取旧容量
- 现将原元素数组的长度增大1.5倍,随后和newCapacity比较。
- 新容量小于参数指定容量,修改新容量:newCapacity(新增容量)<minnewCapacity:newCapacity=minCapacity
- 新容量大于最大容量:newCapacity>minnewCapacity:newCapacity:将就数组拷贝到扩容后的新数组中。
private void grow(int minCapacity) {
int oldCapacity = elementData.length; //旧容量
int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量为旧容量的1.5倍
if (newCapacity - minCapacity < 0) //新容量小于参数指定容量,修改新容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //新容量大于最大容量
newCapacity = hugeCapacity(minCapacity); //指定新容量
//拷贝扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
第一次调用添加元素的add和addAll,size=0,则minCapacity=1 如果ArrayList给了特定的初始值,则需要根据数组实际长度和数组容量差来判断是否调用扩容;如果没有指定初始容量,第一次调用add则一定需要调用grow()
三.ArrayList的线程安全性
在多线程下,ArrayList不能保证原子性(即同一时刻只能有一个线程来对它进行操作)。
举个栗子:线程A对ArrayList进行++处理,期待100;线程B对ArrayList进行–处理,期待98。多线程进行时,可能本应该为100,因为又有–操作,可能++后,结果仍然为99.
多线程环境下,ArrayList线程是不安全的。
保证线程安全性的方法:
1.使用synchronized关键字;
2.用Collection类中的静态方法synchronizedList(),对Arraylist进行调用
四.ArrayList常用方法介绍
arrayList.get(position):根据数组下标进行取值,set同理
arrayList.add(postion):判断是否扩容,根据数组下标进行赋值
arrayList.remove(index)步骤: 1.在目标元素的位置设置赋值的起始位置; 2.将目标位置开始到数组最后一位的数组进行复制; 3.然后覆盖拷贝到要移除的位置上,将要移除的位置进行覆盖; 4.再将最后一个位置的数据进行null设置,等待回收。
remove(index)源码:
public E remove(int index) {
//第一步先判断是否有越界,如果越界直接IndexOutOfBoundsException
rangeCheck(index);
modCount++;
//把该元素从数组中提出
E oldValue = elementData(index);
//需要复制的长度
int numMoved = size - index - 1;
if (numMoved > 0)
//原数组,从哪开始复制,目标数组,复制起始位置,长度。过程如下图:
System.arraycopy(elementData, index+1, elementData, index,numMoved);
//赋值null等待回收
elementData[–size] = null;
return oldValue;
}
五.ArrayList的去重处理
1.循环list中所有的元素然后删除
public static ArrayList removeDuplicate_1(ArrayList list){
for(int i =0;i<list.size()-1;i++){
for(int j=list.size()-1;j>i;j–){
if(list.get(i).equals(list.get(j)))
list.remove(j);
}
}
return list;
}
2.利用hashSet剔除重复元素,无序
public static ArrayList removeDuplicate_2(ArrayList list){
HashSet set = new HashSet(list);
//使用LinkedHashSet可以保证输入的顺序
//LinkedHashSet set2 = new LinkedHashSet(list);
list.clear();
list.addAll(set);
return list;
}
3.利用list的contains方法去重
public static ArrayList removeDuplicate_3(ArrayList list){
ArrayList tempList = new ArrayList(list.size());
for(int i=0;i<list.size();i++){
if(!tempList.contains(list.get(i)))
tempList.add(list.get(i));
}
return tempList;
}
contains是根据什么原理来进行比较的呢? 可以看出contains其实也是用equals来比较的,而equals是比较的地址
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
六.什么时候选择ArrayList?
6.1 ArrayList的for和Iterator遍历效率差别
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
言尽于此,完结
无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。
- 第一,设计模式能让专业人之间交流方便,如下:
程序员A:这里我用了XXX设计模式
程序员B:那我大致了解你程序的设计思路了
- 第二,易维护
项目经理:今天客户有这样一个需求…
程序员:明白了,这里我使用了XXX设计模式,所以改起来很快
- 第三,设计模式是编程经验的总结
程序员A:B,你怎么想到要这样去构建你的代码
程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题
- 第四,学习设计模式并不是必须的
程序员A:B,你这段代码使用的是XXX设计模式对吗?
程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的
从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!
搜集费时费力,能看到此处的都是真爱!
习过设计模式,但是我的经验告诉我是这样写的
[外链图片转存中…(img-w70l1RlR-1711016277124)]
从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!
[外链图片转存中…(img-hQS2mWJq-1711016277125)]
搜集费时费力,能看到此处的都是真爱!