Java Collection 集合

Java Collection 学习

定义:Java 作为面向对象语言,对象的操作必比然是重中之重。要操作一个对象容易,如果需要存储多个对象,则需要一个容器,存储多个对象可以使用数组,但是数组的长度是不可变的。所以有了集合的概念。Collection 集合,就是为了方便操作处理对象而诞生的。

Collection :来源与 Java.util 包,先看看 Collection 的全家福类图

Collection

Collection 可以主要分为 set、List 、Queue 三种类型。这里 Map 是不属于 Collection 的,Map 是一个独立的数据结构。但是 Collention 又和 Map 的实现上又户型依赖。先说说 Collection 。

Collection 是一个的接口,是高度抽象出来的集合,包含了集合的基本操作和属性

红色框是Collection的基本方法(removeif 是1.8 出现的方法),蓝色框是 JDK 8后添加的新方法。

一、Collection 基本方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Collection 基本方法:
1、添加方法
boolean add(Object obj) : 添加一个对象
boolean addAll(Collection c) : 添加一个集合的对象
2、删除方法
void clear() 移除所有对象
boolean remove(Object) 移除一个对象
boolean removeAll(Collection c) 移除一个集合的对象,只要有一个对象移除了,就返回true
3、判断方法
boolean contains(Object o) 判断集合是否包含该对象
boolean containsAll(Collection c) 判断集合中是否包含指定的集合对象,只有包含所有的对象,才返回 true
boolean isEmpty() 判断集合是否为空。
4、获取方法
Iterator<E> iterator() 迭代器
5、长度功能
int size() 对象个数
6.交集功能
boolean retainAll(Collection c) 移除此 Collection 中未包含在指定Collection 中的所有对象,简单说就是,集合 1 和集合 2 进行对比,最终结果保存在集合 1 ,返回值表示的是 A是否发生变化。

Java 8 新方法:
boolean removeif(Predicate filter) 按照一定规则过滤集合中的对象。Predicate 用于判断对象是否符合某个条件,例:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("123");
list.add("12");
list.add("1");

list.removeIf(s -> s.equals("123"));
list.forEach(s -> System.out.println(s));
}

二、迭代器(Iterator)

在说迭代器之前先说说 Collection 继承的 Iterable 这个接口

解释一下这个 Iterator 是啥

  • 如果一个集合对象要表明自己支持迭代,可以使用foreach语句的特权,就必须实现Iterable接口,表明这个集合对象是可迭代的!然而实现Iterable接口,就必需为foreach语句提供一个迭代器来进行 foreach 操作。

    也就是 迭代器(Iterator),所以 Iterable 接口要有 Iterator() 这个方法返回一个实现了 Iterator 迭代器接口的对象。

那为什么要用这个接口而不直接实现 Iterator 接口呢?我们带着疑问去看看!

另外提一下 spliterator 方法,返回一个 spliterator 接口。叫做可分割迭代器(splitable iterator),Spliterator就是为了并行遍历元素而设计的一个迭代器,jdk1.8中的集合框架中的数据结构都默认实现了spliterator。这里只做一个简单的介绍。

带着疑问首先来看看 List 接口的 源代码

可以看到 List 接口也有 Iterator 方法,下面我们看看 List 集合的实现类ArrayList

看到这里,可以解释为什么 Collection 不直接实现 Iterator 接口了:

  • List 集合族 与 Set 集合族 ,都是实现了 Iterable 接口 ,但都不是直接实现 Iterator 接口,为什么要这样做?

    • 因为在 Iterator 接口的核心方法 next()或者 hasNext ()都是依赖于迭代器当前的迭代的位置。

    • 如果 Collection 实现了 Iterator 接口,肯定会导致集合对象中包含当前迭代位置的数据(指针)。

    • 当集合在不同方法间被传递的时候,由于当前迭代的位置是不可以预置。那么 next 方法的结果会变成不可预知。
  • 这样看使用 Iterable 接口就不会有这样问题,每一次调用都是返回一个新的迭代器。迭代器之间是互不干扰的。

看上图 ArrayList 的 Iterator()方法就很直观了。

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("123");
list.add("12");
list.add("1");
Iterator<String> it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}

当我们使用迭代器时, 就是调用 list 实现的具体的 iterator 方法,每次使用迭代器的都是一个新的迭代器。

三、List 集合

  • List 接口的特点:有序集合,可以精确控制列表中每个元素的位置插入。 用户可以通过整数索引访问元素,并搜索列表中的元素。简单来说就是 顺序存储,顺序取出。可以重复。

  • List 还对 Iterator 作了实现 , ListIterator 接口。

ListIterator 接口比 Iterator 要多了几个方法。

  • hasPrevious() 和 previous() 方法都可以实现逆向遍历。
  • add()和 set()方法可以向List 中添加和修改对象。

List 集合的常用子类

  • ArrayList
    • ArrayList 是基于数组的, ArrayList 是无序的,它按照添加的先后顺序排列。如果要对 ArrayList 进行排序,可以调用它的 sort()方法。并提供一个 Comparator 比较器。
    • ArrayList 是线程不安全的。
  • LinkedList
    • LinkedList 是基于链表的,它是一个双向链表,每个节点维护了一个 prev 和 next 指针。同时这个链表维护了 first 和 last指针,分别指向第一个元素和最后一个元素。
    • 它也是一个无序列表,也是按照插入的先后顺序排序。
    • LInkedList 对与快速访问元素不如 ArrayList 快,因为 ArrayList 访问元素是随机的。
    • LinkedList 也是线程不安全的。
  • Vector
    • Vector 和 ArrayList 非常类似,但Vector 是同步的。
    • 由 Vector 创建的 Iterator ,虽然和 ArrayList 创建的 Iterator 是同一个接口,但是由于其同步的特性,当Vector 创建一个 Iterator 并使用时,另一个线程改变了 Vector 的状态,比如说添加或者删除了一些元素, 这时再调用 Iterator的方法则会抛出异常。
    • Stack 类继承与 Vector 类,是一个堆栈,Stack 提供的5个额外的方法 使 vector 可以当做堆栈使用。

四、Set 集合

set

Set 集合的特点

  • 无序,不可重复。

Set 集合中常用的子类

  • HashSet
    • 底层数据结构是哈希表(一个元素为链表的数组)
    • HashSet 是基于 HashMap 实现的,有点像是对 HashMap 做了封装,而且只是用了 HashMap 的 key 来实现其特性。
    • HashSet 不允许重复,如果出现重复就覆盖,允许为空。
    • HashSet 是线程不安全的。
  • TreeSet
    • 底层数据结构是红黑树 (一个自平衡的二叉树)
    • TreeSet 是一个有序的集合,它的作用是提供有序的Set集合。
    • TreeSet的性能比HashSet差但是我们在需要排序的时候可以用TreeSet因为他是自然排序也就是升序
  • LinkedHashSet
    • 底层数据结构是由哈希表和链表组成。
    • LinkedHashSet 是不重复的,按插入的顺序排序。
    • LinkedHashSet 是线程不安全的。

五、Map 集合

为什么 Map 不继承 Collection?

首先 Map 提供的是键值对映射,而 Collection 是集合接口,提供的是一组数据。如果 Map 继承了 Collection 接口,那么所有 Map 的实现类到底是使用键值对还是使用 Collection 来存放数据呢?所以既然是两种数据结构,那也就没有必要去继承 Collection接口了,同时还不违反接口隔离原则。假如有实现类需要使用的 Collection 接口,也不该是 Map 去继承 Collection 接口。

六、总结

了解了以上的这些集合后,实际工作功能该如何选择呢?

  • 如果涉及到堆栈,队列等操作,应该考虑使用 List ,对于需要快速插入,删除元素,应该使用 LinkedList 。需要快速随机访问元素,则使用 ArrayList。

  • 如果程序在单线程环境中,或者访问仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程则考虑 同步 的类。

  • 要注意对哈希表的操作。作为 key 的对象要正确复写 equals 和 hashCode 方法