《Java编程思想》第四版读书笔记 第十一章 持有对象

原创
2016/06/03 09:33
阅读数 225

11.1

在第一个例子中使用了@SuppressWarnings注解。它的作用是抑制编译器产生的告警信息。

(1)用于抑制一个类型的警告:

@SuppressWarnings("unchecked")

(2)用于抑制多个类型的警告:

@SuppressWarnings(value = {"unchecked", "rawtypes"})

(3)用于抑制多有类型的警告:

@SuppressWarnings("all")

该注解目标为类、字段、函数、函数形参、构造函数和函数的局部变量。建议注解声明在最接近告警发生的位置。

javac -Xlint 用于在编译程序的过程中进行更细节的额外检查。

javac带有-X表示非标准选项(不带-X自然是标准选项了),表示当前版本支持,未来不一定支持的选项。通过javac -X可查看当前版本支持的非标准选项。

11.2

Java 容器划分成两个不同的概念:

(1)Collection。一个独立元素的序列,这些元素都服从一些规则。例如,List必须按顺序插入,Set不能有重复的元素,Queue是队列;

(2)Map。一组键值对对象,允许使用键来查找值。

11.3

本小节主要介绍了四种得到列表的方法:

(1)Collection的构造函数可以接受另一个Collection对象,用它来初始化自身;

(2)Arrays.asList()方法的参数可以是数组或变长参数,返回的是List对象,但是在这种情况下,其底层表示的是数组,因此不能调整尺寸,当调用它的add()方法和remove()(此处书中写的是delete()方法,应该是笔误)方法时会抛出Unsupported Operation异常;

(3)Collections.addAll()方法的参数是一个Collection对象、数组或变长参数,将元素添加到Collection对象中;

(4)Collection.addAll()(注意这里是Collection不是Collections)只能接口另一个Collection对象作为参数,这种方法不如(2)和(3)灵活,但是这种方法速度很快,当复合条件时它为首选。

本小节的最后,作者举例说明了Arrays.asList()可能会遇到的问题和解决的方法。当将Light和Heavy类型的对象作为参数传入Arrays.asList()方法中时,编译器会认为方法返回的类型是List,将其赋值给一个List的引用就会报错,解决方法是给Arrays.asList()方法添加显示类型Arrays.asList()。

经过编码测试这个问题在1.8中得到了解决,但是1.7之前的版本依旧存在这个问题。

11.4

如果想要打印数组的内容,必须将其作为参数传递给Arrrays.toString()方法,但是容器不需要类似的帮助,它的toString()直接可以打印出容器的内容。

Collection容器主要包括:

(1)List,它以特定的顺序保存一组元素。ArrayList和LinkedList是List的两个主要类型。ArrayList长于随机访问元素,但是在中间插入和移除元素较慢。LinkedList优化了顺序访问,并且在List中间进行插入和删除操作代价较低,但是随机访问较慢,并且它的特性比ArrayList更大。

(2)Set,它的元素不能重复。HashSet、TreeSet和LinkedHashSet是主要的Set类型。通过将它们的内容打印出来可以看出,它们实现存储元素的方式不同。HashSet使用较复杂的存储方法以提供最快的获取元素方式;TreeSet按照比较结果的升序保存对象;LinkedHashSet按照添加的顺序保存对象。

(3)Queue,它只允许在一端插入对象,在另一端移除对象。

HashMap、TreeMap和LinkedHashMap是Map的三种主要类型,主要区别于 HashSet、TreeSet和LinkedHashSet相同。

11.5

例子中总结了一些List的方法:

可以使用contains()方法来确定某个对象是否在列表中,如果想移除一个对象,则可以将这个对象的引用传递给remove()方法,无此对象会导致删除失败,方法返回false。如果有一个对象的引用,可以使用indexOf()方法来发现对象在List中的索引号,无此对象返回-1。

注意:确定一个元素是否属于List、发现某个元素的索引以及从List中移除一个元素,会用到equals()方法。所以List的这些行为会根据持有对象的equals()行为不同有所变化。也可以使用索引来移除List中的一个元素,这样就不用担心equals()方法。

可以在List中间插入,ArrayList中间插入是很昂贵的,而LinkedList中间插入是很廉价的。

containsAll()判断不在乎顺序。

shuffle()方法的作用是随机打乱原来的元素顺序(洗牌)。

retainAll()仅在List中保留指定collection中所包含的元素。

set()方法替换掉指定索引位置的元素。

addAll()不指定位置将添加到list最后,如果指定位置可以插入到List中间。

toArray()方法将Collection转换为数组。无参数版本返回的是Object数组,有参数的版本可以向其传递目标类型的数组,那么它将产生指定类型的数组(而不是Object数组)。如果参数数组太小,存放不下目标类型的数据,那么它将创建合适尺寸的数组。

11.6

迭代器通常被称为轻量级对象,创建它的代价非常小。

Iterator的作用(它只能单向移动):

(1)使用容器的iterator()方法要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素;

(2)使用next()获得序列中的下一个元素;

(3)使用hasNext()检测序列中是否还有元素;

(4)使用remove()将迭代器新近返回的元素删除。这里需要注意,迭代器真的可以删除容器里的元素,但是注意调用remove()之前必须调用next()方法。remove()方法是一个可选的方法,即不是所有的Iterator实现都必须实现该方法。(为什么它是可选的?我看API remove()方法如果迭代器不支持 remove 操作会抛出 UnsupportedOperationException  。

迭代器其实是一种设计模式,使用它可以不必知道容器的确切类型。这样就能够将遍历序列的操作与序列底层的结构分离

ListIterator是Iterator的子类型,它更强大,但是它只能用于各种List类的访问。它可以双向移动。

nextIndex()对 next 的后续调用所返回元素的索引。(如果列表迭代器在列表的结尾,则返回列表的大小);

previousIndex()返回对 previous 的后续调用所返回元素的索引。(如果列表迭代器在列表的开始,则返回 -1);

可以使用set()方法替换访问过的最后一个元素;

调用listIterator()方法产生一个指向List开始处的ListIterator,也可以调用listIterator(int n)方法创建一个开始就指向 索引为n的元素处的ListIterator。

11.7

LinkedList在实现了基本的List接口的功能之上还添加了可以使其用作栈、队列或双端队列的方法。

有些方法并没有差别、有些只有细微的差别,它们有不同的名字主要是因为这些名字在特定的环境中更加 合适。例如:

getFirst()、element()和peek()都返回列表的第一个元素且不删除它们,如果列表为空前两个方法会抛出NoSuchElmentException,peek()方法则会返回null;

removeFirst()、remove()和poll()方法都是移除并返回列表的头,如果列表为空前两个会抛出NoSuchElementException,poll()方法会返回null;

addFirst()与add()和addLast()相同,都将某个元素 插入到列表的尾部;

removeLast()移除并返回列表的最后一个元素;

offer()将指定元素添加的列表的末尾。

练习13上一段有句话翻译有误,原话为“如果你浏览一下Queue接口就会发现,它在LinkedList的基础上添加了element()、offer()、peek()、poll()和remove()方法,以使其可以成为一个Queue的实现。”翻了一下英文版,认为应该翻译为:“如果你浏览一下Queue接口就会发现,element()、offer()、peek()、poll()和remove()方法被添加到LinkedList类中使其成为一个Queue接口的实现。

11.8

作者使用代理用LinkedList实现了一个栈,这里使用代理而不是继承LinkedList是因为LinkedList还有许多与栈无关的方法,使用代理可以屏蔽掉。一个栈需要的方法主要有push(T t)入栈、poll()出栈和peek()返回栈顶元素但不删除,另外还需要有一个empty()方法判断栈是否为空。

Java在早期版本中就已经有了栈java.util.Stack,但是由于设计糟糕,现在已经不推荐使用了,还有Vector、Dictionary等。

11.9

TreeSet的构造函数允许传入一个比较器(Comparator) 作为参数用于给元素排序

在最后一个例子中作者为字符串不区分大小写排序传入了String.CASE_INSENSITIVE_ORDER比较器。

11.10

对象可以返回它的键的Set——keySet(),返回值的Collection——values(),返回键值对的Set——entrySet()。

11.11

如果想使用队列,可以创建LinkedList对象并向上转型为Queue,这样Queue接口窄化了LinkedList的访问权限。

offer()方法在允许的情况下将元素插入到队尾,peek()和element()在不删除元素的情况下返回队头,如果队列为空前者返回Null,而后者抛出NoSuchElementException异常。poll()和remove()方法返回队头并删除元素,队列为空时前者返回Null,后者抛出异常。

Java 1.5添加了PriorityQueue类,在这个队列中,不是根据插入的时间来弹出元素,而是根据元素的优先级,优先级高的先弹出。当用offer()向PriorityQueue插入对象后,它会在队列中根据自然顺序进行排序,可以向PriorityQueue传入自定义的Comparator来改变默认的顺序。

练习29让我们创建一个继承自Object的类,然后把这个类的实例添加到PriorityQueue队列中,这样会抛出异常,因为这个类没有实现Comparable接口,PriorityQueue没有办法判断元素的优先级。

11.12

首先介绍了AbstractCollection抽象类,它实现了Collection接口,只有iterator()和size()方法是抽象方法,继承时需要实现。另外需要注意的是AbstractCollection的add()方法虽然被实现了,但是函数里仅有一句代码——抛出UnSupportedOperationException异常。所以继承AbstractCollection时还要覆盖add()方法。

11.13

Java 1.5添加了Iterable接口,它的iterator()方法返回一个迭代器,注意到Collection接口也继承了Iterable接口。实现Iterable接口的类都可以应用在foreach语句中,同时数组也可以应用于foreach语句,但是注意数组并没有实现Iterable接口。

System.getEnv()方法返回操作系统的环境变量。

例子中使用了设计模式中的适配器模式,它的主要功能是将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。

例子中着重提到了Arrays.asList()方法,如果修改方法返回的List,那么作为参数传入的数组也将会被修改,此种情况容易被忽略。

11.14

新程序中不应该再使用已经过时的Vector、Hashtable和Stack。

下图是容器相关的接口和类之间的关系:

总结起来共有四种容器:List、Set、Queue和Map。每一种各有两三个实现。常用的在图中用粗线框表示,虚线框表示接口,实线框表示类。空心虚线箭头接口或者类实现了上层接口,实心虚线箭头表示某个类可以生成箭头所指向的类。

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
在线直播报名
返回顶部
顶部