`

第十一章 持有对象

 
阅读更多
首先给出一个整体类图结构:

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


添加一组元素

    在java.util包中的Array和Collection类中都有很实用方法,Arrays.asList()方法接受一个数组或者是一个用逗号分隔的元素列表,并将其转换为一个List对象。Collections.addAll()方法接受一个Collection对象,数组,逗号分隔的列表。
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));
		Collections.addAll(collection, 11,12,13,14,15);
		Collections.addAll(collection, moreInts);
		List<Integer> list = Arrays.asList(16,17,18,19,20);
		list.set(1, 99);
	}

你也可以直接使用Arrays.asList的输出,将其当做List,但是在这种情况下,其底层表示的是数组,因此不能调整大小,如果add或者delete就有可能改变数组大小,此时会得到运行时错误(Unsupported Operation)。

List<Snow> snow1 = Arrays.asList(new Crusty(), new Slush(), new Powder());
//light heavy都集成powder  powder集成snow
		//wont compile
		//List<Snow> snow2 Arrays.asList(new Light(), new Heavy());
		//Compiler says:
		//found: java.util.List<Poder>
		//required: java.util.List<Snow>
		List<Snow> snow3 = new ArrayList<Snow>();
		Collections.addAll(snow3, new Lisht(), new Heavy());
		List<Snow> snow4 = Arrays.<Snow>asList(new Lisht(), new Heavy());

当试图创建snow2时,Arrays.asList中只有Powder类型,因此它会创建List<Powder>而不是List<Snow>,尽管Collections.addAll工作的很好,因为它从第一个参数中了解到了目标类型是什么。
正如在snow4的操作中所看到的,可以在Arrays.asList中间插入一条“线索”,以告诉编译器对于由Arrays.asList产生的List类型,事迹的目标类型应该是什么。这称为现实类型参数说明。

容器的打印

Collection在每个槽中只能保存一个元素。此类容器包括:List,它以特定的顺序保存一组元素;Set。元素不能重复;Queue,只允许在容器的一“端”插入对象,并从另外一端移除对象。Map则用大括号扩住(打印形式)。

ArrayList和LinkedList都是List类型。它们都是按照插入的顺序保存元素。

HashSet、TreeSet和LinkedHashSet都是Set类型,HashSet是最快的获取元素的方式,如果存储顺序很重要,那么可以使用TreeSet,它按照比较结果的升序保存对象;或者使用LinkedHashSet,它按照被添加的顺序保存对象。

Map:HashMap/TreeMap和LinkedHashMap。与HashSet一样,Hash

List

List接口在Collection的基础上添加了大量的方法,使得可以在lIst的中间插入和移动元素。
有两种类型的List:

基本的ArrayList,它长于随机访问元素,但是在List的中间插入和移动元素时较慢。

LinkedList,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问。LickedList在随机访问方面相对较慢,但是他的特性集较ArrayList更大。

如果你有一个对象的引用,则可以使用indexOf()来发现该对象在List中所在位置的索引编号。
它们有很多方法,具体看api文档。

迭代器

Java的iterator只能单向移动,这个Iterator只能用来:
(1) 使用方法iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新近返回的元素删除。

有了Iterator就不必为容器中元素的数量操心了,那是由hasNext()和next()关心的事情。
如果你只是向前遍历List,并不打算修改List对象本身,那么你可以看到foreach语法会显得更加简洁。

Iterator还可以移除由next()产生的最后一个元素,这意味着在调用remove()之前必须先调用next()。
public class Test {

	public static void main(String[] args) {
		LinkedList<Pet> petsLL new LinkedList<Pet>(pets);
		HashSet<Pet> petsHS = new HashSet<Pet>(pets);
		display(pets.iterator());
		display(petsLL.iterator());
		display(petsHS.iterator());
	}
	
	public static void display(Iterator<Pet> it) {
		while(it.hasNext()) {
			Pet p = it.next();
			System.out.println(p.getId());
		}
	}
}


ListIterator

ListIterator是一个更加强大的Iterator的子类型,它只能用于各种List类的访问。尽管Iterator只能向前移动,但是ListIterator可以双向移动。它还可以产生相对于迭代器在列表中指向当前位置的前一个和后一个元素的索引,并且可以使用set()方法替换它访问过的最后一个元素。listIterator()开始指向List的开始处,listIterator(n)指向列表索引为n的元素。

public static void main(String[] args) {
		List<Pet> pets = Pets.arrayList(8);
		ListIterator<Pet> it = pets.listIterator();
		while(it.hasNext()) {
			System.out.println(it.next() + it.nextIndex() + it.previousIndex());
		}
		while(it.hasPrevious()) {
			System.out.println(it.previous().getId());
		}
		it = pets.listIterator(3);
		while(it.hasNext()) {
			it.next();
			it.set(Pets.randomPet());
		}
	}


LinkedList

LinkedList也像ArrayList一样实现了基本的List接口,但是它执行某些操作(在List的中间插入和移除)时比ArrayList更高效,但是在随机访问就稍逊色一些。
浏览Queue接口就会发现,它在LinkedList的基础上添加了element()、offer() peek() poll()和remove()方法,以使其可以成为一个Queue的实现。

Stack


“栈”通常是指“后进先出”的容器。
LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用,不过,有时一个真正的“栈”更能把事情讲清楚:

public class Stack<T> {
	private LinkedList<T> storage = new LinkedList<T>();
	public void push(T t) {
		storage.addFirst(t);
	}
	public T peek() {
		return storage.getFirst();
	}
	public T pop() {
		return storage.removeFirst();
	}
	public boolean empty() {
		return storage.isEmpty();
	}
	public String toString() {
		return storage.toString();
	}
}

尽管已经有了java.util.Stack,但是LinkedList可以产生更好的Stack,因此LinkedList所采用的方式更是可取的。

Set

Set不保存重复的元素。查询时Set中最重要的操作,因此你通常都会选择一个HashSet的实现,它专门对快速查找进行优化。

HashSet使用了散列。HashSet所维护的循序和TreeSet或LinkedHashSet都不同,因为它们的实现具有不同的元素存储方式。TreeSet将元素存储在红黑树数据结构中,而HashSet使用的是散列函数。LinkedHashList因为查询速度的原因也使用了散列,但是看起来他使用了链表来维护元素的插入顺序。

如果你想对结果排序,一种方式是使用TreeSet来代替HashSet

Map

将对象映射到其他对象的能力是一种解决变成问题的杀手锏。
下面允许你使用一个String描述来查找Pet,它还展示了你可以使用怎样的方法通过使用ContainsKey()和containsValue()来测试一个Map:
public static void main(String[] args) {
		Map<String, Pet> petMap = new HashMap<String, Pet>();
		petMap.put("My cat", new Cat("Molly"));
		petMap.put("My dog", new Dog("dog"));
		petMap.put("My Hamster", new Hamster("Hamster"));
		System.out.print{petMap);
		System.out.println(petMap.containsKey("My dog"));
		System.out.println(petMap.containsValue(dog));
}


Queue

队列是一个典型的先进先出的容器。
LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现。通过将LinkedList向上转型为Queue,下面是示例使用了在Queue接口中与Queue相关的方法:
public class QueueDemo {
	public static void printQ(Queue queue) {
		while(queue.peek() != null) {
			System.out.println(queue.remove());
		}
	}
	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));
		}
		printQ(queue);
		Queue<Character> qc = new LinkedList<Character>();
		for(char c : "Brontosaurus".toCharArray()) {
			qc.offer(c);
		}
	}
}


PriorityQueue

优先级队列声明下一个弹出元素是最需要的元素。
当你在PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序。默认的排序将使用对象在队列中的自然顺序,但是你可以通过提供自己的Camparator来修改这个顺序。PriorityQueue可以确保当你调用peek poll remove方法时,获得元素将是队列中优先级最高的元素。
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) {
		PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();
		Random rand = new Random(47);
		for(int i=0; i<10; i++) {
			priorityQueue.offer(rand.nextInt(i+10));
		}
		printQ(priorityQueue);
		List<Integer> ints = Arrays.asList(25,22,12,51,124,564,23,64,32,64,1,2,3,99,77,66);
		priorityQueue = new PriorityQueue<Integer>(ints);
		printQ(priorityQueue);
		priorityQueue = new PriorityQueue<Integer>(ints.size(), Collections.reverseOrder());
		priorityQueue.addAll(ints);
		printQ(priorityQueue);
	}
}

Collections.reverseOrder产生的反序Comparator。

Collection和Iterator

Collection是描述所有序列容器的共性根接口。另外java.util.AbstractCollection类提供了Collection的默认实现,使得你可以创建AbstractCollection子类型而不必重复代码。

生成Iterator是将队列与消费队列的方法连接在一起耦合度最小的方式,并且与实现Collection相比,它在序列类上所施加的约束也少得多。

Foreach与迭代器
由于cs是一个Collection,所以这段代码展示了能够与foreach一起工作是所有Collection对象的特性。

之所以能够工作,是因为java SE5引入了新的被称为Iterable的接口,该接口包含一个能够产生Iterator的iterator方法,并且Iterator接口被foreach用来在序列中移动。因此如果你创建了任何实现了Iterable的类,都可以将它用于foreach语句中:

总结

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

各种Queue以及栈的行为,由LinkedList支持。

Map是一种将对象与对象相关联的设计。HashMao设计用来快速访问;而TreeMap保持“键”始终处于排序状态,所以没有HashMap快。LinkedHashMap保持元素插入的顺序,但是也通过散列提供了快速访问能力。

Set不接受重复元素。HashSet提供最快的查询速度,而TreeSet保持元素处于排序状态。LinkedHashSet以插入顺序保存元素。
新程序中不应该使用过时的Vector Hashtable和Stack。

  • 大小: 54.7 KB
分享到:
评论

相关推荐

    Java编程思想第11章持有对象.ppt

    【Java编程思想第11章持有对象】 在Java编程中,持有对象是一个核心概念,它涉及到如何有效地管理和操作对象集合。本章主要讨论了两个关键主题:泛型和容器类,特别是Collection家族和Map家族的其他成员,以及迭代...

    think in java 第11章 持有对象

    第11章的主题是“持有对象”,这一章主要探讨了如何在Java中创建和管理对象,包括对象的引用、对象的生命周期、类与对象的关系,以及如何通过集合来存储和操作对象。以下是对这些知识点的详细解释: 1. **对象引用*...

    Java编程思想笔记(全)

    第十一章关注于如何持有对象以及对象之间的引用关系。本章探讨了对象引用的各种方式,包括强引用、软引用、弱引用和虚引用,并讨论了它们各自的用途。此外,还会介绍垃圾回收机制如何处理这些不同类型的引用,以及...

    编程思想下篇

    第11章 持有对象 第12章 通过异常处理错误 第13章 字符串 第14章 类型信息 第15章 泛型 第16章 数组 第17章 容器深入研究 第18章 Java I/O系统 第19章 枚举类型 第20章 注解 第21章 并发 第22章 图形化用户界面

    Thinking in java4(中文高清版)-java的'圣经'

    非静态实例初始化 5.8 数组初始化 5.8.1 可变参数列表 5.9 枚举类型 5.10 总结 第6章 访问权限控制 第7章 复用类 第8章 多态 第9章 接口 第10章 内部类 第11章 持有对象 第12章 通过异常处理错误 第13章 字符串 第...

    thinkinjava源码-Thinking-in-Java:ThinkingInJava源代码和练习题

    第11章 持有对象 第12章 通过异常处理错误 第13章 字符串 第14章 类型信息 第15章 泛型 第16章 数组 第17章 容器深入研究 第18章 Java I/O系统 第19章 枚举类型 第20章 注解 第21章 并发 第22章 图形化用户界面 水平...

    swift学习第三章

    第十一节“函数式编程”部分,我们接触了Swift中的高阶函数(Higher-Order Functions)和闭包(Closures)。高阶函数可以接受一个或多个函数作为参数,或者返回一个函数作为结果,这在处理数据集合时非常有用。闭包...

    《财政学》第11章税收制度.ppt

    综上所述,《财政学》第11章税收制度部分为我们提供了一个全面了解和分析税收体系、税收分类及其在宏观经济政策中作用的视角。通过对税收制度的深入研究,可以更好地理解政府的财政政策如何影响国家的经济运行和社会...

    第11章-数据库的安全性和控制

    【第11章-数据库的安全性和控制】 数据库的安全性是确保数据不被未经授权的个人访问或修改的关键方面,尤其对于初学者来说,理解并掌握这一主题至关重要。安全性和完整性是两个不同的概念,前者关注防止非法用户的...

    C#应用程序设计教程 第11章 数据库与ADO.ppt

    在C#应用程序设计中,第11章着重讲解了如何使用数据库和ADO.NET技术来创建数据库应用系统。数据库是组织和存储数据的核心,常见的数据库管理系统包括FoxPro、Sybase、Access、Oracle以及SQL Server,它们大多基于...

    财政学第11章税收制度PPT学习教案.pptx

    第11章主要探讨了税收体系的构成和分类,以及税制结构的设计。税收体系包括个人所得税、公司所得税、社会保险税、财产税、遗产和赠与税、营业税、增值税、关税等多种税种,这些税种依据征税对象在经济活动中的不同...

    操作系统原理:第十四章 保护.ppt

    这一章主要讲解了保护系统的目标、保护域、访问矩阵的实现、访问权限的撤回、基于权限的系统以及基于语言的保护等概念。 首先,保护目标是确保操作系统中的各个对象(如硬件资源、软件模块、数据文件等)只能被授权...

    Android第十八章Android架构模式

    "Android第十八章Android架构模式"可能涵盖了一系列用于优化Android应用程序设计的模式。这些模式旨在提高代码的可读性、测试性和可复用性,从而降低长期维护成本。在本章节中,我们可能会学习到以下几种常见的...

    Java基础入门自学课件 第11章 泛型(共4页).rar

    在这个“Java基础入门自学课件 第11章 泛型”中,我们可以期待学习到以下几个核心知识点: 1. **泛型的基本概念**:泛型允许我们在定义类、接口和方法时指定一种或多种类型参数,这样在实际使用时可以传入具体的...

    Think in java学习笔记

    #### 第11章:持有对象 - **对象的持有方式**:包括强引用、软引用、弱引用和虚引用等不同类型的引用。 #### 第12章:通过异常处理错误 - **异常处理**:介绍了异常的分类、捕获和抛出机制。 #### 第13章:字符...

    心理学-第十四章-态度与品德心理.ppt

    《心理学-第十四章-态度与品德心理》这一章节深入探讨了态度与品德的形成、结构、特征以及它们在个体行为中的作用和影响。 态度这一概念,在心理学中指的是个体对特定事物或观念所持有的相对稳定的评价、情感和行为...

    数据库第四版答案(王珊萨师煊)第11章并发控制[参照].pdf

    共享锁(S锁)则允许事务读取数据,但不允许修改,并且允许多个事务同时持有相同数据对象的共享锁。通过合理的封锁协议,比如两阶段封锁协议,可以有效地避免数据不一致问题。 两阶段封锁协议是一种经典的封锁协议...

    第11章 枚举_注解_内部类.docx

    在Java编程语言中,枚举(Enumeration)是一种特殊的数据类型,自Java 5开始引入,主要用来表示一组有限且固定的值。枚举类型是类的一个子类型,继承自`java.lang.Enum`基类,不能手动定义子类。使用枚举可以提高...

    第17章移动语义.pdf

    移动语义是C++11引入的一种优化机制,主要目的是提高程序性能,特别是涉及资源重分配的情况。在C++中,对象分为两种类型:左值(lvalue)和右值(rvalue)。左值可以是变量,可以有名字并且可以被再次引用,而右值...

Global site tag (gtag.js) - Google Analytics