面试刷题之-ArrayList的源码阅读

数组存储数据的位置和方式
1、寻址操作次数链表要多一些。数组只需对 [基地址+元素大小*k] 就能找到第k个元素的地址,对其取地址就能获得该元素。链表要获得第k个元素,首先要在其第k-1个元素寻找到其next指针偏移,再将next指针作为地址获得值,这样就要从第一个元素找起,多了多步寻址操作,当数据量大且其它操作较少时,这就有差距了。

该回答源自:http://tieba.baidu.com/p/5069120437

2、CPU缓存会把一片连续的内存空间读入,因为数组结构是连续的内存地址,所以数组全部或者部分元素被连续存在CPU缓存里面,平均读取每个元素的时间只要3个CPU时钟周期。   而链表的节点是分散在堆空间里面的,这时候CPU缓存帮不上忙,只能是去读取内存,平均读取时间需要100个CPU时钟周期。这样算下来,数组访问的速度比链表快33倍! (这里只是介绍概念,具体的数字因CPU而异)。

因此,程序中尽量使用连续的数据结构,这样可以充分发挥CPU缓存的威力。这种对缓存友好的算法称为 Cache-obliviousalgorithm。
List接口
ArrayList
特点:内部采用动态数组顺序存储
适用场合 :
1.数据连续读写,需要根据index进行查找
2.按index写入和删除少
不适用的场合:
1.需要按照index写入数据和删除

动态数组的实现方式,JAVA8中是怎么实现的
源码分析:
//添加方法
 public void add(int var1, E var2) {
       //检验是否下标越界异常
        this.rangeCheckForAdd(var1);
        //检验内部容量        
        this.ensureCapacityInternal(this.size + 1);
        System.arraycopy(this.elementData, var1, this.elementData, var1 + 1, this.size - var1);
        this.elementData[var1] = var2;
        ++this.size;
    }
//如果下标为负数或者大于数组长度,那么直接抛下标越界异常
private void rangeCheckForAdd(int var1) {
            if (var1 < 0 || var1 > this.size) {
                throw new IndexOutOfBoundsException(this.outOfBoundsMsg(var1));
            }
        }
//检验内部容量    
private void ensureExplicitCapacity(int var1) {
        ++this.modCount;
//元素个数超过了内部容量,然后执行扩容方法    
        if (var1 - this.elementData.length > 0) {
            this.grow(var1);
        }

    }
//拷贝方法
第一个参数是要被复制的数组
第二个参数是被复制的数字开始复制的下标
第三个参数是目标数组,也就是要把数据放进来的数组
第四个参数是从目标数据第几个下标开始放入数据
第五个参数表示从被复制的数组中拿几个数值放到目标数组中
public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);
//扩容方法啊
private void grow(int var1) {
        int var2 = this.elementData.length;
        //默认是扩容一半
        int var3 = var2 + (var2 >> 1);
       //如果扩容以后还不够,那么直接将数组大小设置为元素的个数    
        if (var3 - var1 < 0) {
            var3 = var1;
        }
       //不过数组的大小也有限制
        if (var3 - 2147483639 > 0) {
            var3 = hugeCapacity(var1);
        }
       this.elementData = Arrays.copyOf(this.elementData, var3);
}

//判断新数组的长度是否超过了系统分配的最大容量
private static int hugeCapacity(int var0) {
        if(var0 < 0) {
            throw new OutOfMemoryError();
        } else {
            return var0 > 2147483639?2147483647:2147483639;
        }
    }
ArrayList为什么查找快,插入和删除慢?见博客

ArrayList为什么是线程不安全的?
源码分析:
 public boolean add(E var1) {
        this.ensureCapacityInternal(this.size + 1);
        this.elementData[this.size++] = var1;
        return true;
    }
线程不安全原因一:
this.ensureCapacityInternal(this.size + 1);
这句代码是可以用来确定容量大小的,上面有这个方法的源码,单线程是没有问题的
现在有线程A,线程B同时执行add方法,此时数组容量为10,现在数组已经有9个值了,
线程A执行到  if (var1 - this.elementData.length > 0)这句,此时var1==10,判断不需要扩容,
此时B同时执行到这句,也判断不需要扩容,两个线程同时执行到this.elementData[this.size++] = var1;
然后现在有11个元素,但是容量为10,那么下标越界

线程不安全原因二:
this.elementData[this.size++] = var1;

我们知道size++,其实就是size=size,然后运行完以后再size=size+1;
那么this.elementData[this.size++] = var1可以分解为下面两步:
this.elementData[size] = var1;
size=size+1;
那么骚操作就来了,现在假设size=10,现在有两个线程,线程B正好走到了this.elementData[size] = var1这步,
给第十个位置赋值为“我去”,没来得及size=size+1,线程B也正好走到这步,重新给第十个位置赋值为"不去"
那么数据就会覆盖掉.

解决办法:Collections.synchronizedList(new ArrayList<int>>()),上同步锁


链表
哈希表

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值