Java 集合框架1:Collection
集合框架
1.概述
集合 collection ,也称为 container 容器,一个将多个对象组合成一个单元的对象。集合用于存储、返回、操作以及聚合。集合框架是一个统一表示和管理集合的框架。它减少不必要的编程工作,提升程序的速度和质量、互操作性,减少重复设计。
接口、实现、聚合操作和算法共同组成了 Java 的集合框架。
- 接口:表示集合的抽象数据类型。接口允许独立于集合具体表示(实现)来操作集合。
- 实现:对集合接口的具体实现。
- 算法:集合上的各种计算操作,算法应该具有多态性,即同样的方法可以在不同的实现下的集合接口下使用。
2.Collection
Collection 是集合框架最基本的接口,除了 Map 外,List、Queue、Set 类集合都实现了它。
基本方法
1.基础操作:
int size();
boolean isEmpty();
boolean contains(Object o);
boolean add(E e);
boolean remove(Object o);
Iterator<E> iterator();
2.bulk 操作有:
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
。
3.stream 流:
-
default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } -
default Stream<E> parallelStream() { return StreamSupport.stream(spliterator(), true); }
4.针对Array
,还有以下的额外操作:
Object[] toArray();
<T> T[] toArray(T[] a);
容器遍历
遍历容器主要有三种方法: 聚合操作、for-each、迭代器Iterator。
String joined = elements.stream() .map(Object::toString) .collect(Collectors.joining(", "));
for (Object o : collection) System.out.println(o);
public interface Iterator<E> { boolean hasNext(); E next(); void remove(); //optional }
对于 Iterator 中的 remove 方法,只能在每次 next 方法之后至多调用一次,否则将会抛出异常。使用迭代器的好处是可以对 collection 中的元素进行删除。在迭代过程中,Iterator 的 remove 方法是唯一可以删除元素的操作,如果在迭代过程中通过其他方式对元素有其他修改,将会抛出异常。
在下面的两种情况下使用 Iterator,而不是 for-each:
- 在迭代中需要删除元素。
- 并行的对容器进行迭代操作。
static void filter(Collection<?> c) { for (Iterator<?> it = c.iterator(); it.hasNext(); ) if (!cond(it.next())) it.remove(); }
容器实现
集合的实现实现了集合接口的方法,主要包含:
- General-purpose,使用最广泛,在日常场景中使用。
- Special-purpose,对于特殊场景设计,具有非标准的性能特点和使用限制,行为。
- Concurrent,支持并发。
- Wrapper,用于组合不同的实现,比如为 general-purpose 实现的集合添加特殊的功能。
- Convenience,最小实现,通过静态工厂方法方法获得,用于替代 general-purpose 的方便,有效的实现。
- Abstract,骨架实现,方便构成。
上面的实现都允许插入 null 元素;并且都不是线程安全的。具有快速失败(fast-fail)的迭代器设计:当迭代容器时检测到了并行修改时,迭代器将会立即抛出异常,防止任意的、不可期的行为发生。它们都实现了 Serializable 接口,并且都实现了 public 的 clone 方法。遗留的 Vector 和 Hashtable 是线程安全的,但已经建议不再使用了。如果需要线程安全的容器,可以使用 Collections 中的 synchronized wrapper 将上面的实现转化成线程安全的,另外在 Java 的 concurrent 包中,提供了更多高性能的并发容器,比如 BlockingQueue , ConcurrentHashMap 等。
元素排序
集合中某些实现类底层是按照一定规则进行存储,如 TreeMap 、 TreeSet。这里有两种排序方式:自然排序与定制排序。
1.自然排序
Java 提供 Comparable 接口,其中有 compareTo 方法。使用自然排序,需要容器中的元素实现 Comparable 接口中的 compareTo 方法,集合的排序根据 compareTo 方法来比较大小进行排序。
Java 基本类型的包装类都已实现 compareTo 方法。
对于 Comparable 接口的实现,最好符合以下事项:
- 符合
e1.compareTo(e2) == 0
和e1.equals(e2)
等价。 - 对 null 元素比较需要抛出 NullPointerException。因为 null 不是任何类的实例。
- compareTo 返回负数、零、正数,分别表示小于、等于、大于,且满足
sign(x.compareTo(y)) == -sign(y.compareTo(x))
。 - compareTo 的实现满足传递性:
x.compareTo(y) == 0
=>sign(x.compareTo(z)) = sign(y.compareTo(z))
。
2.定制排序
如果需要按特殊规则排序或者元素本身不具备比较性时,需要使用定制排序。
定制排序需要一个实现 Comparator 接口的 compare 方法的实现类,并在创建接口时将其作为参数传递过去。示例如下:
public class MyComparator implements Comparator { @Override public int compare(Object o1, Object o2) { // 指定排序规则 return 0; } }
TreeSet tree = new TreeSet(new MyComparator());
3.Collections
collections 是作为 collection 的工具实现类。常用方法示例如下:
包装器实现(Wrapper Implementations)
collections 的包装器是装饰器模式来实现的,包装器并不提供公用类,都只提供静态的工厂方法。
1.同步包装器(Synchronization Wrappers)
同步包装器为非线程安全的 collection 提供同步,通过以下方法,返回对应实现类的同步版本类:
上面的每一个方法返回线程安全的基于指定 collection 的同步 collection。
下面以 synchronizedList 方法装饰 ArrayList 为例,使用以下方式返回线程安全的 SynchronizedRandomAccessList:
List<Type> list = Collections.synchronizedList(new ArrayList<Type>());
ArrayList 实现了 RandomAccess 接口,应当返回 SynchronizedRandomAccessList ,而 SynchronizedRandomAccessList 继承 SynchronizedList ,实际上的同步实现在 SynchronizedList 中。(如果装饰 LinkedList,因为未实现 RandomAccess 接口,直接返回 SynchronizedList。)
SynchronizedList 类中,所有方法内部添加了 synchronized 来同步代码块。
注意:SynchronizedList 只实现了与 ArrayList 实现的接口,对于 ArrayList 提供的额外方法,如 ensureCapacity,无法调用。其它同步包装器也如此。
2.不可变包装器(Unmodified Wrappers)
不可变包装器将 collection 进行改变的方法实现为抛出 UnsupportedOperationException,来保证 collection 不可变或只读,通过以下方法,返回对应实现类的不可变版本类:
Unmodified Wrappers 的实现方式与 Synchronization Wrappers 实现类似,都采用了装饰器模式。以返回的 UnmodifiableList 为例,对于原本修改的方法,“重写”为抛出异常:
Empty 对象
Collections 以下方法返回空的容器:
它主要用于取代 null 的情况,当某个方法需要一个容器作为参数且不能为 null,你不想提供任何值的时候,就创建一个空的 collection:
tourist.declarePurchases(Collections.emptySet());
其实现方式是返回对应的 Empty 对象,以 emptyList 为例:
可以看出,每次调用 emptyList 时,返回的都是同一个 EmptyList 对象,以达到共用的目的。
不可变单例 Set(Immutable Singleton Set)
singleton 用于创建只包含单个指定元素的 Set。常用的就是移除集合中某个元素:
c.removeAll(Collections.singleton(e));
另外一个类似的就是移除 Map 中指定的 value:
job.values().removeAll(Collections.singleton(LAWYER));
不可变多副本列表(Immutable Multiple-Copy List)
nCopies 方法可以创建同个对象的多个副本的不可变 List 。比如创建具有 1000 个 Type 类型的 null 对象:
List<Type> list = new ArrayList<Type>(Collections.nCopies(1000, (Type) null);
第二个主要的用法是在List后面追加:
lovablePets.addAll(Collections.nCopies(69, "fruit bat"));
元素排序
sort 方法实现了对 List 进行排序,其实归根到底还是调用了 Arrays 的排序算法。
Arrays 的 mergeSort 用到了快速排序算法,其比较大小的依据为 Comparable 接口中的 compareTo 方法。
所以调用 sort 方法时,需要保证集合中的元素实现了 Comparable 接口:
4.Stream API
Stream 是 Java 8 中处理集合的关键抽象概念,它可以指定对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
它具有如下特点:
- Stream 自己不会存储元素。
- Stream 不会改变源对象,相反,它们会返回一个持有结果的新 Stream。
- Stream 操作是延迟执行的,这意味这它们会等到需要结果的时候才执行。
// TODO 30/11/2022 meyok: 聚合操作
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构