程序优化之数据结构(List的数据结构的选择)

程序优化之数据结构(List的数据结构的选择)_第1张图片

1.Arrarlist 和 Linkedlist 区别

(1)ArrayList 是实现了基于动态数组的数据结构,LinkedList 基于链表的数据结构。 (2)对于随机访问 get 和 set,ArrayList 觉得优于 LinkedList,因为 LinkedList 要移动指针。 (3)对于新增和删除操作 add 和 remove,LinedList 比较占优势,因为 ArrayList 要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList 的速度反而优于 LinkedList。 但若是批量随机的插入删除数据,LinkedList 的速度大大优于 ArrayList. 因为 ArrayList 每插入 一条数据,要移动插入点及之后的所有数据。(4)ArrayList 底层实现就是数组,且ArrayList实现了RandomAccess,表示它能快速随机访问存储的元素,通过下标 index 访问,只是我们需要用 get() 方法的形式, 数组支持随机访问, 查询速度快, 增删元素慢;LinkedList 底层实现是链表, LinkedList 没有实现 RandomAccess 接口,链表支持顺序访问, 查询速度慢, 增删元素快。

2.Arrarlist和Vector的区别是什么?

(1)Vector的方法都是同步的,是线程安全的,而ArrayList的方法不是,由于线程的同步必然要影响性能;(2)当Vector或ArrayList中的元素超过它的初始大小时,Vector会将容量翻倍,ArrayList只增加50%的大小等。

3.list的安全性考虑(Collections.synchronizedList(new ArrayList<>())与CopyOnWriteArrayList)

ArrayList是不安全的,并发读写,会抛出异常。因此引入了CopyOnWriteArrayList。

CopyOnWriteArrayList

CopyOnWriteArrayList是ArrayList的一种线程安全的变体,在add、set、remove等会改变其内部值和长度的时候会通过创建一个新的数组来进行实现。对于CopyOnWriteArrayList来说,读的时候不加锁,只有在写的时候才加锁,适用于读操作远远大于写操作、而且不希望读的时候也去加锁,不希望在同步遍历时受到其他并发线程的干扰而错误错误的场景。在这种场景下使用CopyOnWriteArrayList非常适合。

 优点: 对于一些读多写少的数据,这种做法的确很不错,例如配置、黑名单、物流地址等变化非常少的数据,这是一种无锁的实现。可以帮我们实现程序更高的并发。

缺点:这种实现只是保证数据的最终一致性,在添加到拷贝数据而还没进行替换的时候,读到的仍然是旧数据。如果对象比较大,频繁地进行替换会消耗内存,从而引发Java的GC问题。

List list = Collections.synchronizedList(new ArrayList<>());也可以保证安全性

Collections.synchronizedList(new ArrayList<>());使用的过程中遇到的陷阱。

4.真实场景的例子:

@NotThreadSafe
class BadListHelper {
    public List list = Collections.synchronizedList(new ArrayList());
    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !list.contains(x);
        if (absent)
            list.add(x);
        return absent;
    }
}

虽然说putIfAbsent方法加了synchronized的锁关键字,但是这个putIfAbsent方法获得锁和list对象的获得锁不是同一个锁;

putIfAbsent获得锁是BadListHelper这个类的锁对象,list获得锁对象是list;如果这么写,那list依旧能够被其他线程获取锁对象来改变list对象的值,就会导致数据出错,或者两两线程在访问这个方法的时候拿到的list数据可能会有错误;所以这么写是不对的;要想保证list数据不出错,就要给他自己上锁,其他线程将不能获得list锁来来改变list对象。

@ThreadSafe
class GoodListHelper {
    public List list = Collections.synchronizedList(new ArrayList());
    public boolean putIfAbsent(E x) {
        synchronized (list) {  //获得list锁对象,其他线程将不能获得list锁来来改变list对象。
            boolean absent = !list.contains(x);
            if (absent)
                list.add(x);
            return absent;
        }
    }
}

5.Collections.synchronizedList(new ArrayList<>())与CopyOnWriteArrayList使用对比:

Collections.synchronizedList(new ArrayList):读写删除加锁,且加锁在代码块上,效率较好,遍历未加锁,可以根据实际业务需求,自行决定是否加锁。

CopyOnWriteArrayList:读未加锁,写使用CAS自旋锁,先复制原数组,修改复制的数组,之后把复制数组重新复制给原地址(当数组过大时,复制效率极低),并发过多,CAS碰撞过多,也会影响性能,读取数据是原数组,所以如果add方法还未执行到setArray方法,读取的数据就是原来的数据。
 

 

你可能感兴趣的:(HashMap,数据结构,java,链表)