美文网首页Android开发Android开发Android开发经验谈
ArrayList add() 数组指针越界?原来是线程同步问题

ArrayList add() 数组指针越界?原来是线程同步问题

作者: d74f37143a31 | 来源:发表于2018-12-20 21:32 被阅读5次

    今天在看友盟控制台的时候发现一个崩溃,报错的是ArrayList add()数组指针越界??ArrayList不是动态扩容的吗?怎么会数组越界呢??带着问题,开始了今天的文章。

    这是崩溃日志.png

    看到这个错误,我第一时间就去谷歌了一下,找到了这篇文章 :

    ArrayList在多线程调用Add()添加元素时的下标越界问题(java.lang.ArrayIndexOutOfBoundsException)

    基本上这个文章已经告诉了我们答案,为什么 ArrayList 集合在add 的时候会数组越界。

    我这里出现这个问题的原因是在网络请求的时候,使用了线程池去发送请求网络数据,在得到结果之后解析结果。

    这里出错就是在解析结果的时候没有做同步操作引起的。

    之所以要做同步操作是因为我多做了一步操作,就是网络不好的时候会多发几次网络请求,有时候可能已经返回了结果但是还是会再次发一次请求。

    (吐槽一下,这个项目的网络请求是公司自己封装的 HttpConection 实现的,用起来还是挺费劲的)

    上面的文章说了是线程同步的问题,但是作者没给出答案,我这里想到了几个解决办法。

    • 给方法加锁 synchronized
    • 使用Vector替代 ArrayList
    • 使用 CopyOnWriteArrayList 替代 ArrayList

    PS: 以上几种实现方式各有优缺点,可在文末找到链接介绍。

    我这里的解决办法是给方法加上 synchronized 锁,然后将用 Vector 替代了 ArrayList。

    之前在听一个算法课的时候有听到 arrayList 的默认长度是10,且每次扩容都是原来的 1.5倍,趁着这次看源码,我就去找了对应的实现。

    默认容量是 10
    private static final int DEFAULT_CAPACITY = 10;

    elementData是个动态数组,扩容为原来 1.5

    image.png

    ArrayList 常用方法

    在验证了以上两点之后,我在源码中又看到了常见的一些方法,结合网上的归纳整理如下:

    • 增加元素到链表中
        boolean add(Element e)
    
        增加指定元素到链表尾部.
    
        void add(int index, Element e)
    
        增加指定元素到链表指定位置.
    
    • 从链表中删除元素
        void clear()
    
        从链表中删除所有元素.
    
        E remove(int index)
    
        删除链表中指定位置的元素.
    
        protected void removeRange(int start, int end)
    
        删除链表中从某一个位置开始到某一个位置结束的元素。
    
    • 获取链表中的元素
        E get(int index)
    
        获取链表中指定位置处的元素.
    
        Object[] toArray()
    
        获取一个数组,数组中所有元素是链表中的元素.(即将链表转换为一个数组)
    
    • 修改某个元素
        E set(int index, E element)
    
        将链表中指定位置上的元素替换成新元素。
    
    • 搜索元素
        boolean contains(Object o)
    
        如果链表包含指定元素,返回true.
    
        int indexOf(Object o)
    
        返回元素在链表中第一次出现的位置,如果返回-1,表示链表中没有这个元素。
    
        int lastIndexOf(Object o)
    
        返回元素在链表中最后一次出现的位置,如果返回-1,表示链表中没有这个元素。
    
    • 检查链表是否为空
        boolean isEmpty()
    
        返回true表示链表中没有任何元素.
    
    • 获取链表大小
        int size()
    
        返回链表长度(链表包含元素的个数).
    

    遍历

    获取链表之后我们经常会对其进行遍历,这里列举出三种遍历方式。

    第一种,通过迭代器遍历。即通过 Iterator 去遍历。

    image.png

    第二种,随机访问,通过索引值去遍历。

    由于 ArrayList 实现了 RandomAccess 接口,它支持通过索引值去随机访问元素。

    image.png

    第三种,for循环遍历。如下:

    image.png

    三种遍历方式中耗时最短的是使用随机访问,使用迭代器遍历的耗时最长。

    参考

    ArrayList用法
    Java入门系列:实例讲解ArrayList用法

    Vector 详解
    Java 集合系列06之 Vector详细介绍(源码解析)和使用示例

    ArrayList 详解
    Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例

    CopyOnWrite 详解
    CopyOnWrite 详解

    List总结
    List总结(LinkedList, ArrayList等使用场景和性能分析)

    本文完~,欢迎你留言和我一起讨论。

    相关文章

      网友评论

        本文标题:ArrayList add() 数组指针越界?原来是线程同步问题

        本文链接:https://www.haomeiwen.com/subject/gjvukqtx.html