Java链表与数组的折中:Arrays.asList

第一点:
内存要么零散, 用指针前后记住位置, 对外表现是一个整体, 数据结构就是链表.
零散对内存的连续性要求不高. 此外好像就没啥优点了.
随机取: 比如找到index=4的元素, 要么你得从第一个节点遍历, 要么从最后一个节点, 平均需要遍历二分之一, 零散的东西, 遍历起来当然慢, 随机自然就慢了. 随机改依赖于随机取, 所以效率低. 当然, 每个节点的index不用维护, 遍历的时候只要有计数器记录一下前驱或者后驱的执行次数步数就行
加减元素: 一般是首尾加减, 如果代码设计的结构已经能记住first和last元素, 当然就知道两端的位置, 直接追加元素就行. 只要像系统申请一个元素差不多大小的空间, 然后有指针串起来就行了
此外, 零散需要维护多余的指针比如pre next啥的, 要多费额外的空间.
在java中, 创建linkedlist的那个家伙自己都说它不好, 阿里巴巴手册记得也说不要用linkedlist这种链表.

第二点
内存要么连续, 一大片, 规整. 地址是挨着个, 节点大小是固定的, 所以直接按角标和节点大小就能很快的随机取, 随机写, 所以随机性很好.遍历很快
就是对内存要求苛刻, 假设需要存的数据很多, 你得保证内存有一整块连续的增减节点呢?
增加节点, 当前声明的内存块装了100个, 现在来一个新的, 怎么办?
原地内存块扩大一个节点 ? 旧的100个不用动? 别做梦了, 你咋知道你旁边的内存不是被其他人占用了.
把那个新的向链表一样串进来? 别做梦了. 节点都是一个类文件, 难道要增加两个字段吗, 多费空间, 而且这个半连续半离散的东西, 你让程序如何智能利用解读出连续的区域所在的位置, 并且发挥出它的性能然后还兼容离散的部分
那就只能申请一块更大的连续的空的内存了, 然后把旧的数据拷贝过去这就是扩容机制. 复制当然耗性能了, 而且连续的大内存往往是珍贵的, 稀缺的, 甚至如果你要求太大, 系统给不了你就提前执行垃圾回收甚至内存不足了. 所以, 扩容扩多大? 2倍, 3倍? 扩少了可能没加几个元素又不够用了后面得不断扩, 扩大了, 可能导致实际用不了那么大, 浪费资源. Java ArrayList的扩容是加大约0.5倍(右移一位), 这个是写死的, 程序员不能改. 当然, 首次申请即创建ArrayList的时候你可以合理的指定他的初始大小. 减元素何时缩,缩多少也是一样的考虑

第三点:
Java的集合除了数据结构, 还得讲讲他依赖于equals方法. 也就是特别是hash**那些. 应该是有这方便的考虑, 所以Java的集合只能装对象, 基本数据类型是不能的, 起码基本数据类型没有方法.
这时候数组就有几个场景了(不是说推荐用数组啊)

  1. 如果你的元素个数是固定的, 个数不加不减, 不用缩容扩容
  2. 你的元素是基本数据类型
  3. 编译器会把可变参数编译为数组, 也算数组
  4. 你的上游就是怼了一个数组进来
    所以, Java数组是可能会在代码中出现的,

第四点
Java数组有几大弱点, 集合是可以用迭代器的, 数组不能, 而且数组本身就不能加减元素, 所以数组多数时候需要向ArrrayList这种可变数组转化, 如何转化, 总不能老让程序员自己遍历一个一个复制过去吧
Java设计大佬们, 搞了个Arrays工具类, 里面有个asList()方法就是转化了一个"Arrays.ArrayList" (忍住别骂, 叫啥不好, 叫ArrayList, 就怕你搞不混).
Arrays.ArrayList这个静态内部类实现了AbstractList, 所以可以兼容List接口.
这个数组的内部类的维护的数据就是当前数组, 实现AbstractList, 比如随机取, 就直接用的是数组, 还有遍历等等
所以, 你操作这个内部类就是操作的原数组, 说他是视图也有道理
而且他不支持加减元素, 改变数组大小的事情是不能做的, 直接抛异常
(所以使用的时候小心了, 推荐 new ArrayList(Array.asList(1,2,3)))这种生成一个全新的真集合的写法
asList()不直接复制成新集合的做法是为了省下内存(应该)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值