网站建设的总结100字,运城哪里做网站,网站反链和外链的区别,情感式软文广告深入解析ArrayList源码#xff1a;从短链项目实战到底层原理
前言
在Java开发中#xff0c;ArrayList是我们最常用的集合类之一。本文将结合一个实际的短链项目#xff0c;深入分析ArrayList的源码实现#xff0c;帮助大家理解其底层原理和设计思想。
1. 项目背景#…深入解析ArrayList源码从短链项目实战到底层原理
前言
在Java开发中ArrayList是我们最常用的集合类之一。本文将结合一个实际的短链项目深入分析ArrayList的源码实现帮助大家理解其底层原理和设计思想。
1. 项目背景短链系统中的ArrayList应用
在我们的短链项目中ArrayList被广泛使用。让我们先看几个典型的应用场景
1.1 统计数据收集
// 短链接访问统计数据收集
ListShortLinkStatsAccessDailyRespDTO daily new ArrayList();
ListShortLinkStatsLocaleCNRespDTO localeCnStats new ArrayList();
ListInteger hourStats new ArrayList();1.2 批量数据处理
// 批量创建短链接
ListShortLinkBatchCreateRespDTO result new ArrayList();
for (int i 0; i requestParam.getOriginUrls().size(); i) {// 处理每个URL并添加到结果列表result.add(createShortLink(createReqDTO));
}1.3 查询结果存储
// 存储分组查询结果
ListGroupDO groupDOList baseMapper.selectList(queryWrapper);
if (CollUtil.isNotEmpty(groupDOList) groupDOList.size() groupMaxNum) {throw new ClientException(已超出最大分组数);
}这些场景都体现了ArrayList的核心特点动态扩容、随机访问、顺序存储。
2. ArrayList核心源码解析
2.1 类定义和继承关系
public class ArrayListE extends AbstractListEimplements ListE, RandomAccess, Cloneable, java.io.Serializable {// 默认初始容量private static final int DEFAULT_CAPACITY 10;// 空数组实例private static final Object[] EMPTY_ELEMENTDATA {};// 默认大小的空数组实例private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA {};// 存储ArrayList元素的数组缓冲区transient Object[] elementData;// ArrayList的大小包含的元素数量private int size;
}关键点解析
RandomAccess标记接口表示支持快速随机访问transient Object[] elementData实际存储数据的数组transient表示不参与序列化size当前元素个数注意区别于数组容量
2.2 构造方法深度解析
2.2.1 无参构造方法
public ArrayList() {this.elementData DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}在短链项目中的应用
// 创建空的统计数据列表后续动态添加
ListShortLinkStatsAccessDailyRespDTO daily new ArrayList();设计思想
延迟初始化不立即分配内存节省资源默认容量首次添加元素时才分配默认容量10
2.2.2 指定容量构造方法
public ArrayList(int initialCapacity) {if (initialCapacity 0) {this.elementData new Object[initialCapacity];} else if (initialCapacity 0) {this.elementData EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException(Illegal Capacity: initialCapacity);}
}在短链项目中的优化应用
// 如果知道大概的数据量可以预设容量避免扩容
ListShortLinkBatchCreateRespDTO result new ArrayList(requestParam.getOriginUrls().size());2.3 核心方法源码解析
2.3.1 add方法 - 动态扩容的核心
public boolean add(E e) {modCount; // 修改次数1用于fail-fast机制add(e, elementData, size);return true;
}private void add(E e, Object[] elementData, int s) {if (s elementData.length)elementData grow(); // 扩容elementData[s] e;size s 1;
}扩容机制详解
private Object[] grow() {return grow(size 1);
}private Object[] grow(int minCapacity) {int oldCapacity elementData.length;int newCapacity ArraysSupport.newLength(oldCapacity,minCapacity - oldCapacity, /* minimum growth */oldCapacity 1 /* preferred growth */);return elementData Arrays.copyOf(elementData, newCapacity);
}扩容策略分析
扩容时机当前大小等于数组长度时扩容大小新容量 旧容量 旧容量/2即1.5倍数据迁移使用Arrays.copyOf复制到新数组
在短链项目中的性能影响
// 不好的做法频繁扩容
ListString urls new ArrayList(); // 默认容量10
for (int i 0; i 1000; i) {urls.add(url i); // 会触发多次扩容
}// 好的做法预设容量
ListString urls new ArrayList(1000); // 避免扩容
for (int i 0; i 1000; i) {urls.add(url i);
}2.3.2 get方法 - 随机访问的实现
public E get(int index) {Objects.checkIndex(index, size); // 边界检查return elementData(index);
}E elementData(int index) {return (E) elementData[index]; // 直接数组访问
}性能分析
时间复杂度O(1)实现原理直接通过数组下标访问
在短链项目中的应用
// 高效的随机访问
ListShortLinkStatsAccessDailyRespDTO dailyStats getDailyStats();
for (int i 0; i dailyStats.size(); i) {ShortLinkStatsAccessDailyRespDTO stat dailyStats.get(i); // O(1)时间复杂度// 处理统计数据
}2.3.3 remove方法 - 元素删除的实现
public E remove(int index) {Objects.checkIndex(index, size);final Object[] es elementData;SuppressWarnings(unchecked) E oldValue (E) es[index];fastRemove(es, index);return oldValue;
}private void fastRemove(Object[] es, int i) {modCount;final int newSize;if ((newSize size - 1) i)System.arraycopy(es, i 1, es, i, newSize - i); // 数组元素前移es[size newSize] null; // 清除引用帮助GC
}删除机制分析
元素移动删除位置后的所有元素前移一位时间复杂度O(n)最坏情况需要移动n-1个元素内存管理将最后一个位置置null帮助垃圾回收
3. ArrayList vs LinkedList在短链项目中的选择
3.1 性能对比
操作ArrayListLinkedList短链项目场景随机访问O(1)O(n)统计数据查询 ✓头部插入O(n)O(1)很少使用尾部插入O(1)*O(1)批量数据收集 ✓中间插入O(n)O(1)**很少使用删除O(n)O(1)**偶尔使用
摊销时间复杂度可能触发扩容 需要先定位到节点
3.2 内存使用对比
// ArrayList内存布局更紧凑
[data1][data2][data3][null][null]...// LinkedList内存布局额外指针开销
[prev|data1|next] - [prev|data2|next] - [prev|data3|next]在短链项目中的选择
// 适合ArrayList的场景统计数据展示
ListShortLinkStatsAccessDailyRespDTO dailyStats new ArrayList();
// 频繁的随机访问内存使用效率高// 不适合LinkedList的场景
// 1. 需要根据索引快速访问统计数据
// 2. 数据量大时LinkedList的指针开销明显4. ArrayList的线程安全问题
4.1 并发修改异常
// 在短链项目中可能遇到的问题
ListString urls new ArrayList();
urls.add(url1);
urls.add(url2);// 遍历时修改会抛出ConcurrentModificationException
for (String url : urls) {if (url.contains(invalid)) {urls.remove(url); // 危险操作}
}解决方案
// 方案1使用Iterator的remove方法
IteratorString iterator urls.iterator();
while (iterator.hasNext()) {String url iterator.next();if (url.contains(invalid)) {iterator.remove(); // 安全删除}
}// 方案2使用removeIf方法推荐
urls.removeIf(url - url.contains(invalid));// 方案3在短链项目中使用线程安全的集合
ListString safeUrls Collections.synchronizedList(new ArrayList());4.2 多线程环境下的选择
// 在短链项目的统计模块中
public class ShortLinkStatsService {// 线程不安全的做法private ListStatsRecord records new ArrayList();// 线程安全的做法private ListStatsRecord safeRecords new CopyOnWriteArrayList();// 或者使用同步private ListStatsRecord syncRecords Collections.synchronizedList(new ArrayList());
}5. ArrayList性能优化实践
5.1 容量预设优化
// 在短链项目中的优化实践
public ListShortLinkBatchCreateRespDTO batchCreateShortLinks(ShortLinkBatchCreateReqDTO requestParam) {ListString originUrls requestParam.getOriginUrls();// 预设容量避免扩容开销ListShortLinkBatchCreateRespDTO result new ArrayList(originUrls.size());for (int i 0; i originUrls.size(); i) {// 批量处理逻辑result.add(createSingleShortLink(originUrls.get(i)));}return result;
}5.2 内存优化
// 及时释放不需要的引用
public void processLargeDataSet() {ListLargeObject largeList new ArrayList(10000);// 处理数据...// 处理完成后及时清理largeList.clear(); // 清除所有元素largeList null; // 释放引用
}5.3 批量操作优化
// 使用addAll进行批量添加
ListString allUrls new ArrayList();
ListString batchUrls getBatchUrls();// 好的做法批量添加
allUrls.addAll(batchUrls);// 不好的做法逐个添加
for (String url : batchUrls) {allUrls.add(url); // 可能触发多次扩容
}6. 实际项目中的最佳实践
6.1 合理选择初始容量
// 根据业务场景选择合适的初始容量
public class ShortLinkStatsCollector {public ListHourlyStats collectHourlyStats() {// 一天24小时预设容量24ListHourlyStats hourlyStats new ArrayList(24);for (int hour 0; hour 24; hour) {hourlyStats.add(getStatsForHour(hour));}return hourlyStats;}public ListDailyStats collectMonthlyStats() {// 一个月最多31天预设容量31ListDailyStats monthlyStats new ArrayList(31);// ...return monthlyStats;}
}6.2 避免不必要的装箱拆箱
// 对于基本类型考虑使用专门的集合
// 不好的做法
ListInteger ids new ArrayList(); // 会有装箱开销// 更好的做法使用第三方库如Trove或Eclipse Collections
// TIntArrayList ids new TIntArrayList(); // 避免装箱6.3 合理使用Stream API
// 在短链项目中处理统计数据
public ListString getActiveShortLinks(ListShortLinkDO allLinks) {return allLinks.stream().filter(link - link.getEnableStatus() 0) // 过滤启用的链接.filter(link - link.getDelFlag() 0) // 过滤未删除的链接.map(ShortLinkDO::getFullShortUrl) // 提取URL.collect(Collectors.toList()); // 收集到新的ArrayList
}7. 总结
通过对短链项目中ArrayList使用场景的分析我们深入了解了ArrayList的
底层实现基于动态数组支持自动扩容性能特点随机访问O(1)插入删除O(n)扩容机制1.5倍扩容使用Arrays.copyOf迁移数据线程安全非线程安全需要额外同步措施优化策略预设容量、批量操作、及时释放引用
在实际项目中选择合适的集合类型需要根据具体的使用场景
频繁随机访问选择ArrayList频繁插入删除考虑LinkedList线程安全需求使用CopyOnWriteArrayList或同步包装大量数据处理注意内存使用和GC影响
希望这篇文章能帮助大家更好地理解和使用ArrayList在实际项目中做出更好的技术选择 参考资料
OpenJDK ArrayList源码《Java并发编程实战》《Effective Java》第三版