Java集合(一):Java集合概述

注:本文基于JDK 1.7

1 概述

Java提供了一个丰富的集合框架,这个集合框架包含了许多接口、虚拟类和实现类。这些接口和类提供了丰富的功能,能够满足基本的聚合需求。下图就是这个框架的整体结构图:

可以看见,这个框架非常大,大到吃惊的地步。这个图的左面部分是接口,右面部分是类,中间的线代表了右面的类实现了左面的哪些接口。比如,AbstractList类实现了List接口,那么继承自AbstractList类的子类都实现了这个接口。还有,如果一个类实现了一个接口,那么这个类也实现了这个接口的所有父接口。比如,TreeSet类实现了Deque接口,那么TreeSet类也实现了Queue接口、Collection接口和Iterable接口(接口Collection扩展了Iterable,这里没显示出来)。

图中红色的类表示抽象类,大部分抽象类都以Abstract开头,除了Dictionary。绿颜色的类表示遗留类,这些类在Java一开始的时候就已经存在了。

2 接口与实现分离

Java集合类库中将接口(interface)与实现(implementation)分离。这里以队列(queue)为例。

队列接口指出可以在队列的尾部添加元素,在队列的头部删除元素,并且可以查找队列中的元素个数。即队列中的元素按照先进先出的元素使用队列。

如果我们设计queue的接口,可能这样设计:

interface Queue<E>
{
    void add(E element);
    E remove();
    int size();
}

这里给出了三个基本的方法。这个接口没有给出队列应该是如何实现的。通常,队列的实现方式有两种:一个是使用循环数组,一个是使用链表。

使用循环数组时需要指出对头head和队尾tail;使用链表时也需要给出头和尾:

class CircularArrayQueue<E> implements Queue<E>
{
    CircularArrayQueue(int capacity){...}
    public void add(E element){...}
    public E remove(){...}
    private E[] elements;
    private int head;
    private int tail;
}

class LinkedListQueue<E> implements Queue<E>
{
    LinkedListQueue(){...}
    public void add(E element){...}
    public E remove(){...}
    public int size(){...}
    private Link head;
    private Link tail;
}

上面的只是我们自己实现的简单的队列,Java类库中没有这两个类。当需要使用队列时,可以使用LinkedList类,这个类实现了Queue接口。

当在程序中使用队列时,一旦构建了集合就不需要知道究竟使用了哪种实现。因此,只有在构建集合对象时,使用具体的类才有意义。可以使用接口类型存放集合的引用:

Queue<Employee> employee=new CircularArrayQueue<>(100);
employee.add(new Employee("Harry"));

利用这种方式,一旦改变了想法,可以轻松地使用另一种不同的实现。只需要对程序的一个地方做出修改,即调用构造器的地方。如果觉得LinkedListQueue是个更好的选择,就可以这样修改:

Queue<Employee> employee=new LinkedListQueue<>();
employee.add(new Employee("Harry"));

接口本身没有给出具体的实现方式,因此也不能给出哪种实现更符合实际应用。这样,选择哪种实现方式就需要由程序员选择。Java类库中实现了很多的类,每个类都有各自的特性以及适用场景,也许选择了某个实现方式在某个特性上更加优秀,但也可能在另一个特性上付出了代价。如何平衡好各个性能的代价,需要类库使用者自己把握。

在上图中,我们可以发现很多红色的以Abstract开头的类,这些类都是抽象类,这些类是为类库的实现者设计的,这些类中实现了一些基本的方法。如果想要实现自己的队列类,你会发现扩展AbstractQueue类比实现Queue接口更方便。

3 Java类库中的集合接口和迭代器接口

在Java类库中,集合类的基本接口是Collection接口。这个接口中有如下两个方法:

boolean add(E element);
Iterator<E> iterator();

当然,除了这两个方法外还有其它的方法,会在后面介绍。

add方法用于向集合中添加元素,如果添加元素确实改变了集合就返回true,如果集合没有发生变化就返回false。

iterator方法用于返回一个实现了Iterator接口的对象。可以使用这个迭代器对象依次访问集合中的元素。

下面来介绍一下Java类库中重要的迭代器。

Iterator迭代器接口有三个方法:

public interface Iterator<E>
{
    E next();
    boolean hasNext();
    void remove();
}

通过反复调用next方法,可以逐个访问集合中的每个元素。但是如果达到了集合的末尾,next方法将抛出一个NoSuchElementException异常。因此,在调用next方法之前应该调用hasNext方法。如果迭代器对象还有多个供访问的元素,这个方法就返回true。因此,可以使用下面的方法访问集合中的所有元素:

Collection<String> c=...;
Iterator<String> it=c.iterator();
while(it.hasNext())
{
    String element=it.next();
    do something with element
}

从Jave SE 5.0起,还可以使用for each循环访问集合中的所有元素:

for(String element : c)
{
    do something with element
}

编译器只是将这个for each循环翻译为带有迭代器的循环。

for each循环可以与任何实现了Iterable接口的对象一起工作,这个接口只包含一个方法:

public interface Iterable<E>
{
    Iterator<E> iterator();
}

Collection接口扩展了Iterable接口,所以对于标准库中的任何集合都可以使用for each循环。

需要注意的是,元素被访问的顺序取决于集合的类型。如果对ArrayList进行迭代,迭代器将会从索引0开始,每迭代一次,索引值加1。然而,如果访问HashSet中的元素,每个元素将会按照某种随机的次序出现。虽然可以确定在迭代过程中能够遍历所有的元素,但却无法预知元素被访问的次序。

Java集合类库中的迭代器与其它类库中的迭代器在概念上有着重要的区别。C++的迭代器是根据数组索引建模的。如果给定这样一个迭代器,就可以查看指定位置上的元素,就像知道数组索引i就可以查看数组元素a[i]一样。不需要查找元素,就可以将迭代器向前移动一个位置。这与不需要执行查找操作就可以通过i++将数组索引向前移动一样。但是,Java迭代器不是这样操作的。查找操作和位置变更是紧密相连的。查找一个元素的唯一方法是调用next,而在执行查找操作的同时,迭代器的位置随之向前移动。

即,可以将迭代器看做一个位置,这个位置在两个元素之间。而next操作会改变这个位置。但调用next时,迭代器就越过下一个元素,并返回刚刚越过的那个元素的引用。

Iterator接口的remove方法用于删除上次调用next方法返回时的元素。也就是说,remove操作和next操作具有依赖性,如果没有调用next方法而调用remove方法是非法的,会抛出一个IllegalStateException异常。下面是删除集合中的第一个元素:

Iterator<String> it=c.iterator();
it.next();
it.remove();

如果删除两个相邻的元素,下面的方法是错误的:

it.remove();
it.remove();

必须先调用next方法越过待删除的元素:

it.remove();
it.next();
it.remove();

有一个不怎么恰当的比喻,可以将迭代器看做光标:

Collection<String> c=ArrayList<>();
c.add("a");
c.add("b");
c.add("c");
Iterator<String> it=c.iterator();

这时,迭代器的位置如下:

| a b c

当调用next方法,光标就会后移,然后返回刚才越过的元素:

it.next();

此时,迭代器的位置如下:

a | b c

并返回元素a。

如果要删除元素,删除的行为就像后退键(Backspace)一样,删除光标后面(以右为前)的元素:

it.remove();

此时,迭代器的位置如下:

| b c

和后退键不同的是,如果迭代器的后面即使还有元素,没有调用next方法也不能删除。

由于Collection接口和Iterator接口都是泛型接口,可以编写操作任何集合类型的实用方法。其实,Collection接口中有很多方法:

public interface java.util.Collection<E> extends java.lang.Iterable<E> {
  public abstract int size();
  public abstract boolean isEmpty();
  public abstract boolean contains(java.lang.Object);
  public abstract java.util.Iterator<E> iterator();
  public abstract java.lang.Object[] toArray();
  public abstract <T> T[] toArray(T[]);
  public abstract boolean add(E);
  public abstract boolean remove(java.lang.Object);
  public abstract boolean containsAll(java.util.Collection<?>);
  public abstract boolean addAll(java.util.Collection<? extends E>);
  public abstract boolean removeAll(java.util.Collection<?>);
  public boolean removeIf(java.util.function.Predicate<? super E>);
  public abstract boolean retainAll(java.util.Collection<?>);
  public abstract void clear();
  public abstract boolean equals(java.lang.Object);
  public abstract int hashCode();
  public java.util.Spliterator<E> spliterator();
  public java.util.stream.Stream<E> stream();
  public java.util.stream.Stream<E> parallelStream();
}

有很多的方法的含义都很明确,这里不做过多的解释。

当然,如果实现Collection接口的每一个类都要实现这些例行方法将是一件很烦人的事。为了能够让实现者更容易地实现这个接口,Java类库提供了一个AbstractCollection类,在这个类里提供了一些例行方法,这样,一个具体的集合类就可以扩展AbstractCollection类而不需要实现所有的例行方法了,并可以覆盖里面的方法。

4 Java类库中的接口

下图给出了Java类库中的所有接口:

其中,Collection和Map是集合框架的两个主要接口,所有的集合类都实现了这两个接口中的一个。Iterator接口和ListIterator接口是迭代器接口,而ListIterator接口提供了更丰富的操作,这个接口会在List列表中介绍。RandomAccess接口是一个标签接口,也就是说这个接口没有任何方法,但是可以用这个接口标注某个类,然后检查一个类是否支持随机访问。

在随后的介绍中,会详细介绍这些接口的方法和使用。

5 Java类库中的类

下面是Java类库中的所有实现类:

其中红颜色的是抽象类。可以明显的分为两个部分,一个集合(Collection),一个映射(Map)。

这些是常用的类,再加上一些专用的类,比如:EnumSet、LinkedHashSet、EnumMap、LinkedHashMap、IdentityHashMap和WeakHashMap,一共14类,它们的特点如下:

Java库中的具体集合
集合类型 描述
ArrayList 一种可以动态增长和伸缩的索引序列
LinkedList 一种可以在任何位置进行高效插入和删除操作的有序序列
ArrayDeque 一种用循环数组实现的双端队列
HashSet 一种没有重复元素的无序集合
TreeSet 一种有序集
EnumSet 一种包含枚举类型值的集
LinkedHashSet 一种可以记住元素插入顺序的集
PriorityQueue 一种允许高效删除最小元素的集合
HashMap 一种存储键/值关联的数据结构
TreeMap 一种键值有序排列的映射表
EnumMap 一种键值属于枚举类型的映射表
LinkedHashMap 一种可以记住键值项添加次序的映射表
WeakHashMap 一种其值无用武之地后可以被垃圾回收器回收的映射表
IdentityHashMap 一种使用==而不是equals比较键值的映射表

对于有序无序、元素可重复和元素不可重复,特点总结如下,注意,对于Map,考察的是键的值是否可重复:

6 更多

在后序的分析中,会给出这些具体类和集合的介绍:

2、Java集合(二):List列表

3、Java集合(三):Set集合

4、Java集合(四):Queue队列

5、Java集合(五):专用Set和专用Map

6、Java集合(六):Java集合框架

7、Java集合(七):Java集合中的算法与遗留类

时间: 2024-10-26 19:25:52

Java集合(一):Java集合概述的相关文章

java高级特性之集合概述

java中的集合概述 map 接口 总结 java集合学习 1 java中存储数据的方式 1 数组 (基本数据类型+引用数据类型).2 集合(对象) 数组存储数据的缺点1:数组一旦创建,长度固定 2:数组不能直接确定有效元素的个数 java中的集合概述: java集合接口:Collection 接口 和Map 接口 (Collection接口 表示不按照添加顺序存放对象的集合,而且集合内的元素可以重复即 无序可重复 集合,它是List,Set,Queue..接口的父接口) Collection

java集合框架--Set集合

1.Set集合概述 一个不包含重复元素的集合. 2.代码引入 package com; import java.util.HashSet; import java.util.Set; /**  * Collection  *  List    有序(存储顺序和取出顺序一致),可重复  *  Set  无序(存储顺序和取出顺序不一致),唯一  *  虽然Set集合的元素无序,但是,作为集合来说,它肯定有自己的存储顺序,  *  我们存储元素的时候可能和它内存的存储元素顺序相同.  *   * Ha

Java笔记(15):集合框架(01)

1.对象数组的概述和使用 1 package cn.itcast_01; 2 3 public class Student { 4 // 成员变量 5 private String name; 6 private int age; 7 8 // 构造方法 9 public Student() { 10 super(); 11 } 12 13 public Student(String name, int age) { 14 super(); 15 this.name = name; 16 thi

java基础5:集合

关于Java基础的文章,我觉得写得还可以,以前发在了我其它的博客了,肯定是原创,现在再分享给大家出来. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Java笔记(18):集合框架(04)

1.Map集合概述特点及测试 1 package cn.itcast_01; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 /* 7 * 作为学生来说,是根据学号来区分不同的学生的,那么假设我现在已经知道了学生的学号,我要根据学号去获取学生姓名,请问怎么做呢? 8 * 如果采用前面讲解过的集合,我们只能把学号和学生姓名作为一个对象的成员,然后存储整个对象,将来遍历的时候,判断,获取对应的名称. 9 * 但是呢,如果我都能把学生

java集合框架--Map集合

1.Map集合的概述 Map集合是将键映射到值的对象.一个映射不能包含重复的键.每个键最多只能映射到一个值. 2.Map接口和Collection接口的不同? Map集合存储元素是成对出现的,Collection集合存储元素是单独出现的. Map集合的键是唯一的,值是可重复的. Collection集合的子接口Set是唯一的,List是可重复的. Map集合的数据结构值针对键有效,和值无关,而Collection接口的数据结构是针对元素有效. 3.Map集合示例及功能 package cn; i

Java基础---泛型、集合框架工具类:collections和Arrays

第一讲     泛型(Generic) 一.概述 1.JDK1.5版本以后出现的新特性.用于解决安全问题,是一个类型安全机制. 2.JDK1.5的集合类希望在定义集合时,明确表明你要向集合中装入那种类型的数据,无法加入指定类型以外的数据. 3.泛型是提供给javac编译器使用的可以限定集合中的输入类型说明的集合时,会去掉“类型”信息,使程序运行效率不受影响,对参数化的泛型类型,getClass()方法的返回值和原始类型完全一样. 4.由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就

java基础知识--java集合框架

java集合框架 1.概述: 集合框架被设计成要满足以下几个目标. 该框架必须是高性能的.基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的. 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性. 对一个集合的扩展和适应必须是简单的. 整个集合框架就围绕一组标准接口而设计.你可以直接使用这些接口的标准实现,诸如: LinkedList, HashSet, 和 TreeSet等,除此之外你也可以通过这些接口实现自己的集合. 集合框架是一个用来代表和操纵集合的统一架构.所有的集合

Java笔记(16):集合框架(02)

1.ArrayList存储字符串并遍历 1 package cn.itcast_01; 2 3 import java.util.ArrayList; 4 import java.util.Iterator; 5 6 /* 7 * List的子类特点: 8 * ArrayList: 9 * 底层数据结构是数组,查询快,增删慢 10 * 线程不安全,效率高 11 * Vector: 12 * 底层数据结构是数组,查询快,增删慢 13 * 线程安全,效率低 14 * LinkedList: 15 *

java学习笔记—集合之Map集合

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; text-align: center; font: 12.0px Times } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Songti SC" } p.p3 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Times } p.p4 { margin: 0.0px 0.0px 0.0px 0.0