Java容器类基础

Java容器类基础

       Java实用类库提供了一套相当完善的容器类,基本类型为List,Set,Map,Queue。它们都有一些特性,例如Set对于每一个值都只保存一个对象,Map允许你将对象和对象关联起来。此外,Java容器类都可以自动调节尺寸。因此,与数组不同,你可以放置任意数量的对象到容器中而不用担心容器应该设置为多大。

       Java容器类有4个接口,它们分别上面提到过的List,Set,Map,Queue;在理想情况下,你编写的代码大多数情况是在和这些接口打交道,并且你唯一需要指定所要使用精确类型的地方就是在创建的时候,例如你可以创建一个list:

List<Apple> list=new ArrayList<Apple>();

这里使用接口的目的在于当你需要修改你的实现时,仅需要在创建的地方修改它即可,因此可以像下面这样进行修改:

List<Apple> list=new LinkedList<Apple>();

因此,你应该创建一个具体的类的对象,然后将其转换成对应的接口,然后再其余的代码中都使用这个接口。但这个方法并非总能奏效,因为某些类具有额外的功能。例如LinkedList具有List接口中未包含的额外方法,因此如果你需要使用这些方法,你就不能将其转换成更加通用的接口。

在这里,我们将对Java容器类的基本知识及典型用法进行重点介绍。

1,基本概念

Java容器类库的用途是保存对象,并将其划分为两个不同的概念:

1)Collection:一个独立元素的序列。list必须按照插入的顺序保存元素,set不能有重复的元素,queue按照排队规则来确定元素的顺序。

2)Map:一组成对的“键值对”对象,允许你用键来查找值。ArrayList允许你用数字来查找值,从某种意义上来说,它将数字与对象关联在一起。Map允许我们用一个对象查找另一个对象,又称其为关联数组,应为它将某些对象和另外一些对象关联在了一起。

<span style="font-size:18px;">import java.util.*;

public class PrintingContainers{
	static Collection fill(Collection<String> collection){
		collection.add("rat");
		collection.add("cat");
		collection.add("dog");
		collection.add("dog");
		return collection;
	}
	static Map fill(Map<String,String> map){
		map.put("rat","123");
		map.put("cat","456");
		map.put("dog","789");
		map.put("dog","268");
		return map;
	}
	public static void main(String[] args){
		System.out.println(fill(new ArrayList<String>()));
		System.out.println(fill(new LinkedList<String>()));
		System.out.println(fill(new HashSet<String>()));
		System.out.println(fill(new TreeSet<String>()));
		System.out.println(fill(new LinkedHashSet<String>()));
		System.out.println(fill(new HashMap<String,String>()));
		System.out.println(fill(new TreeMap<String,String>()));
		System.out.println(fill(new LinkedHashMap<String,String>()));
	}
}
/*output:
[rat, cat, dog, dog]
[rat, cat, dog, dog]
[cat, dog, rat]
[cat, dog, rat]
[rat, cat, dog]
{cat=456, dog=268, rat=123}
{cat=456, dog=268, rat=123}
{rat=123, cat=456, dog=268}
*/
</span>

这里展示了Java容器类库中的两种主要类型,它们的区别在于容器中每个“槽”保存元素的个数。查看输出可以发现,默认的打印行为(使用容器提供的toString方法)即可生成可读性很好地结果。Collection打印出来的内容用方括号括住,每个元素由逗号分隔。Map则用大括号括住,键与值用等号联系。这些容器在这里仅作为展示之用,后面会对其进行更加详细的介绍。

2,常用容器类介绍

1)Collection

下表列出了可以通过Collection执行的所有方法,它们也是Set和List可以执行的操作。

2)List

List接口在Collection基础上添加了大量的方法,使得可以在List的中间插入和移除元素。

有两种类型的List:

·基本的ArrayList,它擅长随机访问元素,但是在List中间插入和删除元素较慢(底层实现:数组)。

·LinkedList,它通过较低的代价在List中间进行插入和删除操作,提供了优化的顺序访问,但在随机访问方面相对较慢(底层实现:双向链表)。

下面列出一些List中常用的方法:

contains():用来确定某个对象是否在列表中

remove():用来移除列表中的对象

indexOf():用来确定对象在列表中的索引编号

subList():用来创建一个列表片段

retainAll():用来进行“交集”操作

set():用来设置指定索引处对象

3)LinkedList

LinkedList一样实现了基本的List接口,还添加了使其可以作为栈,队列以及双端队列的方法。

这些方法中有些彼此之间只是名称上有些差异,或者只存在些许差异,以使得这些名字在特定的上下文中更加适用。例如:

getFirst()和element()完全一样,它们都返回列表的第一个元素,而并不移除它;remove()和removeFirst()也是一样的,它们移除并返回列表的头;add()和addLast()都相同,它们都将元素插入列表的尾端;

4)Set

Set不保存重复的元素。Set最常用的就是测试归属性,你可以很容易的询问某个对象是否在某个Set中。Set具有和Collection完全一样的接口,因此没有任何额外的功能,不像前面有两个不同的List。实际上Set就是Collection,只是行为不同(这是继承和多态的典型应用);


<span style="font-size:18px;">import java.util.*;

public class SetOfOperations{
	public static void main(String[] args){
		Set<String> set1=new HashSet<String>();
		Collections.addAll(set1,"A B C D E F G H I J K L".split(" "));
		set1.add("M");
		System.out.println("H: "+set1.contains("H"));
		System.out.println("N: "+set1.contains("N"));
		Set<String> set2=new HashSet<String>();
		Collections.addAll(set2,"H I J K L".split(" "));
		System.out.println("set2 in set1: "+set1.containsAll(set2));
		set1.remove("H");
		System.out.println(set1);
		System.out.println("set2 in set1: "+set1.containsAll(set2));
		set1.removeAll(set2);
		System.out.println("set2 remove from set1: "+set1);
		Collections.addAll(set1,"X Y Z".split(" "));
		System.out.println("'X Y Z' add to set1: "+set1);
	}
}
/*
output:
H: true
N: false
set2 in set1: true
[D, E, F, G, A, B, C, L, M, I, J, K]
set2 in set1: false
set2 remove from set1: [D, E, F, G, A, B, C, M]
'X Y Z' add to set1: [D, E, F, G, A, B, C, M, Y, X, Z]
*/
</span>

5)Map


将对象映射到其他对象的能力是一种解决编程问题的杀手鐗;

例如下面的例子:

<span style="font-size:18px;">import java.util.*;

public class Statistics{
	public static void main(String[] args){
		Random rand=new Random(47);
		Map<Integer,Integer> m=new HashMap<Integer,Integer>();
		for(int i=0;i<10000;i++){
			int r=rand.nextInt(20);
			Integer feq=m.get(r);
			m.put(r,feq==null?1:feq+1);
		}
		System.out.println(m);
	}
}
/*
output:
{0=481, 1=502, 2=489, 3=508, 4=481, 5=503, 6=519, 7=471, 8=468, 9=549, 10=513, 11=531, 12=521, 13=506, 14=477, 15=497, 17=509, 16=533, 19=464, 18=478}
*/
</span>

6)Queue

队列是一种典型的先进先出的容器,即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列常被当作一种可靠的将对象从程序的某一区域传送到另一区域的途径,在并发编程中特别重要。

LinkedList提供了方法以支持队列行为,并且实现了Queue接口,因此LinkedList可以作为Queue的一种实现。


<span style="font-size:18px;">import java.util.*;

public class QueueDemo{
	public static void printQueue(Queue queue){
		while(queue.peek()!=null){
			System.out.print(queue.remove()+" ");
		}
		System.out.println();
	}
	
	public static void main(String[] args){
		Queue<Integer> queue=new LinkedList<Integer>();
		Random rand=new Random(47);
		for(int i=0;i<10;i++)
			queue.offer(rand.nextInt(i+10));
		printQueue(queue);
		Queue<Character> qc=new LinkedList<Character>();
		for(char c:"HelloWorldJava".toCharArray())
			qc.offer(c);
		printQueue(qc);
	}
}
</span>

下面列出Queue的一些常用方法:

offer():将一个元素插入到队尾

peek()/element():在不删除的情况下返回对头

poll()/remove():移除并返回对头


3,迭代器

迭代器是一个对象,它的工作是遍历选择容器中的对象,它们只是使用容器,不知道或者不关心容器的类型,达到复用代码的目的。此外,迭代器还被称为轻量级对象:创建它的代价小;

迭代器的用法:

1)使用iterator()要求容器返回一个Iterator();

2)使用next()获取容器的中的下一个元素;

3)使用hasNext()检查容器中是否还有元素;

4)使用remove()将迭代器新返回的元素删除;


总结

Java容器简图,常用的容器用黑色粗线框表示,点线框表示接口,实线框表示具体的类;带有空心箭头的点线表示一个特定的类实现了接口,实心箭头表示某个类可以生成箭头所指向类的对象;



下面给出一些容器选择的小建议:

1)如果要进行大量的随机访问,就使用ArrayList;如果经常需要从表中间插入和删除元素,则应该使用LinkedList;

2)HashMap设计用来快速访问;而TreeMap保持键始终处于排序状态,所以没有HashMap快。LinkedHashMap保持元素的插入顺序,但也通过散列提供了快速访问能力。

3)TreeSet不接受重复元素。HashSet提供最快的查询速度,而TreeSet保持元素处于排序状态,LinkedHashSet以插入顺序保存元素。

4)程序中不应该使用过时的Vector,Stack,HashTable;



发布了253 篇原创文章 · 获赞 26 · 访问量 33万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章