Collections(二)——集合接口

Java集合框架在1.2版本引入了接口和实现分离的设计,允许开发者独立于具体实现来使用集合类。接口如Queue定义了基本操作,而实现如CircularArrayQueue和LinkedListQueue提供了不同的效率和特性。迭代器接口使得遍历集合变得简单,而泛型工具方法如contains增强了代码的可读性和复用性。这种设计提高了代码的灵活性,允许在不修改大量代码的情况下切换实现。
摘要由CSDN通过智能技术生成

集合接口

Java的早期版本只发布了一些简单的集合类接口:Vector, Stack, Hashtable, BitSet和Enumerate接口,提供几种元素的访问方法。这是一个聪明的决定——编写合适的容器类显然需要很长时间的思考。
在Java 1.2发布时,设计者感觉发布稍微成熟的数据接口的时间到了。他们面临一些相反的选择,想要设计一种简单易学的接口。他们想要C++中STL类的泛型算法,但是又不想要STL中的复杂度。在这一章,我们考虑Java的一些设计,并解释为什么要这样设计。

将接口和实现分离

作为现代数据结构库的通用做法,Java集合库将接口和实现分离。我们使用队列数据接口解释。
队列接口指定你可以在队列的结尾添加元素,在开头删除元素,你也可以知道当前队列中有多少数据。当你的数据需要“先进先出”逻辑时,你需要使用队列。
最简单的队列接口可以写为

interface Queue<E> // 标准库中队列接口的简化实现
{
	void add(E element);
	E remove();
	int size();
}

这个接口没有告诉你任何关于队列实现的信息,目前主流实现包括两种,循环数列和链表。他们的实现可以分别表示为

class CircularArrayQueue<E> implements Queue<E> // 不是标准库的实现
{
	CircularArrayQueue(int capacity) {...}
	public void add(E element) {...}
	public E remove() {...}
	public int size() {...}

	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标准库中没有上面两个类的实现,如果你想使用循环数组实现的队列,使用ArrayDeque,如果你要使用链表的实现方法,直接使用LinkedList。
当你使用队列时,如果队列已经实现了,那你不需要考虑他的实现。只有在创建队列时,你才需要考虑具体实现。通常,你使用接口类型保存集合类引用。

Queue<Customer> expressLane = new CircularArrayQueue<Customer>(100);
expressLane.add(new Customer("Harry"));

这样,你就可以随时改变你的实现,而不用大规模修改代码。接口并没有说明队列实现的效率,通常,循环数组队列的效率高一点,但是,他的大小很难改变。如果你无法决定队列中元素个数的上限,你最好使用链表队列。
当你读API文档时,你会发现还有一些类以Abstract开头,比如AbstractQueue。这些类指向库实现。如果你要实现自己的类,你会发现集成自AbstractQueue类更简单,而不是继承自Queue接口。

集合接口和迭代器接口

Java中集合类的基本接口是Collection 接口。这个接口有两个基本方法。

public interface Collection<E>
{
	boolean add(E element);
	Iterator<E> iterator();
}

还有一些其他的方法,我们稍后讨论。
add方法想集合中添加一个元素。如果该操作真的改变了集合,那么返回true,否则返回false。比如,如果你向Set类中添加一个元素,而这个元素已经存在,那么就会返回false。
iterator方法返回该集合的迭代器。你可以使用这个迭代器一个一个地访问元素。

迭代器

迭代器(Iterator)接口有三个方法。

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

重复调用next函数,你可以一个接一个地访问集合类中的元素。但是,如果迭代器中没有元素,那么next方法返回NoSuchElementException。所以,在调用next方法之前,你需要先调用hasNext,如果集合中还有元素,他会返回true。如果你要访问集合中所有元素,你需要在hasNext返回true时,调用next。

Collection<String> c = ...;
Iterator<String> ite = c.iterator();
while(ite.hasNext()){
	String element = iter.next();
	dosomethingelse;
}

Java SE5.0 之后,你可以使用for循环逐个读取数据。

for(String element: c){
	dosomethingwithelement;
}

编译器会这段语句转换为迭代器的循环。
上面的for循环可以用于所有实现了Iterator接口的类,接口只有一个方法

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

Collection接口继承自Iterator接口。所以,你可以对所有Collection类使用for循环。
遍历的顺序取决于集合类型。如果你迭代ArrayList,那么会从下标从0开始到最后一个元素。但是,如果你迭代HashSet,那么你没有办法知道迭代顺序,但你可以确定,你会遍历到每个元素。遍历顺序在大多数情况下都不是问题。

移除元素

Iterator接口的remove方法会移除上一次调用next得到的元素。在多数情况下,这是合理的,在你删除元素之前,你需要查看一下它。比如,下面的代码移除第一个元素。

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

如果你在不允许的地方调用remove,那么就会抛出IllegalStateException。
如果你想移除两个相邻的元素,那么下面的代码是错误的。

it.remove();
it.remove();	//错误

你必须使用下面的代码。

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

泛型工具方法

由于Collection和Iterator接口都是泛型的,因此你可以写一些这两个接口的工具类。比如,下面的方法可以判断一个集合中是否包含一个给定的元素。

public static <E> boolean contains(Collection<E> c,Object obj){
	for(E element: c){
		if(element.equals(obj))
			return true;
	}
	return false;
}

Java设计者认为有些方法非常常用,因此应该包含在标准库中,在标准库中,也包含contains函数。
事实上,还有一些其他的接口。

int size();
boolean isEmpty();
boolean contains(Object obj);
boolean containsAll(Collection<?> c);
boolean equals(Object other);
boolean addAll(Collection<? extends E> from);
boolean remove(Object obj);
boolean removeAll(Collection<?> c);
void clear();
boolean retainAll(Collection<?> c);
Object[] toArray();
<T> T[] toArray(T[] arrayToFill);

多数方法的功能跟名字一样。具体用法可查看API文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值