基本概念
Java的集合类,也可以叫做容器类,用来“保存对象”。它有两种:
Collection
如果保存的对象是单个对象,就是 Collection 类。Collection 类就是一个接口。先看看它的实现类和子接口。
这里面最重要的子接口是: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的所有方法:
List
有序集合(也称为序列)。该界面的用户可以精确控制列表中每个元素的插入位置。用户可以通过其整数索引(列表中的位置)访问元素,并在列表中搜索元素。
和 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此注释仅适用于引导加载程序加载的类的字段。 将忽略在引导加载程序之外加载的类字段上的注释。