java编程思想-11持有对象

1.基本概念

Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:
1) Collection。一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复元素。 Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)

2)Map。一组成对的“键值对”对象,允许你使用键来查找值。 Arraylist允许你使用数字来查找值,因此在某种意义上讲,它将数字与对象关联在了一起。映射表允许我们使用另一个对象来查找某个对象,它也被称为“关联数组”,因为它将某些对象与另外一些对象关联在了一起;或者被称为“字典”,因为你可以使用键对象来查找值对象,就像在字典中使用单词来定义一样。Map是强大的编程工具。

Collection接口概括了序列的概念—一种存放一组对象的方式。所有的Collection都可以用foreach语法遍历。

2.添加一组元素

Collection的构造器可以接受另一个 Collection,用它来将自身初始化,因此你可以使用Arrays. List()来为这个构造器产生输入。但是, Collection. addAll()方法运行起来要快得多,而且构建一个不包含元素的 Collection,然后调用 Collections.addAll()这种方式很方便,因此它是首
选方式。
Collection. addAll()成员方法只能接受另一个 Collection对象作为参数,因此它不如Arrays. asList()或 Collections. addAll()灵活,这两个方法使用的都是可变参数列表。
你也可以直接使用 Arrays. asList()的输出,将其当作List,但是在这种情况下,其底层表示的是数组,因此不能调整尺寸。如果你试图用add()或 delete()方法在这种列表中添加或删除元素,就有可能会引发去改变数组尺寸的尝试,因此你将在运行时获得“ Unsupported Operation(不支
持的操作)”错误。

3.容器的打印

你必须使用Arrays.toString()来产生数组的可打印表示。
这里展示了Java容器类库中的两种主要类型,它们的区别在于容器中每个“槽”保存的元素个数。 Collection在每个槽中只能保存一个元素。此类容器包括:List,它以特定的顺序保存一组元素;Set,元素不能重复; Queue,只允许在容器的一“端”插入对象,并从另外一“端”移除对象。Map在每个槽内保存了两个对象,即键和与之相关联的值。

査看输出会发现,默认的打印行为(使用容器提供的 toString方法)即可生成可读性很好的结果。 Collection打印出来的内容用方括号括住,每个元素由逗号分隔。Map则用大括号括住,键与值由等号联系(键在等号左边,值在右边)。
ArrayList和 LinkedList都是List类型,它们都按照被插入的顺序保存元素。两者的不同之处不仅在于执行某些类型的操作时的性能,而且 LinkedList包含的操作也多于ArrayList。

HashSet、 TreeSet和 LinkedHashSet都是Set类型,输出显示在Set中,每个相同的项只有保存一次,但是输出也显示了不同的Set实现存储元素的方式也不同。 HashSet使用的是相当复杂的方式来存储元素的,此刻你只需要知道这种技术是最快的获取元素方式,因此,存储的顺序看起来并无实际意义(通常你只会关心某事物是否是某个Set的成
员,而不会关心它在Set出现的顺序)。如果存储顺序很重要,那么可以使用 TreeSet,它按照比较结果的升序保存对象;或者使用 LinkedHashSet,它按照被添加的顺序保存对象。

Map(也被称为关联数组)使得你可以用键来查找对象,就像一个简单的数据库。键所关联的对象称为值。对于每一个键,Map只接受存储一次。
Map. put(key, value)方法将增加一个值(你想要增加的对象),并将它与某个键(你用来查找这个值的对象)关联起来。 Map. get(key)方法将产生与这个键相关联的值。
注意,你不必指定(或考虑)Map的尺寸,因为它自己会自动地调整尺寸。Map还知道如何打印自己,它会显示相关联的键和值。键和值在Map中的保存顺序并不是它们的插入顺序。因为 HashMap实现使用的是一种非常快的算法来控制顺序。

三种基本风格的Map: HashMap、 TreeMap和 LinkedHashMap。与 HashSet一样, HashMap也提供了最快的查找技术,也没有按照任何明显的顺序来保存其元素。 TreeMap按照比较结果的升序保存键,而 LinkedHashMap则按照插入顺序保存键,同时还保留了HashMap的查询速度。

4.List

List承诺可以将元素维护在特定的序列中。List接口在 Collection的基础上添加了大量的方法,使得可以在List的中间插入和移除元素。
有两种类型的List:
1.基本的 ArrayList,它长于随机访问元素,但是在List的中间插入和移除元素时较慢
2.Linkedlist,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问。 Linkedlist在随机访问方面相对比较慢,但是它的特性集较 ArrayList更大。

可以用contains()方法来确定某个对象是否在列表中。如何想移除一个对象,则可以将这个对象的引用传递给remove()方法。如果你有一个对象的引用,可以使用indexOf()来发现该对象在List中所处位置的索引编号。

扫描二维码关注公众号,回复: 4466096 查看本文章

当确定一个元素是否属于某个List,发现某个元素的索引,以及从某个List中移除一个元素时,都会用到 equals()方法(它是根类 Object的一部分)。对于其他的类, equals的定义可能有所不同。例如,两个 String只有在内容完全一样的情况下才会是等价的。因此为了防止意外,就必须意识到List的行为根据 equals()的行为而有所变化。

在List中间插入元素是可行的,但是这带来了一个问题:对于 LinkedList,在列表中间插入和删除都是廉价操作,但是对于 ArrayList,这可是代价高昂的操作。这是否意味着你应该永远都不要在 Arraylist的中间插入元素,并最好是切换到 Linkedlist?不,这仅仅意味着,你应该意识到这个问题,如果你开始在某个 ArrayList的中间执行很多插入操作,并且你的程序开始变慢,那么你应该看看你的List实现有可能就是罪魁祸首。

subList()方法允许你很容易地从较大的列表中创建出一个片断,而将其结果传递给这个较大的列表的 containsAll()方法时,很自然地会得到true。还有一点也很有趣,那就是我们注意到顺序并不重要,在sub上调用名字很直观的 Collections. sort()和Collection.shuffle()方法,不会影响 containsAll()的结果。 subList()所产生的列表的幕后就是初始列表,因此,对所返回的列表的修改都会反映到初始列表中,反之亦然。

retainAll()方法是一种有效的“交集”操作,请再次注意,所产生的行为依赖于 equals()方法。

removeAll()方法的行为也是基于 equals()方法的。它将从List中移除在参数List中的所有元素。

set()方法的命名显得很不合时宜,因为它与Set类存在潜在的冲突。在此处, replace可能会显得更适合,因为它的功能是在指定的索引|处(第一个参数),用第二个参数替换整个位置的元素。

对于List,有一个重载的 addAll()方法使得我们可以在初始List的中间插入新的列表,而不仅仅只能用 Collection中的 addAl()方法将其追加到表尾。

你可以通过使用 toArray()方法,将任意的 Collection转换为一个数组。这是一个重载方法,其无参数版本返回的是 Object数组,但是如果你向这个重载版本传递目标类型的数据,那么它将产生指定类型的数据(假设它能通过类型检查)。如果参数数组太小,存放不下List中的所有元素, toArray()方法将创建一个具有合适尺寸的数组。

5.迭代器

迭代器(也是一种设计模式)的概念可以用于达成此目的。迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构。此外,迭代器通常被称为轻量级对象:创建它的代价小。因此,经常可以见到对迭代器有些奇怪的限制;
例如,Java的 Iterator只能单向移动,这个 Iterator只能用来:
1)使用方法 terator()要求容器返回一个 Iterator。 Iterator将准备好返回序列的第一个元素。
2)使用 next()获得序列中的下一个元素。
3)使用 hasNext()检查序列中是否还有元素
4)使用 remove()将迭代器新近返回的元素删除。

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

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

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

6.LinkedList

LinkedList也像 ArrayList一样实现了基本的List接口,但是它执行某些操作(在List的中间插入和移除)时比 ArrayList更高效,但在随机访问操作方面却要逊色一些。Linkedlist还添加了可以使其用作栈、队列或双端队列的方法。这些方法中有些彼此之间只是名称有些差异,或者只存在些许差异,以使得这些名字在特定用法的上下文环境中更加适用(特别是在 Queue中)。例如, getFirst()和 element()完全一样,它们都返回列表的头(第一个元素),而并不移除它,如果List为空,则抛出 NoSuchElementException。 peek()方法与这两个方式只是稍有差异,它在列表为空时返回null。

removeFirst与 remove也是完全一样的,它们移除并返回列表的头,而在列表为空时抛出
NoSuchElementException。poll()稍有差异,它在列表为空时返回null。

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

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

7.Stack

栈通常是指后进先出LIFO的容器。有时栈也被称为叠加栈,因为最后压入栈的元素,第一个弹出栈。LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。

类名之后的告诉编译器这将是一个参数化类型,而其中的类型参数,即在类被使用时将会被实际类型替换的参数,就是T。大体上,这个类是在声明“我们在定义一个可以持有T类型对象的 Stack。” Stack是用Linkedlist实现的,而 Linkedlist也被告知它将持有T类型对象。注意, push接受的是T类型的对象,而peek()和pop()将返回T类型的对象。 peek()方法将提供栈顶元素,但是并不将其从栈顶移除,而pop()将移除并返回栈顶元素

如果你只需要栈的行为,这里使用继承就不合适了,因为这样会产生具有 Linkedlist的其他所有方法的类。

如果你想在自己的代码中使用这个 Stack类,当你在创建其实例时,就需要完整指定包名,或者更改这个类的名称;否则,就有可能与 java util包中的 Stack发生冲突。

这两个 Stack具有相同的接口,但是在 java. util中没有任何公共的 Stack接口,这可能是因为在Java1.0中的设计欠佳的最初的 java.util.Stack类占用了这个名字。尽管已经有了 javautil.Stack,但是 Linkedlist可以产生更好的Stack,因此 net. mindview.util.Stack所采用的方式更是可取的。

你还可以通过显式的导入来控制对“首选” Stack实现的选择:
import net.mindview.util. Stack;
现在,任何对 Stack的引用都将选择 net.mindview.util版本,而在选择 java .util. Stack时,必须使用全限定名称。

8.Set

Set不保存重复的元素,如果你试图将相同对象的多个实例添加到set中,那么它就会阻止这种重复现象。Set中最常被使用的是测试归属性,你可以很容易地询问某个对象是否在某个Set中。正因如此,查找就成为了Set中最重要
的操作,因此你通常都会选择一个 HashSet的实现,它专门对快速查找进行了优化。
Set具有与 Collection完全一样的接口,因此没有任何额外的功能,不像前面有两个不同的List。实际上Set就是 Collection,只是行为不同。Set是基于对象的值来确定归属性的。

Hashset使用了散列。 Hashset所维护的顺序与 Treeset或 Linkedhashset都不同,因为它们的实现具有不同的元素存储方式。 Treeset将元素存储在红一黑树数据结构中,而Hashset使用的是散列函数。Linkedhashlist因为査询速度的原因也使用了散列,但是看起来它使用了链表来维护元素的插入顺序。如果想对结果排序,一种方式是使用TreeSet代替HashSet。

9.Map

如果键不在容器中,get()返回null,否则将返回与改键相关联的值。可以使用containsKey()和containsValue()来查看map是否包含某个键或某个值。Map的值可以是其他容器,甚至是其他Map。Map可以返回它的键的Set,它的值的Collection,或者它的键值对的Set。

10.Queue

队列是一个典型的先进先出(FIFO)的容器。即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列常被当作一种可靠的将对象从程序的某个区域传输到另一个区域的途径。队列在并发编程中特别重要因为它们可以安全地将对象从一个任务传输给另一个任务。

Linkedlist提供了方法以支持队列的行为,并且它实现了 Queue接口,因此 Linkedlist可以用作 Queue的一种实现。通过将 Linkedlist向上转型为 Queue。

offer()方法是与 Queue相关的方法之一,它在允许的情况下,将一个元素插入到队尾,或者返回 false。 peek()和 element()都将在不移除的情况下返回队头,但是 peek()方法在队列为空时返回null,而 element()会抛出 Nosuchelementexception异常。poll()和 remove()方法将移除并返回队头,但是poll()在队列为空时返回null,而 remove()会抛出 Nosuchelementexception异常。

自动包装机制会自动地将 nextInt()方法的int结果转换为 queue所需的 Integer对象,将 char c转换为qc所需的 Character对象。 Queue接口窄化了对 Linkedlist方法的访问权限,以使得只有恰当的方法才可以使用,因此,你能够访问的 Linkedlist的方法会变少。

注意,与 Queue相关的方法提供了完整而独立的功能。即,对于 Queue所继承的 Collection,在不需要使用它的任何方法的情况下,就可以拥有一个可用的 Queue。

Priority Queue
先进先出描述了最典型的队列规则。队列规则是指在给定一组队列中的元素的情况下,确定下一个弹出队列的元素的规则。先进先出声明的是下一个元素应该是等待时间最长的元素。

优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)。例如,在飞机场,当飞机临近起飞时,这架飞机的乘客可以在办理登机手续时排到队头。如果构建了一个消息系统,某些消息比其他消息更重要,因而应该更快地得到处理,那么它们何时得到处理就与它们何时到达无关。

Priorityqueue添加到 Java SE5中,是为了提供这种行为的一种自动实现。当你在 Priority Queue上调用 offer()方法来插入一个对象时,这个对象会在队列中被排序。默认的排序将使用对象在队列中的自然顺序,但是你可以通过提供自己的 Comparator来修改这个顺序。 Priority Queue可以确保当你调用 peek()、poll()和 remove()方法时,获取的元素将是队列中优先级最高的元素

重复是允许的,最小的值拥有最高的优先级(如果是 String,空格也可以算作值,并且比字母的优先级高)。
Integer、 String和 Character可以与 Priority Queue一起工作,因为这些类已经内建了自然排序。如果你想在 Priority Queue中使用自己的类,就必须包括额外的功能以产生自然排序,或者必须提供自己的 Comparator

11.Collection和Iterator

Collection是描述所有序列容器的共性的根接口,它可能会被认为是一个“附属接口”,即因为要表示其他若干个接口的共性而出现的接口。另外,java. util. AbstractCollection类提供了Collection的默认实现,使得你可以创建 AbstractCollection的子类型,而其中没有不必要的代码重复。

我们注意到标准C+类库中并没有其容器的任何公共基类—容器之间的所有共性都是通过迭代器
达成的。在Java中,遵循C++的方式看起来似乎很明智,即用迭代器而不是 Collection来表示容
器之间的共性。但是,这两种方法绑定到了一起,因为实现 Collection就意味着需要提供
iterator()方法。

Collection更方便一点,因为它是 Iterable类型,因此,在 display( Collection)实现中,可以使用 foreach结构,从而使代码更加清晰。当你要实现一个不是 Collection的外部类时,由于让它去实现 Collection接口可能非常困难或麻烦,因此使用 Iterator就会变得非常吸引人。

remove()方法是一个“可选操作”。

如果你实现 Collection,就必须实现 iterator,并且只拿实现iterator与继承 AbstractCollection相比,花费的代价只有略微减少。但是,如果你的类已经继承了其他的类,那么你就不能再继承 AbstractCollection了。在这种情况下,要实现 Collection,就必须实现该接口中的所有方法。此时,继承并提供创建迭代器的能力就会显得容易得多了。

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

12.Foreach与迭代器

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

iterator()方法返回的是实现了 Iterator< String>的匿名内部类的实例,该匿名内部类可以遍历数组中的所有单词。在 Java se5中,大量的类都是 Iterable类型,主要包括所有的 Collection类(但是不包括各种Map)。

foreach语句可以用于数组或其他任何 Iterable,但是这并不意味着数组肯定也是一个 Iterable,而任何自动包装也不会自动发生。尝试把数组当做一个Iterable参数传递会导致失败。这说明不存在任何从数组到Iterable的自动转换,你必须手工执行这种转换。

13.总结

Java提供了大量持有对象的方式:

1)数组将数字与对象联系起来。它保存类型明确的对象,查询对象时,不需要对结果做类型转换。它可以是多维的,可以保存基本类型的数据。但是,数组一旦生成,其容量就不能改变。

2) Collection保存单一的元素,而Map保存相关联的键值对。有了Java的泛型,你就可以指定容器中存放的对象类型,因此你就不会将错误类型的对象放置到容器中,并且在从容器中获取元素时于,不必进行类型转换。各种 Collection和各种Map都可以在你向其中添加更多的元素时,自动调整其尺寸。容器不能持有基本类型,但是自动包装机制会仔细地执行基本类型到容器中所持有的包装器类型之间的双向转换。

3)像数组一样,List也建立数字索引与对象的关联,因此,数组和List都是排好序的容器,List能够自动扩充容量。

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

5)各种 Queue以及栈的行为,由 Linkedlist提供支持。

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

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

8)新程序中不应该使用过时的 Vector、 Hashtable和 Stack。

浏览一下Java容器的简图(不包含抽象类和遗留构件)会大有裨益。这里只包含你在一般情况下会碰到的接口和类。

这里写图片描述

你可以看到,其实只有四种容器:Map、List、Set和 Queue,它们各有两到三个实现版本( Queue的 java. util. concurrent实现没有包括在上面这张图中)。常用的容器用黑色粗线框表示。

点线框表示接口,实线框表示普通的(具体的)类。带有空心箭头的点线表示一个特定的类实现了一个接口,实心箭头表示某个类可以生成箭头所指向类的对象。例如,任意的Collection可以生成 Iterator,而List可以生成Listiterator(也能生成普通的 lterator,因为List继承自 Collection)。

可以看到,除了 Treeset之外的所有Set都拥有与 Collection完全一样的接口。List和Collection存在着明显的不同,尽管List所要求的方法都在 Collection中。另一方面,在 Queue接口中的方法都是独立的;在创建具有 Queue功能的实现时,不需要使用 Collection方法。最后,Map和 Collection之间的唯一重叠就是Map可以使用 entrySet()和 values()方法来产生 Collection。

注意,标记接口 java.util.RandomAccess附着到了 Arraylist上,而没有附着到 Linkedlist上。这为想要根据所使用的特定的Ls而动态修改其行为的算法提供了信息。

猜你喜欢

转载自blog.csdn.net/qq_26462167/article/details/79270412