list集合怎么能存储值

list集合可以存储get方法获取的值嘛,怎么才能保存下来啊

        List<String> list = new ArrayList<>();

        list.add("Value 1");
        list.add("Value 2");
        list.add("Value 3");

        String value1 = list.get(0);
        String value2 = list.get(1);
        String value3 = list.get(2);

        System.out.println(value1);
        System.out.println(value2);
        System.out.println(value3);

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 这个问题的回答你可以参考下: https://ask.csdn.net/questions/7678404
  • 我还给你找了一篇非常好的博客,你可以看看是否有帮助,链接:List集合深度复制方法,解决List集合浅复制后修改原集合复制后的也修改的问题
  • 除此之外, 这篇博客: List集合的实现类总结中的 查找方法 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
        public E get(int index) {
            rangeCheck(index);
    
            return elementData(index);
        }
    
        @SuppressWarnings("unchecked")
        E elementData(int index) {
            return (E) elementData[index];
        }

    首先,检测index是否在集合范围内,若不在,抛出IndexOutOfBoundsException;

    然后直接根据index查找数组中的对应的值,并返回。

    通过上面的add方法可知,每次在添加元素之前都会进行扩容检测。下面根据源码来分析ArrayList集合具体是怎么进行扩容的。

    扩容操作的第一步:

        private void ensureCapacityInternal(int minCapacity) {
            modCount++;
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    • 在添加元素的时候,会调用ensureCapacityInternal方法来判断是否需要扩容;
    • 当minCapacity(也就是add方法中的(size+1))大于数组长度时,将调用grow方法真正的进行扩容操作;
    • 其中,源码中的modCount++表示记录集合修改次数。

    扩容操作的第二步:

        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    • 源码中的int newCapacity = oldCapacity + (oldCapacity >> 1)就是扩容的重点。其中(oldCapacity >> 1)表示右移一位,可以看作(oldCapacity/2)。即扩容1.5倍
    • 后面的if语句是对容量的一系列判断,ArrayList最大容量为Integer.MAX_VALUE
    • 最后,将旧数组复制元素到新数组完成扩容操作。

    ArrayList底层依赖数组来实现,查询效率较高,增删效率较低

    ArrayList中的元素有序、可重复、允许null值

    ArrayList会自动进行扩容1.5倍。初始化时尽量指定初始容量,可避免频繁扩容,影响程序执行效率

    线程不安全,适用于单线程环境。

     

    //继承关系
    java.lang.Object 
        java.util.AbstractCollection<E> 
            java.util.AbstractList<E> 
                java.util.Vector<E> 
    
    //实现接口
    List<E>, Collection<E>, Iterable<E>, Serializable, Cloneable, RandomAccess
        protected Object[] elementData;

    Vector的底层实现与ArrayLIst一样,也是依赖数组。

        public Vector() {
            this(10);
        }
    
        public Vector(int initialCapacity) {
            this(initialCapacity, 0);
        }
    
        public Vector(int initialCapacity, int capacityIncrement) {
            super();
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            this.elementData = new Object[initialCapacity];
            this.capacityIncrement = capacityIncrement;
        }
    
        public Vector(Collection<? extends E> c) {
            ......
        }

    第一个无参构造:调用第二个构造方法,继而调用第三个构造方法,所传参数就是默认初始化集合容量,大小为10

    第二个带参构造:调用第三个构造方法,自定义集合容量;

    第三个带参构造:自定义集合容量,并设置容量增长量,如果增量大于0,则在扩容后的容量为原容量+增量

    第四个带参构造:这里不进行说明。

        //将指定的元素追加到此Vector的末尾。
        public synchronized boolean add(E e) {
            modCount++;
            ensureCapacityHelper(elementCount + 1);
            elementData[elementCount++] = e;
            return true;
        }

    Vector只有在集合末尾添加单个元素的方法(当然,它有在指定位置添加一个集合的方法,这里不作讲述),与ArrayList的add()方法类似。

    有一点区别就是Vector的扩容检测方法是ensureCapacityHelper(elementCount + 1)。

        public synchronized E get(int index) {
            if (index >= elementCount)
                throw new ArrayIndexOutOfBoundsException(index);
    
            return elementData(index);
        }
    
        @SuppressWarnings("unchecked")
        E elementData(int index) {
            return (E) elementData[index];
        }

    同ArrayList的get方法类似。 

    Vector的扩容与ArrayList有一些不同,主要是多了一个成员变量capacityIncrement,下面重点讲与ArrayList不同的地方。

        protected int capacityIncrement;

    扩容操作的第一步:同ArrayList类似

        private void ensureCapacityHelper(int minCapacity) {
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }

    扩容操作的第二步:

        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                             capacityIncrement : oldCapacity);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            elementData = Arrays.copyOf(elementData, newCapacity);
        }

    从源码中第三行代码可以看出Vector具体的扩容方法:

    • 在对集合进行初始化的时候,若没有通过构造方法对capacityIncrement赋值(默认为0)则集合扩容后的容量为原来的2倍

    否则,集合扩容后的容量为原容量+增量

    Vector底层依赖数组来实现,查询效率较高,增删效率较低

    Vector中的元素有序、可重复、允许null值,添加单个元素的话,只能添加到集合末尾

    Vector会自动进行扩容。扩容后的容量由增量来决定,(2倍 or 原容量+增量)

    大多数方法用关键字synchronized修饰,线程安全。

    //继承关系
    java.lang.Object 
        java.util.AbstractCollection<E> 
            java.util.AbstractList<E> 
                java.util.AbstractSequentialList<E> 
                    java.util.LinkedList<E>
    
    //接口实现
    List<E>, Queue<E>, Deque<E>, Collection<E>, Iterable<E>, Serializable, Cloneable
    transient Node<E> first;
    
    transient Node<E> last;

     LinkedList底层依赖链表。从源码中可以看出,它封装了头结点、尾节点。而链表的优点就是方便增删节点

    若面试官细问,可以回答LinkedList底层依赖双向循环链表。因为双向链表包含两个指针,pre指向前一个节点,next指向后一个节点。 由下面源码可知,第一个节点的pre指向最后一个节点,最后一个节点的next指向第一个节点,形成一个“环”。 

         private void linkFirst(E e) {
            final Node<E> f = first;
            final Node<E> newNode = new Node<>(null, e, f);
            first = newNode;
            if (f == null)
                last = newNode;
            else
                f.prev = newNode;
            size++;
            modCount++;
        }
    
        void linkLast(E e) {
            final Node<E> l = last;
            final Node<E> newNode = new Node<>(l, e, null);
            last = newNode;
            if (l == null)
                first = newNode;
            else
                l.next = newNode;
            size++;
            modCount++;
        }
        public LinkedList() {
        }
    
        public LinkedList(Collection<? extends E> c) {
            this();
            addAll(c);
        }

    第一个无参构造:LinkedList集合容量默认为空,没有扩容的概念。

    第二个带参构造:这里不进行说明。

        //将指定的元素追加到此列表的末尾。
        public boolean add(E e) {
            linkLast(e);
            return true;
        }  
    
        //将指定元素插入此列表中的指定位置。
         public void add(int index, E element) {
            checkPositionIndex(index);
    
            if (index == size)
                linkLast(element);
            else
                linkBefore(element, node(index));
        }    
        //返回此列表中指定位置的元素。
        public E get(int index) {
            checkElementIndex(index);
            return node(index).item;
        }
    
        Node<E> node(int index) {
            // assert isElementIndex(index);
    
            if (index < (size >> 1)) {
                Node<E> x = first;
                for (int i = 0; i < index; i++)
                    x = x.next;
                return x;
            } else {
                Node<E> x = last;
                for (int i = size - 1; i > index; i--)
                    x = x.prev;
                return x;
            }
        }

    首先,进行异常检测,若有异常,抛出IndexOutOfBoundsException;

    因为LinkedList无法随机访问,只能通过遍历的方式找到相应的节点;

    从源码中可以看出,为了提高效率,当前位置首先和元素数量的中间位置开始判断,小于中间位置,从头节点开始遍历,大于中间位置从尾节点开始遍历。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^