java集合框架之List以及源码分析(二)

全飞扬
2023-12-01

前言:上次我们分析Collection接口的一些通用功能,还有一些源码的简单分析,接下来我们继续从上往下分析,了解每个接口的特性,以及每个接口下面的实现类底层源码是如何实现的.

一、List接口的特性

官方描述:

  • 该界面的用户可以精确控制列表中每个元素的插入位置。 用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。

首先List接口继承了Collection接口,是一个有序的集合与Set集合相反,增加了一些索引位置的相关操作。

  • add(int index, Object o):在指定位置插入元素
  • addAll(int index, Collection c):...
  • get(int index):取得指定位置元素
  • indexOf(Obejct o):返回对象o在集合中第一次出现的位置
  • lastIndexOf(Object o):...
  • remove(int index):删除并返回指定位置的元素
  • set(int index, Object o):替换指定位置元素
  • subList(int fromIndex, int endIndex):返回子集合

下面我们在介绍一下用工厂的方式创建List集合

  • List.of()静态工厂方法提供了一种创建不可变列表的方便方法。 由这些方法创建的List实例具有以下特征:

    • 它们在结构上是不可变的 。 元素无法添加,删除或替换。 调用任何mutator方法将总是导致UnsupportedOperationException被抛出。 但是,如果包含的元素本身是可变的,则可能会导致列表的内容出现更改。
    • 他们不允许null元素。 尝试使用null元素创建它们将导致NullPointerException
    • 如果所有元素是可序列化的,它们是可序列化的。
    • 列表中的元素的顺序与提供的参数或提供的数组中的元素的顺序相同。
    • 他们是value-based 。 调用者不应该对返回的实例的身份进行假设。 工厂可以自由创建新的实例或重用现有的实例。 因此,对这些实例的身份敏感操作(引用等式( == ),身份哈希码和同步)是不可靠的,应该避免。
    • 它们按Serialized Form页面的规定进行序列化。
public class Demo1_Collecton {
    public static void main(String[] args) {
       
        List<String> a = List.of("a", "b");
        List<Integer> b = List.of(1, 3);
        System.out.println(a);
        System.out.println(b);
    }
}

注意这个新特性是缺点的,我们不能修改,不能删除和添加,只能查看,如果执行插入操作的话,编译不报错,运行报错,错误如下:

UnsupportedOperationException  不支持操作异常  ,所以这个快速创建集合的方式,一般很少用。

二、源码探究

* @author  Josh Bloch
 * @author  Neal Gafter
 * @see Collection
 * @see Set
 * @see ArrayList
 * @see LinkedList
 * @see Vector
 * @see Arrays#asList(Object[])
 * @see Collections#nCopies(int, Object)
 * @see Collections#EMPTY_LIST
 * @see AbstractList
 * @see AbstractSequentialList
 * @since 1.2
 */

public interface List<E> extends Collection<E> {

我们看到特也是一个接口,继承了Collection接口,也就是,继承了Collection的所有通用方法,往下都是一些方法,我们选一些金典的分析一下

/**
     * Returns an immutable list containing four elements.
     *
     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
     *
     * @param <E> the {@code List}'s element type
     * @param e1 the first element
     * @param e2 the second element
     * @param e3 the third element
     * @param e4 the fourth element
     * @return a {@code List} containing the specified elements
     * @throws NullPointerException if an element is {@code null}
     *
     * @since 9
     */
    static <E> List<E> of(E e1, E e2, E e3, E e4) {
        return new ImmutableCollections.ListN<>(e1, e2, e3, e4);
    }

我们看看静态化工厂是如何快速创建集合元素的。

class ImmutableCollections {
    /**
     * A "salt" value used for randomizing iteration order. This is initialized once
     * and stays constant for the lifetime of the JVM. It need not be truly random, but
     * it needs to vary sufficiently from one run to the next so that iteration order
     * will vary between JVM runs.
     */
    static final int SALT;
    static {
        long nt = System.nanoTime();
        SALT = (int)((nt >>> 32) ^ nt);
    }

它是通过ImmutableCollections这个类来创建的,并且这个静态抽象类继承了AbstractList<E>实现了

RandomAccess, Serializable
abstract static class AbstractImmutableList<E> extends AbstractList<E>
                                                implements RandomAccess, Serializable {
        @Override public boolean add(E e) { throw uoe(); }
        @Override public boolean addAll(Collection<? extends E> c) { throw uoe(); }
        @Override public boolean addAll(int index, Collection<? extends E> c) { throw uoe(); }
        @Override public void    clear() { throw uoe(); }
        @Override public boolean remove(Object o) { throw uoe(); }
        @Override public boolean removeAll(Collection<?> c) { throw uoe(); }
        @Override public boolean removeIf(Predicate<? super E> filter) { throw uoe(); }
        @Override public void    replaceAll(UnaryOperator<E> operator) { throw uoe(); }
        @Override public boolean retainAll(Collection<?> c) { throw uoe(); }
        @Override public void    sort(Comparator<? super E> c) { throw uoe(); }
    }

重写一些常用的方法,而每个方法都抛出同一个异常,我们也从根源,找到了,它为啥,就是不能添加删除等等,因为每当对这个新创建的集合操作是,都会有异常出现。

static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }

我们看看Sort()这个方法

default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

将对象转化为数组,调用Arrays数组工具类的sort方法传入这个转化的数组进行排序,然后调用List Iterator迭代器进行遍历,进行相应的操作,等下次有时间我们在一起探究一下数组工具类sort是如何排序的,它的底层又是实现的,有二分搜索,有快排等等,这些内容很精彩,他们都是一个个小世界,我要去探究探究。

 @Override
    default Spliterator<E> spliterator() {
        if (this instanceof RandomAccess) {
            return new AbstractList.RandomAccessSpliterator<>(this);
        } else {
            return Spliterators.spliterator(this, Spliterator.ORDERED);
        }
    }

我们可以看到这是一个默认并行迭代器的使用,首先我们探究一下if中this instanceof RandomAccess是是么作用,我们点击RandomAccess查看它的源码

public interface RandomAccess {
}

这是一个空接口,这个很闹心?为啥要设计一个空接口呢,为什么,它有何作用,一系列的问题都迎面而来,我们自己百度看看资料,我们可以知道空接口的作用一般是起到一个标识的作用。

通俗点讲,就是判断一个list是否实现了RandomAcess接口,如果实现了,采用下面所示的简单的for循环进行访问速度比较快:

for (int i=0, n=list.size(); i < n; i++)
   list.get(i);

如果未实现RandomAcess接口,则采用下面的iterator循环访问速度比较快。

for (Iterator i=list.iterator(); i.hasNext(); )
   i.next();

判断使用instanceof,即

if (list instanceof RandomAccess)

至此我们就大概理解了为什么它要实现这个空接口,因为List要访问元素,for循环和iterator的访问速度不同

下篇我们继承List接口实现类的核心源码探究

 类似资料: