实战小技巧8:容器的初始化大小指定

简介: 容器可以说是我们日常开发中,除了基本对象之外,使用最多的类了,那么平时在使用的时候,是否有主意到良好编程习惯的大佬,在创建容器的时候,一般会设置size;那么他们为什么要这么干呢?是出于什么进行考量的呢?今天我们将针对最常见的List/Map/Set三种容器类型的初始化值选择,进行说明

容器可以说是我们日常开发中,除了基本对象之外,使用最多的类了,那么平时在使用的时候,是否有主意到良好编程习惯的大佬,在创建容器的时候,一般会设置size;那么他们为什么要这么干呢?是出于什么进行考量的呢?


今天我们将针对最常见的List/Map/Set三种容器类型的初始化值选择,进行说明


1. List



列表,在我们日常使用过程中,会接触到下面几个


  • ArrayList: 最常见的数组列表
  • LinkedList: 基于链表的列表
  • CopyOnWriteArrayList: 线程安全的数组列表


接下来逐一进行说明


1.1 ArrayList


现在以ArrayList为例,进行源码分析,当我们不指定列表大小,直接创建时

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
复制代码


上面是内部实现,其中elementData就是列表中存数据的数组,初始化为默认数组

当我们第一次添加一个元素时,发现数组为默认值,会触发一次数组扩容,新的数组大小为10 (详情看源码)


其次就是数组的库容机制,通过源码/网上分享知识点可以知道,这个扩容的实现如下


  • 当新添加的元素,数组放不下时,实现扩容
  • 扩容后的大小 = 扩容前大小 + max(添加元素个数, 1/2 * 扩容前大小)


基于上面的知识点,大致可以得出指定列表长度的好处


  • 节省空间(用多少申请多少,避免浪费)
  • 减少扩容带来的拷贝(扩容一次就会带来一次数组拷贝,如果已知列表很大,结果还使用默认的10,这会产生很多可避免的扩容开销)


1.2 LinkedList


基于链表的列表,不同于上面的数组列表,它没有提供指定大小的构造方法,why?

因为链表本身的数据结构的特点,它就像糖葫芦一样,一个串一个,有数据,才有接上的可能,因此不需要指定大小


1.3 CopyOnWriteArrayList


这个又非常有意思,它同样不能指定大小,但是原因与前面不同,主要在于它保证线程安全的实现方式


  • 每次新增/修改(加锁,保证单线程访问),都是在拷贝的数组操作;完成之后,用新的替换旧的


所以说,每次变更,都会存在数组拷贝,因此就没有必要提前指定数组大小

那么它的初始化每次都使用默认的么?


并不是这样的,当我们已知这个列表中的值时,推荐使用下面这种方式


List<String> values= Arrays.asList("12", "220", "123");
List<String> cList = new CopyOnWriteArrayList<>(values);
复制代码
  • 将初始化值,放在一个普通的列表中,然后利用普通列表来初始化

CopyOnWriteArrayList


2.Map



常见的map容器使用,大多是下面几个

  • HashMap
  • LinkedHashMap: 有序的hashmap
  • TreeMap: 有序的hashmap
  • ConcurrentHashMap: 线程安全的map


2.1 HashMap


HashMap的底层数据结构是 数组 + 链表/红黑树,关于这个就不细说了

我们在初始化时,若不指定size,则数组的默认长度为8(请注意,Map的数组长度是2的倍数)


与ArrayList的扩容时机不一样的是,默认情况下,Map容量没满就会触发一次扩容

默认是数量达到 size * 0.75(0.75为扩容因子,可以在创建时修改),就会触发一次扩容


why?


  • 主要是为了减少hash冲突


同样的为了减少冲突,在初始化时,我们需要指定一个合适大小

比如我们


  • 已知map的数量为2,这个时候Map的大小选择因该是4
  • map数量为6,这个时候Map的大小选择是16


有时候让我们自己来计算这个值,就有些麻烦了,这个时候,可以直接使用Guava的工具类来完成这个目的


Map<String, String> map = Maps.newHashMapWithExpectedSize(6);
复制代码


2.2 LinkedHashMap


初始化方式同上,略


2.3 ConcurrentHashMap


初始化方式同上,略


2.4 TreeMap


不同于上面几个的是treeMap,没有提供指定容器大小的构造方法

原因和前面说到的LinkedList有些类似,TreeMap的底层数据结构为Tree,所以新增数据是挂在树的一个节点下面,无需指定容量大小


3. Set



集合用的最多应该就是HashSet了,底层结构模型复用,所以初始化大小指定与HashMap一致,也不需要多说


4. 小结



今天这篇博文主要介绍的是三种常见的容器,在创建时,如何指定容量大小

首先明确一点,指定容量大小是为了


  • 减少扩容带来的额外开销
  • 指定容量代销,可以减少无效的内存开销


初始化值设置的关键点:


  • ArrayList: 数据有多少个,初始化值就是多少
  • HashMap: 考虑到扩容因子,初始化大小 = (size / 0.75 + 1)




相关文章
|
6月前
|
监控 Kubernetes 安全
还没搞懂Docker? Docker容器技术实战指南 ! 从入门到企业级应用 !
蒋星熠Jaxonic,技术探索者,以代码为笔,在二进制星河中书写极客诗篇。专注Docker与容器化实践,分享从入门到企业级应用的深度经验,助力开发者乘风破浪,驶向云原生新世界。
731 51
还没搞懂Docker? Docker容器技术实战指南 ! 从入门到企业级应用 !
|
9月前
|
缓存 监控 前端开发
告别卡顿!3大前端性能优化魔法 + CSS容器查询实战
告别卡顿!3大前端性能优化魔法 + CSS容器查询实战
320 95
|
10月前
|
运维 监控 数据可视化
容器化部署革命:Docker实战指南
容器化部署革命:Docker实战指南
|
6月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
Ubuntu Linux Docker
Docker容器的实战讲解
这只是Docker的冰山一角,但是我希望这个简单的例子能帮助你理解Docker的基本概念和使用方法。Docker是一个强大的工具,它可以帮助你更有效地开发、部署和运行应用。
275 27
|
人工智能 安全 API
容器化AI模型的安全防护实战:代码示例与最佳实践
本文基于前文探讨的容器化AI模型安全威胁,通过代码示例展示如何在实际项目中实现多层次的安全防护措施。以一个基于TensorFlow的图像分类模型为例,介绍了输入验证、模型加密、API认证和日志记录的具体实现方法,并结合最佳实践,如使用安全容器镜像、限制权限、网络隔离等,帮助构建更安全的AI服务。
|
机器学习/深度学习 人工智能 Kubernetes
容器化AI模型部署实战:从训练到推理
在上一篇中,我们探讨了AI技术如何赋能容器化生态。本篇聚焦于AI模型的容器化部署,通过图像分类任务实例,详细介绍了从模型训练到推理服务的完整流程。使用PyTorch训练CNN模型,Docker打包镜像,并借助Kubernetes进行编排和部署,最终通过FastAPI提供推理服务。容器化技术极大提升了AI模型部署的便利性和管理效率,未来将成为主流趋势。
|
关系型数据库 应用服务中间件 PHP
实战~如何组织一个多容器项目docker-compose
本文介绍了如何使用Docker搭建Nginx、PHP和MySQL的环境。首先启动Nginx容器并查看IP地址,接着启动Alpine容器并安装curl测试连通性。通过`--link`方式或`docker-compose`配置文件实现服务间的通信。最后展示了Nginx配置文件和PHP代码示例,验证了各服务的正常运行。
417 3
实战~如何组织一个多容器项目docker-compose
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
1385 8