持有对象
### Java 有多种方式保存对象(应该说是对象的引用)。
### Java 类库中提供了一套相当完整的容器:其中基本的
类型是 List 、 Set 、 Queue 和 Map 。这些对象的类型
也称为集合类。
》》泛型和类型安全的容器
### ArrayList -->当作“可以自动扩充自身尺寸的数组”
方法:add() ---->加入元素
get(索引值) ---->通过索引值来获取元素
size() ----> 返回该容器中已经添加了多少元素
### class Apple {
}
class Orange{
}
不使用泛型的容器:
ArrayList apples = new ArrayList();
apples.add(new Apple());
apples.add(new Orange());
说明:在以上不使用泛型的容器中,可以加入 Apple 类的
对象的引用,还可以加入 Orange 类的对象的引用。这两个
类都默认的继承 Object 。
所以,加入的不同类的引用,在容器中都看做是 Object 类型。
当从这样的容器中通过 get () 获取到元素时,要将 Object 类型
强转 为 实际的类型,才能被正确的应用。
如果不做上面的强转操作,那么会得到语法错误。
### 使用泛型:
要想定义用来保存 Apple 对象的 ArrayList ,你可以声明
ArrayList<Apple> ,而不仅仅只是 ArrayList , 其中尖括号
括起来的是类型参数(可以有多个),它指定了这个容器实例
可以保存的类型。
通过使用泛型,就可以在编译期防止将错误类型的对象放置到
容器中。
---------------
使用泛型的示例:
ArrayList<Apple> apples = new ArrayList<Apple> ();
--------------
### 通过使用泛型,你不仅知道编译器将会检查你放置到容器中
的对象类型,而且在使用容器中的对象时,可以使用更加清晰的
语法。(不需要进行类型强转,因为在加入之前就知道是什么类型的)
### 如果不需要使用每个元素的索引,你可以使用 foreach 语法来
选择 List 中的每个元素。
### 当你指定了某个类型作为泛型参数时,你并不仅限于只能将
该确切类型的对象放置到容器中。向上转型也可以像作用于其他类型
一样作用于泛型。(即可以添加类型参数的导出类)
###通过使用 hashCode() 方法来产生散列码。
》》基本概念
### Java 容器类类库的用途是“保存对象”,并将其划分为两个不同的
概念:
@@ Collection
一个独立元素的序列,这些元素都服从一条或多条规则。
List 必须按照插入的顺序保存元素;
Set 不能有重复的元素;
Queue 按照排队规则来确定对象产生的顺序(通常与它们被插
入的顺序相同)
@@ Map
一组成对的“键值对”对象,允许你使用键来查找值。
ArrayList 允许你使用数字来查找值,因此在某种意义上来说,它将
数字与对象关联在了一起。
映射表允许我们使用另一个对象来查找某个对象,它也被称为“关联
数组”
因此,你可以使用键对象来查找值对象。
### List <Apple> apples = new ArrayList<Apple>();
说明:ArrayList 已经向上转型为 List 类型了
List 是一个接口。
### 在 Set 集合中,只有元素不存在的情况下才会添加。在使用 ArrayList
或者任何种类的 List 时,add( ) 方法总是表示“把它放进去”,因为在
List 中不关心是否存在重复。
### 所有的 Collection 都可以用 foreach 语法遍历。
》》添加一组元素
### Collection.addAll() 方法运行起来要快得多,而且构建一个不包含
元素的 Collection ,然后调用 Collections.addAll()这种方式很方便,
因此它是首选方式。
### Collection.addAll() 成员方法只能接受另一个 Collection对象作为参数,
因此它不如 Arrays.asList() 或 Collections.addAll() 灵活,这两个方法
使用的都是可变参数列表。
### 如果你直接使用 Arrays.asList() 的输出,将其当作 List , 但是这种
情况下,其底层表示的是数组,因此不能调整尺寸。如果你试图
add () 或者 delete () 方法在这种列表中添加或删除元素,就有可能
引发改变数组尺寸的尝试,因此你将在运行时获得“Unsupported(不支
持的操作)”错误。
### 例如:
List<Snow> snow4 = Arrays.<Snow>asList();
说明:可以在 Arrays.asList() 中间插入一条“线索”,以告知编译器
对于由 Arrays.asList() 产生的 List 类型,实际的目标类型应该是什么,
这称为显式类型参数说明。
》》容器的打印
### 使用 Arrays.toString() 来产生数组的可打印表示,但是打印容器无需任何
帮助。
### Java 容器类库中的两种主要类型:Collection 和 Map
两者之间的区别因素:在于容器中每个“槽”保存的元素个数。
@@ Collection 在每个槽中只能保存一个元素。
List , 它以特定的顺序保存一组元素;
Set , 元素不能重复;
Queue , 只允许在容器一“端”插入对象,并从另一“端”
移除对象。
@@ Map 在每个槽内保存了两个对象,即键和与之相关联的值。
### Collection 打印出来的内容用方括号括住,每个元素由逗号隔开;
例如: [rat , cat , dog]
Map 打印出来的内容用大括号括住,键与值由等号联系(键在左,值在右)
例如: { 键1 =值1 , 键2=值2 , 键3=值3}
### ArrayList 和 LinkedList 都是 List 类型的。从输入可以看出,它们都
按照被插入的顺序保存元素。两者之间的不同之处不仅仅在于执行某些
类型的操作时的性能,而且 LinkedList 包含的 操作多于 ArrayList 。
### HashSet 、TreeSet 和 LinkedSet 都是 Set 类型,每个相同的项
只有保存一次,但是输出也显示了不同的 Set 实现存储元素的方式也
不同。
HashSet : 是最快的获取元素方式。
TreeSet : 偏向于存储顺序很重要,它按照比较结果的升序保存对象。
LinkedSet : 按照被添加的顺序保存对象。
### Map (也被称为关联数组):使得你可以用键来查找对象。
补充:对于每一个键,Map 只接受存储一次。
### Map.put(key,value) 方法将增加一个值,并将它与某个键关联起来。
Map.get(key) 方法将产生与这个键相关联的值。
注意:你不必考虑 Map 的尺寸,因为它自己会自动地调整尺寸。
### 键和值在 Map 中的保存顺序并不是它们的插入顺序,因为 HashMap
实现使用的是一种非常快的算法来控制顺序。
### Map : HashMap 、TreeMap 和 LinkedHashMap
HashMap : 提供了最快的查找技术,也没有按照任何明显的顺序来保存
其元素;
TreeMap : 按照比较结果的升序保存键;
LinkedHashMap : 按照插入顺序来保存键,同时还保留了 HashMap 的
查找速度
》》List
### List 接口在 Collection 的基础上添加了大量的方法,使得可以在 List 的
中间插入和移除元素。
### 有两种类型的 List :
@@ 基本的 ArrayList , 它长于随机访问元素,但是在 List 的中间插入和
移除元素慢
@@ LinkedList ,它通过代价较低的在 List 中间进行 的插入和删除操作,
提供了优化的顺序访问。LinkedList 在随机访问方面相对比较慢,但是
它的特性集较 ArrayList 更大 。
### 与数组不同,List 允许在它被创建之后添加元素、移除元素,或者自我
调整尺寸。这正是它的重要价值所在:一种可修改的序列。
### 对于其他的类, equals () 的定义可能有所不同。例如,两个
String 只有在内容完全相同的情况下才是等价的。
### 对于 LinkedList ,在列表中间插入和删除都是廉价的操作,但是对于
ArrayList 是代价高昂的操作。
### 优化是一个很棘手的问题,最好的策略就是置之不顾,直到你发现需要
担心它了(尽管理解这些问题总是一种好的思路)
》》迭代器
### 任何容器类,都必须有某种方式可以插入元素并将它们再次返回。毕竟,
持有事物是容器最基本的工作。对于 List , add( ) 是插入元素的方法之一,
而 get( ) 是取出元素的方法之一。
### 迭代器(也是以一种设计模式)是一个对象,它的工作是遍历并选择序列
中的对象,而客户端程序员不必知道或关心该序列底层的结构。
### 迭代器通常被称为轻量级对象:创建它的代价小。因此,经常可以见到
对迭代器有些奇怪的限制;
例如,Java 的 Iterator 只能单向移动,这个 Iterator 只能用来:
(1)、使用方法 iterator() 要求容器返回一个 Iterator 。Iterator 将
准备好返回序列的第一个元素
(2)、使用 next () 获得序列中的下一个元素
(3)、使用 hasNext() 检查序列中是否还有元素
(4)、使用 remove() 将迭代器新近返回的元素删除。
### 有了 Iterator 就不必为容器中元素的数量操心了,那是由 hasNext( )
和 next( ) 关心的事情了。
### 如果你只是向前遍历 List ,并不打算修改 List 对象本身,那么你可以
看到 foreach 语法显得更加简洁。
### Iterator 还可以移除由 next( ) 产生的最后一个元素,这意味着在调用
remove( ) 之前必须先调用 next ( )
### Iterator 能够将遍历序列的操作与序列底层的结构分离。正由于此,
我们有时会说:迭代器统一了对容器的访问方式。
----- ListIterator
### ListIterator 是一个更加强大的 Iterator 的子类型,它只能用于各种
List 类的访问。尽管 Iterator 只能向前移动,但是 ListIrerator 可以
双向移动。它还可以产生相对于迭代器在列表中指向的当前位置和
前一个和后一个元素的索引,并且可以使用 set( ) 方法替换它访问过
的最后一个元素。
### 你可以通过调用 listIterator () 方法产生一个指向 List 开始处的
ListIterator , 并且还可以通过调用 listIterator (n) 方法创建一个一开始
就指向列表索引为 n 的元素处 ListIterator 。
》》LinkedList
### LinkedList 也像 ArrayList 一样实现了基本的 List 接口,但是它执行
某些操作(在 List 的中间插入和移除)时比 ArrayList 更高效,但在
随机访问操作方面却要逊色一些。
### LinkedList 还添加了可以是用作栈、队列或双端队列的方法。
### getFirst( ) 和 element( ) 完全一样,它们都返回元素列表的头(第一个
元素),而并不移除它,如果 List 为空,则抛出 NoSuchElement :
Exception 。
peek( ) 方法与这两个方式只是有些差异,它在列表为空时返回 null 。
### removeFirst() 与 remove() 也是完全一样的,它们移除并返回列表的头,
而在列表为空时抛出 NoSuchElementException 。
poll ( ) 稍有些差异,它在列表为空时返回 null 。
### addFirst( ) 与 add( ) 和 addLast( ) 相同,它们都将某个元素插入到列表
的尾(端)部。
### removeLast( ) 移除并将返回列表的最后一个元素。
### Queue 接口是在 LinkedList 的基础上添加了 element( ) 、offer () 、
peek( ) 、poll( ) 和 remove() 方法,以使其成为一个 Queue 的实现。
》》Stack
### “栈”通常是指“后进先出”(LIFO)的容器。有时栈也被成为叠加栈,
因为最后“压入”栈的元素,第一个“弹出”栈。经常用来类比栈的事物
是装有弹簧的储放器中的自助餐托盘,最后装入的托盘总是最先拿出的。
### LinkedList 具有能够直接实现栈的所有功能的方法,因此可以直接将
LinkedList 作为栈使用。不过,有时一个真正的“栈”更能把事情讲清楚。
### 将 LinkedList 看作一个 栈(Stack) 来使用:
peek( ) 方法将提供栈顶元素,但是不将其从栈顶移除;
poll( ) 将其移除并返回栈顶元素
》》Set
### Set 不保存重复的元素。如果你试图将相同对象的多个实例添加到
Set 中,那么它就会阻止这种重复现象。
### Set 中最常使用的是测试归属性,你可以很容易地询问某个对象是否在
某个 Set 中。正因为如此,查找就成了 Set 中最重要的操作,因此你
通常都会选择一个 HashSet 的实现,它专门对快速查找进行了优化。
### Set 具有与 Collection 完全一样的接口,因此没有任何额外的功能。
### 实际上 Set 就是 Collection ,只是行为不同。
### Set 是基于对象的值来确定归属性的。
### HashSet 所维护的顺序与 TreeSet 或 LinkedHashSet 都不同,因为
它们的实现具有不同的元素存储方式。
@@ TreeSet 将元素存储在 红-- 黑树数据结构中
@@ HashSet 使用的是散列函数
@@LinkedHashSet 因为查询速度的原因也使用了散列,但是看起来
使用了链表来维护元素的插入顺序
###如果你想对结果排序,一种方式是使用 TreeSet 来代替 HashSet 。
》》Map
### 将对象映射到其他对象的能力是一种解决编程问题的杀手锏。
### Map 与数组和其他的 Collection 一样,可以很容易地扩展到多维,而
我们只需要将其值设置为 Map (这些 Map 的值可以是其他容器,甚至
是其他 Map )。因此,我们能够很容易地将容器组合起来从而快速地
生成强大的数据结构。
假如:你正在跟踪拥有多个宠物的人,你所需只是一个
Map<Person , List<Pet>>
### Map 可以返回它的键的 Set ,它的值的 Collection ,或者它的键值对的
Set 。
》》Queue
### 队列是典型的先进先出(FIFO)的容器。即从容器的一端放入事物,从另
一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列常被当作
一种可靠的将对象从程序的某一个区域传输到另一个区域的途径。队列在并发
编程中特别重要,因为它可以安全地将对象从一个任务传输给另一个任务。
### LinkedList 提供了方法以支持队列的行为,并且它实现了 Queue 接口,因此
LinkedList 可以用作 Queue 的一种实现。
### 注意,与 Queue 相关的方法提供了完整而独立的功能。即,对于 Queue 所继承
的 Collection , 在不需要使用它的任何方法的情况下,就可以有一个可用的
Queue 。
----- PriorityQueue (优先级队列)
### 先进先出描述了最典型的队列规则。先进先出声明的是下一个元素应该是等待时间
最长的元素。
### 优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)。
### Integer 、 String 和 Character 可以与 PriorityQueue 一起工作,因为这些类已经
内建了自然排序。如果你想在 PriorityQueue 中使用自己的类,就必须包括额外的
功能以产生自然排序,或者必须提供自己的 Comparator 。
》》Collection 和 Iterator
### Collection 是描述所有序列容器的共性的根接口,它可能会被认为是一个“附属接口”,
即因为要表示其他若干个接口的共性而出现的接口。另外,java.util.AbstractColletion
类提供了 Collection 的默认实现,使得你可以创建 AbstractCollection 的子类型,而
其中没有必要的代码重复。
### 使用接口描述的一个理由是它可以使我们能够创建更通用的代码。通过针对接口而非
具体的实现来编写代码,我们的代码可以应用于更多的对象类型。
### 实现 Collection 就意味着需要提供 iterator() 方法。
### 生成 Iterator 是将队列与消费队列的方法连接在一起耦合度最小的方式,并且与实现
Collection 相比,它在序列类上所施加的约束也少得多。
》》Foreach 与迭代器
### 到目前为止,foreach 语法主要用于数组,但是它也可以应用于任何 Collection 对象。
### 不存在任何从数组到 Iterator 的自动转换,你必须手工执行转换。
------ 适配器方法惯用法
### ”适配器“来自于设计模式。
》》总结
### 数组将数字与对象联系起来。它保存类型明确的对象,查询对象时,不需要对结果做
类型转换。它可以是多维的,可以保存基本类型的数据。但是,数组一旦生成,其容量
就不能改变。
### Collection 保存单一的元素,而 Map 保存相关联的键值对。有了 Java 的泛型,你就
可以指定容器中存放的对象类型,因此你就不会将错误类型的对象放置到容器中,并且
在容器中获取元素时,不必进行类型转换。各种 Collection 和各种 Map 都可以在你向
其中添加元素时,自动调整其尺寸。容器不能持有基本类型,但是自动包装机制会仔细地
执行基本类型到容器中所持有的包装器类型之间的双向转换。
### 像数组一样,List 也建立数字索引与对象的关联,因此,数组和 List 都是排好序的容器。
List 能够自动扩充容量。
### 如果要进行大量的随机访问,就使用 ArrayList ; 如果要经常从表中间插入或删除元素,
则应该使用 LinkedList
### 各种 Queue 以及栈的行为,由 LinkedList 提供支持。
### Map 是一种将对象(而非数字)与对象相关联的设计。HashMap 设计用来快速访问;
而 TreeMap 保持 ” 建 “ 始终处于排序状态,所有没有 HashMap 快。LinkedHashMap
保持元素插入的顺序,但是也通过散列提供了快速访问能力。
### Set 不接受重复的元素。HashSet 提供最快的查询速度,而 TreeSet 保持元素处于排序
状态。 LinkedHashSet 以插入顺序保存元素。
### 新程序中不应该使用过时的 Vector 、 Hashtable 和 Stack 。
### 简单的容器分类图:(如下)
说明:(1)、从上图中可以看出,只有四种容器:Map 、 List 、 Set 、 Queue 。
它们各有两到三个实现版本。常用的容器用黑色粗线线框(黄色背景)表示。
(2)、点线框表示接口,实现框表示普通的(具体的)类。
(3)、带有空心箭头的点线表示一个特定的类实现一个接口;
实心箭头表示可以生成箭头所指向类的对象。
(4)、除了 TreeSet 之外的所有 Set 都拥有与 Collection 完全一样的接口。
(5)、List 和 Collection 存在着明显的不同,尽管 List 所要求的方法都在
Collection 中。
(6)、在 Queue 接口中的方法都是独立的,在创建具有 Queue 功能的实
现时 不需要使用 Collection 方法。
(7)、Map 和 Collection 之间唯一重叠就是 Map 可以使用 entrySet ( )
和 values ( ) 方法来产生 Collection
补充:(1)、标记接口 java.util.RandomAccess 附着到了 ArrayList 上,而没有
附着到 LinkedList 上。这为想要根据所使用的特定的 List 而动态修改其行为
的算法提供了信息。