欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java中ArrayList、LinkedList、Vector的区别

程序员文章站 2022-07-14 08:34:17
...

先说共同点: 都继承自List接口

ArrayList  里面数组类型,可以存放任意类型的对象。ArrayList不具有线程安全性。

LinkedList可以看做为一个双向链表,LinkedList也是线程不安全的,在LinkedList的内部实现中,并不是用普通的数组来存放数据的,而是使用结点<Node>来存放数据的,有一个指向链表头的结点first和一个指向链表尾的结点last。LinkedList的插入方法的效率要高于ArrayList,但是查询的效率要低一点。

Vector  类似于ArrayList的可变长度的数组类型,它的内部也是使用数组来存放数据对象的。值得注意的是Vector与ArrayList唯一的区别是,Vector是线程安全的。在扩展容量的时候,Vector是扩展为原来的2倍,而ArrayList是扩展为原来的1.5倍。

HashSet、LinkedHashSet、TreeSet的内部实现简介

1、HashSet继承AbstractSet类,实现了Set等接口,但最重要的是HashSet是基于HashMap来实现的

2、LinkedHashSet

LinkedHashSet继承了HashSet,又基于LinkedHashMap来实现。LinkedHashSet底层使用LinkedHashMap的key来保存所有元素,从而维护着一个运行于所有元素的双向链表

3、TreeSet

TreeSet继承自AbstractSet,又基于TreeMap实现,因为TreeSet底层使用一个TreeMap,它的元素存储在TreeMap的key中,保证了不可重复性

HashMap,LinkedHashMap,TreeMap,HashTable区别

HashMap: 它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null(多条会覆盖);允许多条记录的值为 Null。非同步的。

TreeMap: 能够把它保存的记录根据键(key)排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。非同步的

Hashtable: 与 HashMap类似,不同的是:key和value的值均不允许为null;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。 

LinkedHashMap: 保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。key和value均允许为空,非同步的。

HashMap和ConcurrentHashMap的区别,HashMap的底层源码

Hashmap本质是数组加链表。根据key取得hash值,然后计算出数组下标,如果多个key对应到同一个下标,就用链表串起来,新插入的在前面。

Map map = Collections.synchronizedMap(new HashMap()); 这句话没有改变map对象本身, 要同步需要使用map的引用调用

ConcurrentHashMap:在hashMap的基础上,ConcurrentHashMap将数据分为多个segment,默认16个(concurrency level),然后每次操作对一个segment加锁,避免多线程锁的几率,提高并发效率。。

三、HashMap源码分析

4 final float loadFactor; //加载因子

其中加载因子是表示Hash表中元素的填满的程度.若:加载因子越大,填满的元素越多,好处是,空间利用率高了,但:冲突的机会加大了.反之,加载因子越小,填满的元素越少, 

好处是:冲突的机会减小了,但:空间浪费多了.冲突的机会越大,则查找的成本越高.反之,查找的成本越小.因而,查找时间就越小.

因此,必须在 “冲突的机会”与”空间利用率”之间寻找一种平衡与折衷. 这种平衡与折衷本质上是数据结构中有名的”时-空”矛盾的平衡与折衷.

线程安全的CopyOnWriteArrayList介绍

CopyOnWriteArrayList使用了一种叫写时复制的方法,当有新元素

添加到CopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组

做写操作,写完之后,再将原来的数组引用指向到新数组。

CopyOnWriteArrayListadd操作的源代码如下:

public boolean add(E e) {
    //1、先加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        //2、拷贝数组
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        //3、将元素加入到新数组中
        newElements[len] = e;
        //4、将array引用指向到新数组
        setArray(newElements);
        return true;
    } finally {
       //5、解锁
        lock.unlock();
    }
}

读的情况分几种:

1、如果写操作未完成,那么直接读取原数组的数据; 
2、如果写操作完成,但是引用还未指向新数组,那么也是读取原数组数据; 
3、如果写操作完成,并且引用已经指向了新的数组,那么直接从新数组中读取数据。

可见,CopyOnWriteArrayList读操作是可以不用加锁的。

通过上面的分析,CopyOnWriteArrayList 有几个缺点: 
1、由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc

2、不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求;

CopyOnWriteArrayList 合适读多写少的场景,不过这类慎用 
因为谁也没法保证CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点多,每次add/set都要重新复制数组,这个代价实在太高昂了。在高性能的互联网应用中,这种操作分分钟引起故障。

如上面的分析CopyOnWriteArrayList表达的一些思想: 
1、读写分离,读和写分开 
2、最终一致性 

3、使用另外开辟空间的思路,来解决并发冲突 ;

ArrayBlockingQueue和LinkedBlockingQueue

ArrayBlockQueue和LinkedQueue属于Java.util.current包下的两个封装的线程安全的队列,主要讨论线程安全和阻塞操作的实现 ;  

java中ArrayList、LinkedList、Vector的区别


java中ArrayList、LinkedList、Vector的区别
condition 接口里面的signal 方法和 await方法。    互为一对