单列集合List

List的常用子类

如果要执行大量的增删操作选择LinkedList,如果只是查询的话用ArrayList

ArrayList集合

  1. 有序存储,继承了List的特性,平时常用来查询,遍历数据
  2. 底层是数组结构,所以增删慢,查询快,效率高意味着线程不安全

LinkedList集合

  1. 有序存储,继承了List的特性
  2. 底层是链表结构,所以增删快,查询慢,同样的效率高,但是线程不安全
  3. LinkedList是一个双向链表
  4. 特有方法
public void addFirst(E e) :将指定元素插入此列表的开头。
public void addLast(E e) :将指定元素添加到此列表的结尾。
public E getFirst() :返回此列表的第一个元素。
public E getLast() :返回此列表的最后一个元素。
public E removeFirst() :移除并返回此列表的第一个元素。
public E removeLast() :移除并返回此列表的最后一个元素
?
//添加元素
link.addFirst("AA");
link.addLast("CC");
System.out.println(link);
// 获取元素
System.out.println(link.getFirst());
System.out.println(link.getLast());
// 删除元素
System.out.println(link.removeFirst());
System.out.println(link.removeLast());
System.out.println(link)

Vector源码:

    /**
     * Adds the specified component to the end of this vector,
     * increasing its size by one. The capacity of this vector is
     * increased if its size becomes greater than its capacity.
     *
     * <p>This method is identical in functionality to the
     * {@link #add(Object) add(E)}
     * method (which is part of the {@link List} interface).
     *
     * @param   obj   the component to be added
     */
    public synchronized void addElement(E obj) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = obj;
    }

Arraylist源码:

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

LinkedList源码:

    /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

.如果要保证ArrayList线程安全,有几种方式?

  2.1 自己编写一个ArrayList集合类,根据业务一般来说,add/set/remove加锁;

  2.2 利用List<Objecct> list=Collections.synchronizedList(new ArrayList<>());    采用synchronized加锁

public E get(int index) {
    synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
    synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
    synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
    synchronized (mutex) {return list.remove(index);}
}

    

  2.3 new CopyOnWriteArrayList<>().add(" ");    采用ReentrantLock加锁

public boolean add(E e) {      //加锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {        //获取原始集合
            Object[] elements = getArray();
            int len = elements.length;        //复制一个新集合
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;        //替换原始集合为新集合
            setArray(newElements);
            return true;
        } finally {        //释放锁
            lock.unlock();
        }
    }

3.了解CopyOnWriteArrayList底层?CopyOnWriteArrayList与Collections.synchronizedList有什么区别?

  3.1 CopyOnWriteArrayList底层实现:

    CopyOnWriteArrayList在执行修改操作的时候,会复制一份新的数组数据,代价昂贵,修改过来将原来的集合指向新的集合完成操作;

    3.1.1 add添加:在添加的时候是需要加锁的,否则多线程写的时候会Copy出N个副本出来;使用ReentrantLock保证多线程环境下的集合安全;

        public boolean add(E e) {
                    final ReentrantLock lock = this.lock;            //获取了一把锁
                    lock.lock();                        //加锁
                    try {
                        Object[] elements = getArray();        //获取当前数组数据,给elements
                        int len = elements.length;            //记录当前数组的长度
                        Object[] newElements = Arrays.copyOf(elements, len + 1);        //复制一个新的数组
                        newElements[len] = e;    //将数据填入到新数组当中
                        setArray(newElements);    //将当前array指针指向到新的数据
                        return true;
                    } finally {
                        lock.unlock();            //释放锁
                    }
                }

    3.1.2 get读取:读的时候没有加锁,如果读的时候有多个线程正在向CopyOnWriteArrayList添加数据,读还是会读到旧的数据,因为写的时候不会锁住旧的CopyOnWriteArrayList;

  public E get(int index) {
        return get(getArray(), index);
    }

    CopyOnWriteArrayList应用场景:适用于读取操作远大于写操作场景(底层get读取没有加锁,直接获取)

  3.2 Collections.synchronizedList几乎底层方法都加上了synchronized的锁;

    场景:写操作的性能比CopyOnWriteArrayList要好,但是读取的性能不如CopyOnWriteArrayList;

4.CopyOnWriteArrayList设计思想是怎样的,有什么缺点?

  设计思想:读写分离,最终一致;

  缺点:

    内存占用问题:由于写时复制,内存中就会出现两个对象占用空间,如果对象大则容易发生Young GC和Full GC;

    数据一致问题:CopyOnWriteArrayList容器只能保证数据最终一致性,不能保证数据实时一致性;

5.ArrayList扩容机制是怎样的?

  1.7以及之前版本JDK,默认的大小为10;

  1.8版本集合大小如果创建时没有指定,则默认为0;若已经指定集合大小,则初始值为当前指定的大小;

    当第一次添加数据的时候,集合大小扩容为10,第二次以及后续每次按照int oldCapacity=elementData.length;      newCapacity = oldCapacity+(oldCapacity>>1),就是其容量扩展为原来容量的1.5倍;

  5.1 属性

    默认初始值的大小:

private static final int DEFAULT_CAPACITY = 10;

    默认的空对象数组:

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    实际存储数据的数组:

transient Object[] elementData;

  5.2 无参构造器

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

  5.3 扩容机制 

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
//ensureCapacityInternal方法接受了size+1作为minCapacity,并且判断如果数组是空数组,那么10和minCapacity的较大值就作为新的minCapacity。

  private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)  //如果其元素个数大于其容量,则进行扩容;
            grow(minCapacity);
    }

//判断传入的minCapacity和elementData.length的大小,如果elementData.length大于minCapacity,说明数组容量够用,就不需要进行扩容,//反之,则传入minCapacity到grow()方法中,进行扩容

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;      //原来的容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);  //新的容量,原来容量的1.5倍
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)    //如果大于ArrayList可以允许的最大容量,则设置为最大容量
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);    //最终进行扩容,生成一个1.5倍元素
    }

  进入grow()方法,会将newCapacity设置为旧容量的1.5倍,这也是ArrayList每次扩容都为原来的1.5倍的由来。然后进行判断,如果newCapacity小于minCapacity,那么就将minCapacity的值赋予newCapacity。

  然后在检查新容量是否超出了定义的容量,如果超出则调用hugeCapacity方法,比较minCapacity和MAX_ARRAY_SIZE的值;如果minCapacity大,那么新容量为Integer。MAX_VALUE,否则新容量为MAX_ARRAYSIZE。最后调用Arrays.cpoyOf传递elementData和新容量,返回新的elementData;

原文地址:https://www.cnblogs.com/mayuan01/p/12496893.html

时间: 2024-11-13 10:01:49

单列集合List的相关文章

单列集合的总结

Collection 单列集合的根接口 List 特点 有序 有索引 元素可重复 ArrayList 底层数据是数组 查询快 增删慢 线程不安全 效率高 LinkedList 底层数据是链表 查询慢 增删快 线程不安全 效率高 Vector 底层数据是数组 查询快 增删慢 线程安全 效率低 Set  特点 元素唯一 HashSet 底层数据是哈希表 查询和增删都比较快 无序  唯一  先比较hashCode方法 如果hashCode不同 将元素添加到集合 如果hashCode相同 继续比较equ

单列集合的框架简介

集合概述 集合:集合是java中提供的一种容器,可以用来存储多个数据. 集合和数组既然都是容器,它们有啥区别呢? 数组的长度是固定的.集合的长度是可变的. 数组中存储的是同一类型的元素,可以存储基本数据类型值.集合存储的都是对象.而且对象的类型可以不一致.在开发中一般当对象多的时候,使用集合进行存储. 集合框架 集合按照其存储结构可以分为两大类, 单列集合java.util.Collection 双列集合java.util.Map Collection:单列集合类的根接口,集合本身是一个工具,它

获取单列集合,双列集合,数组的Stream流对象以及简单操作

获取流对象 获取单列集合,双列集合,数组的流对象 单列集合获取流对象: 1.java.util.Collection接口中加入了default方法stream()获取流对象,因此其所有实现类均可通过此方式获取流. public class StreamTest01 { List<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream(); Set<String> set =

单列集合

一.集合和数组既然都是容器,它们的区别: 1. 数组的长度是固定的.集合的长度是可变的 2. 数组中存储的是同一类型的元素,可以存储基本数据类型值.集合存储的都是对象.而且对象的类型可以不一致.在开发中一般当对象多的时候,使用集合进行存储 二.集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection和双列集合java.util.Map Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是java.util.Lis

Collection单列集合中的常用实现类

Collection 集合层次的根接口 List 有序 有索引 可以重复 ArrayList 底层数据结构是数组 查询快 增删快 线程不安全 效率高 LinkedList 底层数据结构是链表 查询慢 增删快 线程不安全 效率高 Vector 底层数据是数组 查询和增删介于前两者之间 线程安全 效率低 Set 唯一 在调用add()方法时: 在底层中以map的形式存储,存储时调用元素的hashCode(),equals(),map会自动比较key值(就是hash值) 先比较hash值 如果对象的h

Collection单列集合 Map双列集合

Map集合:该集合存储键值对,一对一对往里存.而且要保证键的唯一性. 1.添加 put(K key, V value) putAll(Map<? extends K,? extends V> m) 2.删除 clear() remove(Object key) 3.判断 containsKey(Object key) containsValue(Object value) isEmpty() 4.获取 get(Object key) :获取值 size() values() entrySet(

java之Set接口(单列集合)

Set接口概述 java.util.Set 接口和 java.util.List 接口一样,同样继承自 Collection 接口,它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Collection 接口更加严格了.与 List 接口不同的是, Set 接口中元素无序,并且都会以某种规则保证存入的元素不出现重复.Set 集合有多个子类,这里我们介绍其中的 java.util.HashSet . java.util.LinkedHa

集合单列--Colletion

集合 学习集合的目标: 会使用集合存储数据 会遍历集合,把数据取出来 掌握每种集合的特性 集合和数组的区别 数组的长度是固定的.集合的长度是可变的. 数组中存储的是同一类型的元素,可以存储基本数据类型值.集合存储的都是对象.而且对象的类型可以不一致,在开发中一般当对象多的时候使用集合进行存储. 集合框架 JAVASE提供了满足各种需求的API,在使用这些API前,先了解其继承与接口操作架构,才能了解何时采用哪个类,以及类之间如何彼此合作,从而达到灵活应用. 集合按照其存储结构可以分为两,分别是单

Java 之集合

collection(单列集合) List(有序,可重复) ArrayList     底层数据结构是数组,查询快,增删慢,线程不安全,效率高 Vector     底层数据结构是数组,查询快,增删慢,线程安全,效率低 LinkedList  底层数据结构是链表,查询慢,增删快,线程不安全,效率高 Set(无序,唯一) HashSet   底层数据结构是哈希表,依赖hashCode()和equals() 执行顺序:判断hashCode()是否相同,相同继续执行equals(),返回true不添加