目录:
- 第十一章 持有对象
- 11.1 泛型和类型安全的容器
- 11.2 基本概念
- 11.3 添加一组元素(Arrays.asList(),Collections.addAll(), Arrays.addAll())
- 11.4 容器的打印
- 11.5 List
- 11.6 迭代器
- 11.7 LinkedList
- 11.8 栈 Stack
- 11.9 Set
- 11.10 Map
- 11.11 Queue 队列
- 11.12 Collection和Iterator
第十一章 持有对象
容器类的基本类型:List
, Set
, Queue
, Map
。这些对象类型也成为集合类,或容器。
Set对于每个对象只保存一次,Map是允许你讲某些对象与其他一些对象关联起来的关联数组,Java容器类都可以自动调整自身的尺寸。
11.1 泛型和类型安全的容器
当需要一个Apple对象的容器,我们使用最基本最可靠的容器ArrayList。ArrayList可以被当做“可以自动扩充自身尺寸的数组”来看待。
使用ArrayList:创建一个实例,用add()插入对象,然后用get()访问这些对象,此时需要使用索引,就像数组一样,但是不需要方括号。ArrayList还有一个size()方法,可以知道有多少元素添加进来了。
class Apple{
private static long counter;
private final long id = counter++;
public long id(){
return id;
}
}
class Orange{}
public class ApplesAndOrangesWithoutGenerics {
public static void main(String[] args) {
ArrayList apples = new ArrayList();
for (int i = 0; i < 3; i++){
apples.add(new Apple());
}
//apples.add(new Orange());
long id;
for(int i = 0; i < apples.size(); i++){
id = ((Apple) apples.get(i)).id();
System.out.println(id);
}
}
}
// Output:0 1 2
// 去掉注解,会报错,提示不能将Orange对象转为Apple对象
ArrayList保存的是一个Object。所以可以将Orange和Apple放入同一个容器,但是在取出的时候,需要先将ArrayList内的Object对象向下转型为原始类型,然后调用方法id()。
为了防止这种ArrayList取值时,发生异常转换的问题,我们可以在创建ArrayList对象时限定其对象格式,即预定义泛型,定义用来保存Apple对象的ArrayList,可以声明:ArrayList<Apple> apples = new ArrayList<>();
,而不仅仅是ArrayList,其中尖括号括起来的是类型参数
(可以有多个),它制定了这个容器实例可以保存的类型。通过使用泛型,可以防止在编译期将错误类型的对象放入到容器中。
public class ApplesAndOrangesWithoutGenerics {
public static void main(String[] args) {
ArrayList<Apple> apples = new ArrayList<Apple>();
for (int i = 0; i < 3; i++){
apples.add(new Apple());
}
// Compire-time error
//apples.add(new Orange());
for(Apple apple : apples){
System.out.println(apple.id());
}
}
}
此刻可以发现:在将元素从List中取出时,类型转换也不再是必须的了。因为List知道它保存的是什么类型,因此它会在调用get()时,替你执行转型。
当你指定了某个类型作为泛型参数时,你并不仅限于只能将确切类型的对象放置到容器中,向上转型也可以像作用于其他类型一样作用于泛型:
class Fuji extends Apple{}
然后在apples里面添加Fuji对象,也是允许的,最后使用foreach语法循环取出方法仍然不变
apples.add(new Fuji());
当一个对象里没有重写toString()方法时,直接打印该对象时,输出的为:Apple@4554617c
程序默认输出的是从Object默认的toString()方法产生的,该方法将打印类名,后面跟随该对象的散列码的无符号十六进制表示
(这个散列码是通过hashCode()方法产生的)。
练习1
:创建一个Gerbil(沙鼠),包含int gerbilNumber,在构造器中初始化它。添加一个方法hop(),用以打印沙鼠的号码以及它正在跳跃的信息。创建一个ArrayList,并向其中添加一串Gerbil对象。使用get()遍历List,并且对每个Gerbil调用hop().
class Gerbil{
private final int gerbilNumber;
Gerbil(int gerbilNumber){
this.gerbilNumber = gerbilNumber;
}
@Override
public String toString() {
return "gerbil " + gerbilNumber;
}
public void hop(){
System.out.println(this + " is hopping!");
}
}
public class E01_Gerbil {
public static void main(String[] args) {
ArrayList<Gerbil> gerbils = new ArrayList<>();
for (int i = 0; i < 10; i++){
gerbils.add(new Gerbil(i));
}
for (Gerbil gerbil: gerbils) {
gerbil.hop();
}
}
}
11.2 基本概念
Java的容器类库的用途是“保存对象”,并将其划分为两个不同的概念:
-
Collection
:一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复的元素。Queue按照排队规则
来确定对象产生的顺序(通常与它们被插入的顺序相同)。 -
Map
: 一组成对的“键值对”对象,允许你使用键来查找值。ArrayList允许你使用数字来查找值,因此某种意义上讲,它将数字与对象关联在了一起。映射表
允许我们使用另一个对象来查找某个对象,它也被称为“关联数组”或者“字典”。
在创建的时候如果你决定去修改你的实现,就需要在创建时修改,如:ArrayList<Apple> apples = new ArrayList<Apple>();
因此,应该创建一个具体的对象,将其转型为对应的接口,然后在其余的代码中都是用这个接口。
但是也有例外,例如,LinkedList
具有在List接口中未包含的额外的方法,而TreeMap
也具有在Map接口中未包含的方法。如果需要使用这些方法,就不能将它们向上转型为通用的接口。
11.3 添加一组元素(Arrays.asList(),Collections.addAll(), Arrays.addAll())
在java.util包中的Arrays和Collections类中都有很多使用的方法,可以在一个Collection中添加一组元素。Arrays.asList()方法可以接受一个数组或者是一个用逗号隔开的元素列表(使用可变参数),并将其转换为一个List对象。Collections.addAll()方法接受一个Collection对象,以及一个数组或是一个用逗号分隔的列表,将元素添加到Collection中。
public class AddingGroups {
public static void main(String[] args) {
Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
Integer[] moreInts = {6,7,8,9,10};
collection.addAll(Arrays.asList(moreInts));
System.out.println(collection); //Output:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Collections.addAll(collection, 11,12,13,14,15);
System.out.println(collection); //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
Collections.addAll(collection, moreInts);
System.out.println(collection); //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 10]
List<Integer> list = Arrays.asList(16,17,18,19,20);
System.out.println(list); //[16, 17, 18, 19, 20]
list.set(1,99); //修改list下标为1的元素的值为99
System.out.println(list); //[16, 99, 18, 19, 20]
//list.add(21); //Runtime error because the underlying array cannot be resized
//使用Arrays.asList创建的list底层是数组,因此不能调整尺寸。
}
}
Collection 的构造器可以接受另一个Collection,用它来将自身初始化,因此可以使用Arrays.asList()来为这个构造器产生输入。但是Collection.addAll()方法运行起来要快得多,而且构建一个不包含元素的Collection,然后调用Collection.addAll()这种方式很方便,因此它是首选方式。
Collection.addAll()成员方法一次只能接受另一个Collection对象作为参数,因此它不如Arrays.asList
或Collections.addAll()
灵活,这两个方法使用的都是可变参数列表。
也可以直接使用Arrays.asList()的输出,将其当做List,但是在这种情况下,其底层表示的是数组,因此不能调整尺寸。如果使用add()或delete()方法在这种列表中添加或者删除元素,就会引发去改变数组的尝试,将会出现运行时的错误:Unsupported Operation(不支持的操作)。
11.4 容器的打印
Java容器类库中的两种主要类型:Collection
和Map
它们的区别在于容器中每个“槽”保存的元素个数。
Collection在每个槽中只能保存一个元素。此类容器包括:List
,它以特定的顺序保存一组元素;Set
,元素不能重复;Queue
,只允许在容器的一端插入对象,并从另一端移除对象。
Map在每个槽内保存了两个对象,即键和与之相关联的值。
ArrayList和LinkedList都是List类型,从输出上看他们都是按照被插入的顺序保存
元素。
HashSet,TreeSet,LinkedHashSet都是Set类型
,每个相同的项都只保存一次
,不同的Set实现存储元素的方式也是不同的,HashSet是最快的获取元素方式
。如果存储顺序很重要,那么可以使用TreeSet,它按照比较结果的升序保存对象;或者使用LinkedHashSet,它按照被添加的顺序保存对象。
Map中的键是唯一的,只接受存储一次。
Map.put(key, value)方法将增加一个值,并将它与某个键关联起来。Map.get(key)方法将产生与这个键相关联的值。
Map:HashMap,TreeMap, LinkedHashMap。与HashSet一样,HashMap也提供了最快的查找技术,也没有按照任何明显的顺序来保存其元素。TreeMap按照比较结果的升序保存键,而LinkedHashMap则按照插入顺序保存键,同事还保存了HashMap的查询速度。
练习4
:创建一个生成类,它可以在每次调用其next()方法时,产生你由你最喜欢的电影(你可以使用Snow white 或者Start Wars)的字符构成的名字(作为String对象)。在字符列表中的电影名字用完后,循环到这个字符列表的开始处。使用这个生成器来填充数组、ArrayList、LinkedList、HashSet、LinkedHashSet和TreeSet,然后打印每一个容器。
class MovieNameGenerator {
String[] characters = {
"Grumpy", "Happy", "Sleepy", "Dopey", "Doc", "Sneezy", "Bashful", "Snow White", "Witch Queen", "Prince"
};
int next;
public String next(){
String r = characters[next];
next = (next + 1) % characters.length;
return r;
}
}
public class E04_MovieNameGenerator {
private static final MovieNameGenerator mng = new MovieNameGenerator();
static String[] fill(String[] array){
for (int i = 0; i < array.length; i++){
array[i] = mng.next();
}
return array;
}
static Collection<String> fill(Collection<String> collection){
for (int i = 0; i < 5; i++){
collection.add(mng.next());
}
return collection;
}
public static void main(String[] args) {
System.out.println(Arrays.toString(fill(new String[5]))); //[Grumpy, Happy, Sleepy, Dopey, Doc]
System.out.println(fill(new ArrayList<>())); //[Sneezy, Bashful, Snow White, Witch Queen, Prince]
System.out.println(fill(new LinkedList<>())); //[Grumpy, Happy, Sleepy, Dopey, Doc]
System.out.println(fill(new HashSet<>())); //[Prince, Sneezy, Snow White, Bashful, Witch Queen]
System.out.println(fill(new LinkedHashSet<>())); //[Grumpy, Happy, Sleepy, Dopey, Doc]
System.out.println(fill(new TreeSet<>())); //[Bashful, Prince, Sneezy, Snow White, Witch Queen]
}
}
11.5 List
两种类型的List:
基本的ArrayList,它常用于随机访问元素,但是在List的中间插入和移除元素时比较慢。
LinkedList:它通过代价比较低的在List中间进行插入和删除操作,提供了优化的顺序访问,在随机访问方面相对比较慢,但是它的特性集较ArrayList更大。
下面实例通过导入typeinfo.pets,可以看到list的常用方法:
typeinfo.pets的jar包:链接: https://pan.baidu.com/s/17ba6RNplxR4sEEWVug3A8g 提取码: ptsv
import typeinfo.pets.*;
import java.util.*;
public class ListFeatures {
public static void main(String[] args) {
Random rand = new Random(47);
List<Pet> pets = Pets.arrayList(7);
System.out.println( "1:" + pets); //1:[Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug]
Hamster h = new Hamster();
pets.add(h);
System.out.println("2:" + pets); //2:[Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Hamster]
System.out.println(pets.contains(h)); //true
pets.remove(h);
System.out.println("3:" + pets); //3:[Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug]
Pet p = pets.get(2);
System.out.println("4:" + p +" " + pets.indexOf(p)); //4:Cymric 2
Pet cymric = new Cymric();
System.out.println("5:" + pets.indexOf(cymric)); //5:-1 不包含
System.out.println("6:" + pets.remove(cymric)); //6:false
System.out.println("7:" + pets.remove(p)); //7:true
System.out.println("8:" + pets); //8:[Rat, Manx, Mutt, Pug, Cymric, Pug]
pets.add(3, new Mouse());
System.out.println("9:" + pets); //9:[Rat, Manx, Mutt, Mouse, Pug, Cymric, Pug]
List<Pet> sub = pets.subList(1,4);
System.out.println("subList:" + sub); //subList:[Manx, Mutt, Mouse]
System.out.println("10:" + pets.contains(sub)); //10:false
System.out.println("10:" + pets.containsAll(sub)); //10:true
Collections.sort(sub);
System.out.println("sorted subList: " + sub); //sorted subList: [Manx, Mouse, Mutt]
System.out.println("11: " + pets.containsAll(sub)); //11: true 排序之后 仍然包含
Collections.shuffle(sub, rand);
System.out.println("shuffled subList:" + sub); //shuffled subList:[Mouse, Manx, Mutt]
System.out.println("12:" + pets.containsAll(sub)); //12:true
List<Pet> copy = new ArrayList<Pet>(pets);
System.out.println("copy:" + copy);
System.out.println("pets:" + pets); //pets:[Rat, Mouse, Manx, Mutt, Pug, Cymric, Pug]
sub = Arrays.asList(pets.get(1), pets.get(4));
System.out.println("sub:" + sub); //sub:[Mouse, Pug]
copy.retainAll(sub); //保留,取交集
System.out.println("13:" + copy); //13:[Mouse, Pug]
copy = new ArrayList<>(pets); //Get a fresh copy
copy.remove(2);
System.out.println("14:" + copy); //14:[Rat, Mouse, Mutt, Pug, Cymric, Pug]
copy.removeAll(sub);
System.out.println("15:" + copy); //15:[Rat, Mutt, Cymric, Pug]
copy.set(1,new Mouse()); //replace an element
System.out.println("16:" + copy); //16:[Rat, Mouse, Cymric, Pug]
copy.addAll(2,sub);
System.out.println("17:" + copy); //17:[Rat, Mouse, Mouse, Pug, Cymric, Pug]
System.out.println("18:" + pets.isEmpty()); //18:false
pets.clear();
System.out.println("19:" + pets); //19:[]
System.out.println("20:" + pets.isEmpty()); //20:true
pets.addAll(Pets.arrayList(4));
System.out.println("21:" + pets); //[Manx, Cymric, Rat, EgyptianMau]
Object[] o = pets.toArray();
System.out.println("22:" + o[3]); //22:EgyptianMau
Pet[] pa = pets.toArray(new Pet[0]);
System.out.println("23:" + pa[3].id()); //23:14
}
}
在第五和第六的打印中可能会有些不解,这是因为List的行为根据equals()的行为而有所变化。在确定一个元素是否属于某个List,发现某个元素的索引,以及从某个List中移除一个元素时,都会用到equals()方法(它是根类Object的一部分)。
在输出行7和8中,可以看到对精确匹配List中某个对象的对象进行移除是成功的,因为这两个引用对象指向了同一个对象。
retainAll()方法是一种有效的交集额操作,如13行输出
通过使用toArray()方法,可以将任意的collection转换为一个数组。这是一个重载方法,将其无参数版本转换为Object数组。
练习7
:创建一个类,然后创建一个用你的类的对象进行初始化过的数组。通过使用subList()方法,创建一个你的List的子集,然后在你的List中移除这个子集。
class IDClass{
private static int counter;
private int count = counter++;
public String toString(){
return "IDClass " + count;
}
}
public class E07_TestList {
public static void main(String[] args) {
IDClass[] ids = new IDClass[10];
for (int i = 0; i < ids.length; i++){
ids[i] = new IDClass();
}
List<IDClass> lst = new ArrayList<>(Arrays.asList(ids));
System.out.println( "lst:" + lst); //lst:[IDClass 0, IDClass 1, IDClass 2, IDClass 3, IDClass 4, IDClass 5, IDClass 6, IDClass 7, IDClass 8, IDClass 9]
List<IDClass> subSet = lst.subList(lst.size()/4, lst.size()/2);
System.out.println("subSet = " + subSet); //subSet = [IDClass 2, IDClass 3, IDClass 4]
System.out.println("index = " + lst.size()/4); //index = 2
//lst.removeAll(subSet); //这两句功能是相同的,都可以清除lst中的subSet的内容
//subSet.clear();
subSet.set(0, new IDClass()); // subList中的修改 是可以同步到 lst中的,因为截取并没有创建新的对象,只是创建一个引用对象
System.out.println("subSet = " + subSet);
System.out.println("lst:" + lst);
}
}
11.6 迭代器
迭代器是一个对象,它的工作是遍历并选择序列中的对象。
Java中的Iterator只能单向移动,这个Iterator只能用来:
- 使用方法iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
- 使用next()获得序列中的下一个元素。
- 使用hasNext()检查序列中是否还有元素。
- 使用remove()将迭代器最新返回的元素删除。
示例:
import typeinfo.pets.Pet;
import typeinfo.pets.Pets;
import java.util.Iterator;
import java.util.List;
public class SimpleIterator {
public static void main(String[] args) {
List<Pet> pets = Pets.arrayList(12);
Iterator<Pet> iterator = pets.iterator();
while (iterator.hasNext()){
Pet p = iterator.next();
System.out.print(p.id() + ":" + p + " ");
}
//Output:0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:EgyptianMau 11:Hamster
System.out.println();
for (Pet p : pets){
System.out.print(p.id() + ":" + p + " ");
}
System.out.println();
// an iterator can also remove elements
System.out.println(iterator);
iterator = pets.iterator();
for (int i = 0; i < 6; i++){
iterator.next();
iterator.remove();
}
System.out.println(pets);
}
}
在最后删除之前又使用了一次iterator = pets.iterator();
将迭代器初始化,第一次看到这个感觉好像这句多余了,前面已经有了初始化了,就将这句先注释掉运行一下,发现竟然没有输出pets!!!这就跟神奇了,然后想着是不是遍历一遍之后,Iterator就自动清零了或者变为null了呢?尝试直接打印iterator看看,结果输出iterator:java.util.ArrayList$Itr@78308db1
,这就证明迭代器遍历之后仍然存在,这时候突然想到c语言中的指针,遍历之后,指针将移动到容器的最后一个元素后面的下标了,这时候再使用迭代器遍历,就没有东西了,为了验证这个,尝试打印iterator.hasNext()
果然为false,验证了我的猜测是正确的。
如果只是编列List,并不打算修改List对象本身,使用foreach语法会更加简洁。
Iterator还可以移除由next()产生的最后一个元素,这意味着在调用remove()之前必须先调用next()。
11.7 LinkedList
练习14
:创建一个空的LinkedList<Integer>
,通过使用ListIterator,将若干个Iterator插入这个List中,插入时,总是将他们插入到List的中间。(这个是真的没有看懂,那个判断偶数是怎能玩的)
public class E14_MiddleInsertion {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
ListIterator<Integer> it = list.listIterator();
for (int i = 1; i <= 10; i++){
it.add(i);
if (i % 2 == 0){
it.previous();
}
}
System.out.println(list);
}
}
11.8 栈 Stack
“栈”通常指“先进后出(LIFO)”容器。有时栈也被称为叠加栈,因为最后压入栈的元素,第一个弹出栈。
Stack是使用LinkedList实现的。peek()方法返回栈顶的元素,但是并不是将其从栈顶移除,而pop()将移除并返回栈顶元素。
练习15
:请使用net.mindview.util.Stack对下面的表达式求值,其中“+”表示将后面的字母压进栈,而“-”表示弹出栈顶字母并打印它:“+U+n+c---+e+r+t---+a-+i-+n+t+y---+ -+r+u--+l+e+s---
”
import java.util.Stack;
public class E15_Evaluator {
private static final Stack<Character> STACK = new Stack<>();
private static void evaluate(String expr){
char[] data = expr.toCharArray();
for (int i = 0; i < data.length;){
switch (data[i++]){
case '+' :
STACK.push(data[i++]);
break;
case '-' :
System.out.print(STACK.pop());
}
}
}
public static void main(String[] args) {
evaluate("+U+n+c---+e+r+t---+a-+i-+n+t+y---+ -+r+u--+l+e+s---");
}
}
/* Output:
cnUtreaiytn ursel
*///:~
11.9 Set
Set不保存重复的元素。如果试图将相同对象的多个实例添加到Set中,那么它就会阻止这种重复现象,这种阻止不是报错,而是忽略添加此重复的对象。
可以看一下Set的add方法的注释:add方法的返回值是boolean类型的,如果插入成功返回true,否则返回false。
/**
* Adds the specified element to this set if it is not already present
* (optional operation). More formally, adds the specified element
* <tt>e</tt> to this set if the set contains no element <tt>e2</tt>
* such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>. In combination with the
* restriction on constructors, this ensures that sets never contain
* duplicate elements.
*
* <p>The stipulation above does not imply that sets must accept all
* elements; sets may refuse to add any particular element, including
* <tt>null</tt>, and throw an exception, as described in the
* specification for {@link Collection#add Collection.add}.
* Individual set implementations should clearly document any
* restrictions on the elements that they may contain.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
* @throws UnsupportedOperationException if the <tt>add</tt> operation
* is not supported by this set
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this set
* @throws NullPointerException if the specified element is null and this
* set does not permit null elements
* @throws IllegalArgumentException if some property of the specified element
* prevents it from being added to this set
*/
boolean add(E e);
Set中最常被使用的是测试归属性,你可以很容易的询问某个对象是否在某个Set中。正因如此,查找成了Set中最重要的操作,通常在使用Set时,会选择HashSet,因为它专门对快速查找进行了优化。
下面是一个存放Integer对象的HashSet的示例:
public class SetOfInteger {
public static void main(String[] args) {
Random random = new Random(47);
Set<Integer> intSet = new HashSet<Integer>();
for (int i = 0; i < 10000; i++){
intSet.add(random.nextInt(30));
}
boolean add = intSet.add(10);
System.out.println(add);
System.out.println(intSet);
}
}
但是让我不解的是,按理说使用的是HashSet,应该是乱序的,书上的intSet输出就是乱序的,而我用IDEA运行输出是经过排序的,输出是0-29从小到大排序的,我又尝试使用myeclipse运行,结果又不一样了,15之前的数是排序,15后面的数,顺序不对?????
myeclipse:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28]
IDEA:[0, 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]
可以看到这里只存了0-29之间的数,每个数只有一个实例出现在结果中。
而使用TreeSet时,IDEA和Myeclipse的输出都是从小到大排序的。
使用Set比较多的就是使用contains() 方法用来判断其归属性。如果Set中存在某个元素,则返回true。
11.10 Map
Map可以返回它的键的Set,它的值的Collection,或者它的键值对的Set。
keySet()方法产生了Map中的所有键组成的Set,它在foreach语句中被用来迭代遍历该Map。
对一个HashMap中的键值对进行排序:先用keySet()方法抽取所有的键,排序之后,再将结果放入LinkedHashMap里面。
public class E18_MapOrder {
public static void main(String[] args) {
// 填充Map
Map<String, String> m1 = new HashMap<String, String>(Countries.capitals(25));
System.out.println(m1);
// 获取Map的键
String[] keys = m1.keySet().toArray(new String[0]);
Arrays.sort(keys);
Map<String, String> m2 = new LinkedHashMap<String, String>();
for (String key : keys) m2.put(key, m1.get(key));
System.out.println(m2);
}
}
11.11 Queue 队列
队列是一个典型的先进先出的容器,即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序和取出的顺序是相同的。
LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现。通过将LinkedList向上转型为Queue。
public class QueueDemo {
public static void printQ(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<>();
Random random = new Random(47);
for (int i = 0; i < 10; i++){
queue.offer(random.nextInt(i + 10));
}
printQ(queue);
Queue<Character> qc = new LinkedList<>();
for (char c : "Brontosaurus".toCharArray()){
qc.offer(c);
}
printQ(qc);
}
}
/* Output::
8 1 1 1 5 14 3 1 0 1
B r o n t o s a u r u s
*/
offer()方法是与Queue相关的方法之一,它在允许的情况下,将一个元素插入到队尾,或者返回false。
peek()和element()都将在不移除的情况下返回队头,但是peek()方法在队列为空时返回null,而element()会抛出NoSuchElementException异常。
poll()和remove()方法将移除并返回队头,但是poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常。
11.12 Collection和Iterator