Java基础-集合
1.Java集合框架

下面是简化图:

在介绍这张图之前,先来了解下Java集合的设计思路。
1.1集合接口与实现分离
以队列这种常见的数据结构来说明Java是如何分离接口(implementation)与实现的。

- Queue接口继承自Collection接口;
- Queue接口有子接口;
- 上面的实现类大多是实现子接口,像ArrayDeque是Deque< E >的实现。
下面以ArrauDeque的实现过程来举栗zi
队列接口的基本形式为:
public interface Queue<E>{
void add(E element);
E remove();
int size();
}
Qeue的子接口Deque接口:
public interface Deque<E> extends Queue<E>{
void addLast(E e);
boolean offerFirst(E e);
}
//它新增了一些自己的方法
ArrayDeque是Deque的实现类:
public class ArrayDeque<E> extends AbstractCollection<E>
implements Deque<E>, Cloneable, Serializable
新建一个ArrayDeque对象使用:
ArrayDeque<Object> objects = new ArrayDeque<>();
1.2Colleaction接口
Collection中有两个基本方法
public interface Collection<E> extends Iterable<E>{
boolean add(E element);
Iterator<E> iterator();
//返回一个实现了Iterraator接口的对象
}
1.3迭代器
Iterraator接口中包含以下4个方法:
default void | forEachRemaining(Consumer<? super E> action) 对每个剩余元素执行给定的操作,直到所有元素都被处理或动作引发异常。 |
|---|---|
boolean | hasNext() 如果迭代具有更多元素,则返回 true 。 |
E | next() 返回迭代中的下一个元素。 |
default void | remove() 从底层集合中删除此迭代器返回的最后一个元素(可选操作)。 |
我们平时常用的遍历集合中的元素的方法:
List<Integer> list=new ArrayList<>();
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
1.4Collection中的常用方法
boolean | add(E e) 将一个元素添加到集合中,如果由于这个调用改变了集合,则返回true |
|---|---|
boolean | addAll(Collection<? extends E> c) 将指定集合中的所有元素添加到此集合(可选操作)。 |
void | clear() 从此集合中删除所有元素(可选操作)。 |
boolean | contains(Object o) 如果此集合包含指定的元素,则返回 true 。 |
boolean | containsAll(Collection<?> c) 如果此集合包含指定 集合中的所有元素,则返回true。 |
boolean | equals(Object o) 将指定的对象与此集合进行比较以获得相等性。 |
int | hashCode() 返回此集合的哈希码值。 |
boolean | isEmpty() 如果此集合不包含元素,则返回 true 。 |
Iterator<E> | iterator() 返回此集合中的元素的迭代器。 |
default Stream<E> | parallelStream() 返回可能并行的 Stream与此集合作为其来源。 |
boolean | remove(Object o) 从该集合中删除指定元素的单个实例(如果存在)(可选操作)。 |
boolean | removeAll(Collection<?> c) 删除指定集合中包含的所有此集合的元素(可选操作)。 |
default boolean | removeIf(Predicate<? super E> filter) 删除满足给定谓词的此集合的所有元素。 |
boolean | retainAll(Collection<?> c) 仅保留此集合中包含在指定集合中的元素(可选操作)。 |
int | size() 返回此集合中的元素数。 |
default Spliterator<E> | spliterator() 创建一个Spliterator在这个集合中的元素。 |
default Stream<E> | stream() 返回以此集合作为源的顺序 Stream 。 |
Object[] | toArray() 返回一个包含此集合中所有元素的数组。 |
<T> T[] | toArray(T[] a) 返回包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。 |
1.5集合框架的接口
| 序号 | 接口描述 |
|---|---|
| 1 | Collection 接口 Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。Collection 接口存储一组不唯一,无序的对象。 |
| 2 | List 接口 List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。List 接口存储一组不唯一,有序(插入顺序)的对象。 |
| 3 | Set Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。Set 接口存储一组唯一,无序的对象。 |
| 4 | SortedSet 继承于Set保存有序的集合。 |
| 5 | Map Map 接口存储一组键值对象,提供key(键)到value(值)的映射。 |
| 6 | Map.Entry 描述在一个Map中的一个元素(键/值对)。是一个 Map 的内部接口。 |
| 7 | SortedMap 继承于 Map,使 Key 保持在升序排列。 |
| 8 | Enumeration 这是一个传统的接口和定义的方法,通过它可以枚举(一次获得一个)对象集合中的元素。这个传统接口已被迭代器取代。 |
2.具体集合
| 序号 | 类描述 |
|---|---|
| 1 | AbstractCollection 实现了大部分的集合接口。 |
| 2 | AbstractList 继承于AbstractCollection 并且实现了大部分List接口。 |
| 3 | AbstractSequentialList 继承于 AbstractList ,提供了对数据元素的链式访问而不是随机访问。 |
| 4 | LinkedList 该类实现了List接口,允许有null(空)元素。主要用于创建链表数据结构,该类没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,解决方法就是在创建List时候构造一个同步的List。例如:List list=Collections.synchronizedList(newLinkedList(...));LinkedList 查找效率低。 |
| 5 | ArrayList 该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%,插入删除效率低。 |
| 6 | AbstractSet 继承于AbstractCollection 并且实现了大部分Set接口。 |
| 7 | HashSet 该类实现了Set接口,不允许出现重复元素,不保证集合中元素的顺序,允许包含值为null的元素,但最多只能一个。 |
| 8 | LinkedHashSet 具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。 |
| 9 | TreeSet 该类实现了Set接口,可以实现排序等功能。 |
| 10 | AbstractMap 实现了大部分的Map接口。 |
| 11 | HashMap HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。 该类实现了Map接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许一条记录的键为null,不支持线程同步。 |
| 12 | TreeMap 继承了AbstractMap,并且使用一颗树。 |
| 13 | WeakHashMap 继承AbstractMap类,使用弱密钥的哈希表。 |
| 14 | LinkedHashMap 继承于HashMap,使用元素的自然顺序对元素进行排序. |
| 15 | IdentityHashMap 继承AbstractMap类,比较文档时使用引用相等。 |
除了以Map结尾的类之外,其他类都实现了Collection接口;以Map结尾的类则实现了Map接口。
2.1链表
数组以及动态的ArrarList的缺陷很明显,就是删除数组中的一个元素开销比较大,因为被删除元素之后的元素都需要向前挪动一位。为解决这一情况,引入了链表这一数据结构(LinkedList类)。且在Java中,所有链表都是双向链接的。

2.2ListIterator接口
List< E >接口中有一个listIterator方法,可以返回一个列表迭代器,用来访问列表中的元素。
void | add(E e) 将指定的元素插入列表(可选操作)。 |
|---|---|
boolean | hasNext() 返回 true如果遍历正向列表,列表迭代器有多个元素。 |
boolean | hasPrevious() 返回 true如果遍历反向列表,列表迭代器有多个元素。 |
E | next() 返回列表中的下一个元素,并且前进光标位置。 |
int | nextIndex() 返回随后调用 next()返回的元素的索引。 |
E | previous() 返回列表中的上一个元素,并向后移动光标位置。 |
int | previousIndex() 返回由后续调用 previous()返回的元素的索引。 |
void | remove() 从列表中删除由 next()或 previous()返回的最后一个元素(可选操作)。 |
void | set(E e) 用 指定的元素替换由 next()或 previous()返回的最后一个元素(可选操作)。 |
2.3数组列表
ArrayList类不同于链表,该数据结构便于随机地访问每个元素,该类也实现了List接口。ArrayList封装了一个动态再分配的对象数组。
动态再分配
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
2.4散列集(Set)
为什么引入散列集:
数组和链表虽然可以查找指定元素,但是我们必须要知道指定元素的位置,否则就需要访问所有的元素,直到找到为止。而散列表可以用于快速地查找对象。
散列表为每个对象计算一个整数,称为散列码。又因为每个整数对应一个散列码,所以Set集合中的元素是唯一的。
散列表的实现

java.util.HashSet< E >
boolean | add(E e) 将指定的元素添加到此集合(如果尚未存在)。 |
|---|---|
void | clear() 从此集合中删除所有元素。 |
Object | clone() 返回此 HashSet实例的浅层副本:元素本身不被克隆。 |
boolean | contains(Object o) 如果此集合包含指定的元素,则返回 true 。 |
boolean | isEmpty() 如果此集合不包含元素,则返回 true 。 |
Iterator<E> | iterator() 返回此集合中元素的迭代器。 |
boolean | remove(Object o) 如果存在,则从该集合中删除指定的元素。 |
int | size() 返回此集合中的元素数(其基数)。 |
Spliterator<E> | spliterator() 在此集合中的元素上创建*late-binding和故障快速* Spliterator 。 |
2.5树集
TreeSet与散列集很相似,最大区别在于树集是一个有序集合。可以对按照任意顺序插入的元素自动排序。当然前提是元素必须实现Comparator接口。
下面以String对象为例,当然对于自定义的实体类,只要实现Comparator接口,也是可以排序的。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
====================================
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
TreeSet的类结构
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
新版本的TreeSet没有实现SortedSet接口。
2.6队列
队列可以高效地再尾部添加元素,头部删除元素;也就是元素满足先进后出。
boolean | add(E e) 将指定的元素插入到此队列中,如果可以立即执行此操作,而不会违反容量限制, true在成功后返回 IllegalStateException如果当前没有可用空间,则抛出IllegalStateException。 |
|---|---|
E | element() 检索,但不删除,这个队列的头。 |
boolean | offer(E e) 如果在不违反容量限制的情况下立即执行,则将指定的元素插入到此队列中。 |
E | peek() 检索但不删除此队列的头,如果此队列为空,则返回 null 。 |
E | poll() 检索并删除此队列的头,如果此队列为空,则返回 null 。 |
E | remove() 检索并删除此队列的头。 |
双端队列
双端队列可以在头部和尾部都添加或删除元素。Java6中引入了Deque接口,ArrayDeque和LinkedList类都实现了这个接口。
优先队列
优先队列中的元素可以按照任意的顺序插入,但会按照有序的顺序进行检索。即任意时刻调用remove()方法都会获得当前优先队列中的最小元素。因此优先队列常用于任务调度。
3.映射
在集合中,我们要查找一个元素,需要有所要查询的那个元素的准确副本。但是通常情况下,我们都是只知道元素的某些信息,希望获取与之关联的元素。而映射这种数据结构就是为此设计的。
映射用于存放键/值对,提供了键,就能获取值。
3.1基本映射操作
Java类库中为映射提供了两个实现:HashMap与TreeMap,两者都实现了Map接口。
散列映射对键进行散列,而树映射是根据键的顺序将元素组织为一个搜索树,方便按照有序的顺序访问键。
| V get(Object key) | 获取与键关联的值 |
|---|---|
| default V getOrDefault(Object key, V defaultValue) | 返回到指定键所映射的值,或 defaultValue如果此映射包含该键的映射。 |
| V put(K key, V value) | 将指定的值与该映射中的指定键相关联(可选操作)。 |
| default void forEach(BiConsumer<? super K,? super V> action) | 对此映射中的每个条目执行给定的操作,直到所有条目都被处理或操作引发异常。 |
| boolean containsKey(Object key) | 如果此映射包含指定键的映射,则返回 true 。 |
3.2映射视图
映射的视图有三种,键集、值集、键值对集。获取分别对应下面的三种方法。
| Set< K > ketSet() | 获取键集 |
|---|---|
| Collection< V > values() | 获取值集 |
| Set< Map,Entry > entrySet() | 获取键值对集 |
1169

被折叠的 条评论
为什么被折叠?



