Java Collection API中的表

本文深入探讨Java中的Collection API,包括Collection接口的主要方法及其用途、Iterator接口的功能与使用场景,同时对比了ArrayList和LinkedList两种List接口的实现方式及其各自的优势与局限。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在类库中,Java语言包含一些普通数据结构的实现,这部分通常被称作Collection API,表ADT是在Collection API中实现的数据结构之一。

Collection接口

Collection API位于java.util包中,集合(collection)的概念在Collection接口中得到抽象,它存储一些类型相同的对象。以下代码显示该接口主要的部分。

public interface Collection<AnyType> extends Iterable<AnyType>{
   int size();
   boolean isEmpty();
   void Clear();
   boolean contains(AnyType x);
   boolean add(AnyType x);
   java.util.Iterator<AnyType> iterator();
}

1. size返回集合中的项数;
2. isEmpty当且仅当集合的大小为0时返回true;
3. 如果x在集合中,则contain返回true,该接口并不规定集合如何决定x是否属于该集合,而是要由实现该Collection接口的具体的类来决定;
4. add和remove从集合中添加和删除x,如果操作成功则返回true,如果因某个(非异常)原因失败则返回false,如删除项不在集合中,则remove可能失败,而如果特定集合不允许重复,当企图插入重复项时,add操作就会失败;
5. Collection接口扩展了Iterable接口。该接口在java.lang包里:

public interface Iterable<T> {}


这里写图片描述

实现Iterable接口的类可以拥有增强的for循环,该循环施于这些类之上以观察它们所有的项。

这里写图片描述

public static <AnyType> void print(Collection<AnyType>coll){
   for(AnyType item:coll)
      System.out.println(item);
}


这段代码可以用来打印任意集合中的所有项,当coll具有类型AnyType[]时相应的实现也一样,它们逐个字符都是一样的。

Iterator接口

实现Iterable接口的集合必须提供一个称为iterator的方法,该方法返回一个Iterator类型的对象。

这里写图片描述
该Iterator是一个在java.util包中定义的接口

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

Iterator接口的思路是,通过iterator方法,每个集合均可创建并返回给客户一个实现Iterator接口的对象,并将当前位置的概念在对象内部存储下来。

每次对next调用都给出集合的(尚未见到的)下一项。如第一次调用next给出第一次项,第二次调用给出第二项等等。hasNext用来告诉是否存在下一项。当编译器见到一个正在用于Iterable的对象的增强的for循环时,它用对iterator方法的那些调用代替增强的for循环以得到一个Iterator对象,然后调用next和hasNext。因此,上面的print例程有编译器重写,代码如下

public static <AnyType> void print(Collection<AnyType> coll){
   Ierator<AnyType> itr = coll.iterator();
   while(itr.hasNext()){
      AnyType item = itr.next();
      System.out.print(item);
   }
}


这是通过编译器使用一个迭代器改写的Iterable类型上的增强的for循环。由于Iterator接口中的现有方法有限,因此,很难使用Iterator做简单变量Collection以外的任何工作。Iterator集合还包含一个方法,叫做remove。该方法可以删除由next最新返回的项(此后,我们不再调用remove,直到对next再一次调用以后)。Collection接口也有remove方法,但使用Iterator的remove方法可能有更多的有点。

Iterator的remove方法的有点在于,Collection的remove方法必须首先找出要被删除的项。如果直到所要删除的项的准确位置,删除它的开销可能要小得多。

直接使用Iterator(而不是通过一个增强的for循环间接使用)时,当对正在被迭代的集合进行结构上的改变(即对集合使用add
、remove、clear方法等),该迭代器就不再合法。因为迭代器可能准备给出某一项作为下一项(nextitem)而该项此后或者被删除,或者一个新的项正好插入该项的前面。这意味着,只有需要立即使用一个迭代器的时候,我们才应该获取迭代器。然而,如果迭代器调用了自己的remove方法,那么该迭代器就仍然合法。这是我们有时候更愿意使用迭代器的remove的另一个原因。

List接口、ArrayList和LinkList类

List接口在java.util包中,它继承了Collection接口,

public interface List<E> extends Collection<E> {
   ...
}

因此它包含Collection接口里所有的方法,还另外有一些主要方法

这里写图片描述



List ADT有两种流行的实现方式。ArrayList类提供了List ADT的一种可增长数组的实现,

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
...
}

使用ArrayList的优点是对get和set的调用花费常数时间,缺点在于新项的插入和现有项的删除代价昂贵,除非变动是在ArrayList的末端进行。

LinkList提供了List ADT的双链表实现。其优点是新项插入和现有项的删除均开销很小,这里假设变动项的位置是已知的。在表的前端进行添加和删除都是常数时间的操作,LinkList提供了方法addFirst和removeFirst、addLast和removeLast、以及getFisrt和getLast等有效地添加、删除和访问表两端的项。使用LinkList的缺点是不容易做索引,所以对get的调用时昂贵的,除非调用非常接近表的端点(如果对get的调用是对接近表后部的项进行,那么索引的进行可以从表的后部开始)。

下面我们考察对一个List进行操作的某些方法。首先,设我们通过末端添加一项来构造一个List。

public static void makeList1(List<Integer>lst,int N){
   lst.clear();
   for(int i=0;i<N;i++)
      lst.add();
}


这里ArrayList或LinkList作为参数被传递时,makeList的运行时间都是O(N),因为add的每次调用都是在表的末端进行从而均花费常数时间(可以忽略对ArrayList偶尔进行的扩展)。如果我们通过表的前端添加一些项来构造一个List:

public static void makeList2(List<Integer>lst,int N){
   lst.clear();
   for(int i=0;i<N;i++)
      lst.add(0,i);
}


此时LinkList的运行时间是O(N),但是ArrayList的运行时间是O(N*N),因为对ArrayList的前端进行添加是一个O(N)操作。下面的例程是计算List中的数的和:

public static int sum(List<Integer>lst){
   int total=0;
   for(int i=0;i<N;i++)
      total += lst.get(i);
   return total;
}


这里ArrayList的运行时间是O(N),但对于LInkLIst来说,其运行时间是O(N*N),因为在LinkList中,对get方法的调用为O(N)操作。当使用一个增强的for循环时,则对于任的List的运行时间都是O(N),因为迭代器能有效地从一项到下一项推进。

对搜索而言,ArrayList和LinkList都是低效的,对于Collection的contains和remove方法(它们都是AnyType)的调用均花费线性时间。

在ArrayList中有一个容量的概念,用来表示数组的大小。需要时,ArrayList将自动增加其容量保证它至少具有表的大小。如果该大小的早起估计存在,则ensureCapacity可以设置容量为一个足够大的量避免数组容量以后的扩展。还有trimTosize可以在所有的ArrayList添加操作完成以后使用以避免浪费空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值