Java集合类简介

基本概念

Java的集合类,也可以叫做容器类,用来“保存对象”。它有两种:

Collection

如果保存的对象是单个对象,就是 Collection 类。Collection 类就是一个接口。先看看它的实现类和子接口。
Collection Interface
这里面最重要的子接口是:Set, List, Queue. JDK 文档说了,JDK不提供Collection接口的直接实现。
看到这里挺纳闷的,明明有那么多类实现了Collection接口,这不是乱说吗?难道是老的JDK有没有直接实现,新的JDK已经有直接实现了,带着问题小心求证一下:

JDK7: https://docs.oracle.com/javase/7/docs/api/java/util/Collection.html
The JDK does not provide any direct implementations of this interface: it provides implementations of more specific subinterfaces like Set and List. 

JDK8: https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html
The JDK does not provide any direct implementations of this interface: it provides implementations of more specific subinterfaces like Set and List. 

JDK9: https://docs.oracle.com/javase/9/docs/api/java/util/Collection.html
The JDK does not provide any direct implementations of this interface: it provides implementations of more specific subinterfaces like Set and List.

JDK10: https://docs.oracle.com/javase/10/docs/api/java/util/Collection.html
The JDK does not provide any direct implementations of this interface: it provides implementations of more specific subinterfaces like Set and List. 

事实结果说明我的假设是错的。

Class name type related jar description
ValuesView in ConcurrentHashMap (java.util.concurrent) inner class JDK rt.jar 没有实现add 和 addAll 方法
BeanContext (java.beans.beancontext) interface JDK rt.jar public interface BeanContext extends BeanContextChild, Collection, DesignMode, Visibility
Multiset (com.google.common.collect) interface guava-27.0.1-jre.jar 一个像Set一样可以保存元素的集合,不过它可以保存重复的元素,这种集合又称之为Bag
CheckedCollection in Collections (java.util) inner class JDK rt.jar 一个代理类,在创建的时候传入被代理的Collection对象
Set (java.util) interface JDK rt.jar 不包含重复元素的集合。 更正式的是,集不包含一对元素 e1 和 e2,使得 e1.equal(e2),最多包含一个null元素。 顾名思义,此接口对数学集抽象进行模型。
SynchronizedCollection in Synchronized (com.google.common.collect) inner class guava-27.0.1.jar static class SynchronizedCollection extends SynchronizedObject implements Collection 通过 synchronized (mutex) {}的方式给代理对象加锁
SynchronizedCollection in Collections (java.util) inner class rt.jar static class SynchronizedCollection implements Collection, Serializable 通过 synchronized (mutex) {}的方式给代理对象加锁
ContextStack in ThreadContext (org.apache.logging.log4j) inner interface log4j-api.2.11.2.jar The ThreadContext Stack interface.
AbstractCollection (java.util) abstract class JDK rt.jar 没有实现add(E var1)等方法,会抛UnsupportedOperationException
List (java.util) interface rt,jar 有序集合(也称为序列)。该界面的用户可以精确控制列表中每个元素的插入位置。用户可以通过其整数索引(列表中的位置)访问元素,并在列表中搜索元素。
CollectionView in ConcurrentHashMap (java.util.concurrent) inner abstract static class JDK rt.jar 没有实现完Collection接口的方法。
ForwardingCollection (com.google.common.collect) abstract class abstract class 装饰器模式,允许用户向现有对象添加新功能,而无需更改其结构。这种设计模式属于结构模式,因为该模式充当现有类的包装。
UnmodifiableCollection in Collections (java.util) innner static class rt.jar 包含一个final属性final Collection<? extends E> c;
Queue (java.util) interface rt.jar 设计用于在处理之前容纳元素的集合。除了基本Collection操作外,队列还提供其他插入,提取和检查操作。这些方法中的每一种都以两种形式存在:一种在操作失败时引发异常,另一种返回一个特殊值(null或false,取决于操作)。插入操作的后一种形式是专门为限制容量的Queue 实现而设计的。在大多数实现中,插入操作不会失败。

可以看出,Collection 接口最重要的子接口就是List, Set 和 Queue 了。

先看看Collection的所有方法:
Collection methods

List

List interface
有序集合(也称为序列)。该界面的用户可以精确控制列表中每个元素的插入位置。用户可以通过其整数索引(列表中的位置)访问元素,并在列表中搜索元素。
和 Set 相比:

  • List 允许重复的元素, 也就是说,e1.equals(e2), 可以插入e1 和 e2.
  • List 如果完全允许使用空元素,则通常允许使用多个空元素。允许插入多个 null 元素.
    List 相较于 Collection 接口,扩展了根据索引位置操作元素的方法:
  • get(int)
  • set(int, E)
  • add(int, E)
  • remove(int)

List 还提供了根据给的对象找索引的方法。

  • indexOf(Object). 查到第一次出现给定对象的索引位置。
  • lastIndexOf(Object) , 查找List当中最后一次出现给定对象的位置。

还有返回ListIterator的方法:

  • listIterator() 返回此列表中的元素的列表迭代器(按适当顺序) ListIterator。
  • listIterator(int) 从列表中的指定位置开始,以适当的顺序返回在此列表中的元素上的列表迭代器 ListIterator。
    插一句嘴,Iterator 和 Iterable 的区别:
package java.util;

import java.util.function.Consumer;

public interface Iterator<E> {
    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    default void forEachRemaining(Consumer<? super E> var1) {
        Objects.requireNonNull(var1);

        while(this.hasNext()) {
            var1.accept(this.next());
        }

    }
}
package java.lang;

import java.util.Iterator;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;

public interface Iterable<T> {
    Iterator<T> iterator();

    default void forEach(Consumer<? super T> var1) {
        Objects.requireNonNull(var1);
        Iterator var2 = this.iterator();

        while(var2.hasNext()) {
            Object var3 = var2.next();
            var1.accept(var3);
        }

    }

    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(this.iterator(), 0);
    }
}

Iterator 有方法:

  • hasNext() 如果迭代具有更多元素,则返回true。
  • next() 返回迭代中的下一个元素。
  • remove() 从基础集合中移除此迭代器返回的最后一个元素(可选操作)。
  • forEachRemaining​(Consumer<? super E> action) 对剩余的每个元素执行给定的操作,直到所有元素都已处理或该操作引发异常。
    Iterable 有方法:
  • default void forEach​(Consumer<? super T> action) 对中的每个元素执行给定的操作,Iterable 直到所有元素都已处理或该操作引发异常。
  • Iterator iterator​() 返回类型为的元素上的迭代器T。
  • default Spliterator spliterator​() Spliterator在this所描述的元素上 创建一个Iterable。

从java9开始,List 还提供一堆of方法,就是返回一个大小不可变的List. 代码实现是用了ImmutableCollections类的方法。
比如返回有N个原属构成的大小固定的List
看到没有,为了达到大小不可变,本质上就是用final修饰了: private final E[] elements;

    static final class ListN<E> extends AbstractImmutableList<E>
            implements Serializable {

        // EMPTY_LIST may be initialized from the CDS archive.
        static @Stable List<?> EMPTY_LIST;

        static {
            VM.initializeFromArchive(ListN.class);
            if (EMPTY_LIST == null) {
                EMPTY_LIST = new ListN<>();
            }
        }

        @Stable
        private final E[] elements;

        @SafeVarargs
        ListN(E... input) {
            // copy and check manually to avoid TOCTOU
            @SuppressWarnings("unchecked")
            E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input
            for (int i = 0; i < input.length; i++) {
                tmp[i] = Objects.requireNonNull(input[i]);
            }
            elements = tmp;
        }

        @Override
        public boolean isEmpty() {
            return elements.length == 0;
        }

        @Override
        public int size() {
            return elements.length;
        }

        @Override
        public E get(int index) {
            return elements[index];
        }

        @java.io.Serial
        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            throw new InvalidObjectException("not serial proxy");
        }

        @java.io.Serial
        private Object writeReplace() {
            return new CollSer(CollSer.IMM_LIST, elements);
        }

        @Override
        public Object[] toArray() {
            return Arrays.copyOf(elements, elements.length);
        }

        @Override
        @SuppressWarnings("unchecked")
        public <T> T[] toArray(T[] a) {
            int size = elements.length;
            if (a.length < size) {
                // Make a new array of a's runtime type, but my contents:
                return (T[]) Arrays.copyOf(elements, size, a.getClass());
            }
            System.arraycopy(elements, 0, a, 0, size);
            if (a.length > size) {
                a[size] = null; // null-terminate
            }
            return a;
        }
    }

关于Stable注解的解释

如果字段的所有组件变量最多更改一次值,则字段可能被批号为稳定字段。字段的值计为其组件值。如果将字段键入为数组,则数组的所有非空组件(深度到字段的数组类型排名)也计为组件值。通过扩展,任何已批过为稳定的变量(数组或字段)称为稳定变量,其非空值或非零值称为稳定值。由于所有字段的引用默认值为 null(对基元为零),因此此注释指示存储在字段中的第一个非空值(resp.,非零)值永远不会更改。如果字段不是数组类型,则没有数组元素,则指示为稳定的值只是字段的值。如果字段值的动态类型是数组,但静态类型不是,则数组的组件不是被视为稳定。如果字段是数组类型,则字段值和字段值的所有组件(如果字段值为非空)都指示为稳定。如果字段类型是排名 [@code N = 1] 的数组类型,则字段值的每个组件(如果字段值是非 null)被视为排名 [@code N-1] 的稳定数组。声明 [@code 最终] 的字段也可以被条号为稳定字段。由于最终字段已作为稳定值,因此此类注释不会传达有关字段值更改的其他信息,但如果字段的类型是数组类型()如上所述)。HotSpot VM 依赖于此注释将非空(resp.,非零)组件值提升为常量,从而根据此类值(如常量折叠)实现代码的卓越优化。更具体地说,HotSpot VM 将以与静态最终字段类似的方式处理非空稳定字段(最终或其他),从而将字段的值提升为常量。 因此,撇开空/非空值和数组的差异不谈,最终的稳定字段将被视为 Java 语言和 HotSpot VM 的最终字段。如果为被批示为稳定的字段提供第三个值(通过显式更新稳定字段、稳定数组的组件或通过反射或其他方式更新最终稳定字段),则会发生什么情况(当前)未定义。由于 HotSpot VM 将非空组件值提升为常量,因此,如果此类常量(字段的第二个值)用作字段的值(更改为第三个值),则 Java 内存模型可能会显示为损坏).@implNote此注释仅适用于引导加载程序加载的类的字段。 将忽略在引导加载程序之外加载的类字段上的注释。

发布了76 篇原创文章 · 获赞 9 · 访问量 12万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章