java学习笔记(十二)——容器深入

12.1 填充容器

就像Arrays一样,相应的Collections类也有一些实用的static方法,其中包括fill()。与Arrays版本一样,此fill()方法也是只复制同一个对象用来填充整个容器的,并且只对List对象有用,但是所产生的列表可以传递给构造器或addAll方法。

12.1.1 一种generator解决方案

事实上,所有的Collection子类型都有一个接收另一个Collection对象的构造器,用所接收的Collection对象中的元素来填充新的容器。为了更加容易地创建测试数据,要构建接收Generator和quantity数值并将它们作为构造器参数的类。

11.1.2 使用abstract类

对于产生用于容器的测试数据问题,另一种解决方式是创建定制的Collection和Map实现每个java.util容器都有其自己的Abstract类,它们提供了该容器的部分实现,因此必须去实现那些产生想要的容器所必需的方法。如果所产生的容器是只读的,就像它通常用的测试数据那样,那么你需要提供的方法数量将减少到最少。

可以在普通的解决方案需要过多的对象,或者产生普通对象太占用空间时使用享元。享元模式使得对象的一部分可以被具体化,因此,与对象中所有事物都包含在对象内部不同,可以在更加高效的外部表中查找对象的一部分或整体(或者通过某些其他节省空间的计算来产生对象的一部分或整体)。

12.2 可选操作

执行各种不同的添加和移除的方法在Collection接口中都是可选操作。这意味着实现类并不需要为这些方法提供功能定义。

这是不寻常的接口定义方式。接口死面向对象设计中的契约,它声明无论你选择如何实现该接口,保证可以向该接口发送这些消息。但是可选操作违反这个基本的原则,它声明调用某些方法将不会执行有意义的行为,相反,它们会抛出异常。这看起来好像抛弃了编译期类型安全。

但并非如此,如果一个操作是可选的,编译器仍旧会严格要求你只能调用该接口中的方法。这与动态语言不同,动态语言可以在任何对象上调用任何方法,并且可以在运行时发现某个特定调用是否可以工作。另外,Collection当作参数接受的大部分方法只会从该Collection中读取,而Collection的读取方法都是不可选的。

将方法定义为可选是因为这样做可以防止在设计中出现接口爆炸的情况。容器类库中的其他设计看情况总是为了描述每个主题的各种变体,这样导致接口过剩。甚至这么做仍不能捕捉接口的各种特例,因为总是有人会发明新接口。“未获支持的操作”这种方式可以实现Java容器类库的一个重要目标:容器一个易学易用。未获支持的操作是一种特例,可以延迟到需要时再实现。

12.2.1 未获支持的操作

最长久的未获支持的操作,都来源于背后由固定尺寸的数据结构支持的容器。当你用Arrays.asList()将数组转换为List时,就会得到这样的容器。你还可以通过使用Collections类中的“不可修改”的方法,选择创建任何会抛出UnsupportedOperationException的容器(包括Map)。

12.3 List功能方法

基本List很容易使用:add()添加对象,get()一次取出一个元素,以及调用iterator()获取用于该序列的Iterator。

12.4 Set和存储顺序

如果没有其他限制,HashSet就应该是默认的选择,因为它对速度进行了优化。

定义hashCode()的机制,必须为散列存储和树形存储都创建一个equals()方法,但是hashCode()只有在这个类将会被置于HashSet或者LinkedHashSet中时才是必须的。但是,对于良好的编程风格而言,应该在覆盖equals()方法时,总是同时覆盖hashCode()方法。

12.5 队列

除了并发应用,Queue仅有两个实现是LInkedList和PriorityQueue,它们的差异在于排序行为而不是性能。

12.5.1 优先队列

其中有to-do列表,该列表中的每个对象都包含一个字符串和一个主要的以及次要的优先级值。该列表的排序顺序也是通过实现Comparable进行控制的。

12.5.2 双向队列

双向队列可以在任何一端添加或移除元素。在LinkedList中包含支持双向队列的方法,但是在Java标准库中没有任何显式的用于双向队列的接口,因此LinkedList无法实现这样的接口。

12.6 Map

映射表(也称为关联数组)的基本思想是维护键值关联,因此可以用键来查找值。标准的Java类库中包含了Map的几种实现:HashMap, TreeMap, LinkedHashMap, WeakHashMap, ConcurrentHashMap, IdentityHashMap。它们都有同样的基本接口Map,但是行为特性各不相同,这表现在效率、键值对的保存及呈现次序、对象的保存周期、映射表如何在多线程程序中工作和判定键等价的策略等方面。

12.6.1 性能

性能使映射表中的一个重要问题,当使用get()进行线性搜索时,执行速度会相当慢,HashMap在此提升了速度。HashMap使用散列码,来取代键值对的搜索。散列码是相对唯一的,用以代表对象的int值,它是通过将该对象的某些信息进行转换而成。hashCode()是根类Object中的方法,因此所有的Java对象都能产生散列码。HashMap就是使用对象的hashCode()进行快速查询的,此方法能够显著提高性能。它应当成为我们的默认选择。

12.6.2 SortedMap

使用SortedMap(TreeMap是其唯一实现),可以确保键处于排序状态。

12.6.3 LinkedHashMap

为了提高速度,LinkedHashMap散列化所有元素,但是在遍历键值对时,却又以元素的插入顺序返回键值对。此外,在构造器中设定此,使之采用基于访问的LRU算法,于是没有被访问过的元素就会出现在队列的前面。对于需要定期清理元素以节省空间的程序员来说,此功能使得程序很容易得以实现。

12.7 持有引用

java.lang.ref类库包含了一组类,这些类为垃圾回收提供了更大的灵活性。当存在可能会耗尽内存的大对象的时候,这些类发挥作用。有三个继承自抽象类Reference的类:SoftReference/WeakReference/PhantomReference。当垃圾回收器正在考察的对象只能通过某个Reference对象才可获得时,上述这些不同的派生类为垃圾回收器提供了不同级别的间接性指示。

对象是可获得的,是指对象可在程序中的某处找到。这意味着在栈中有一个普通的引用,而它正指向此对象;有可能引用指向某对象,而那个对象含有另一个引用指向正在讨论的对象;也可以有更多中间链接。如果一个对象是可获得的,垃圾回收器就不能释放它,因为它仍然为你的程序所用。如果一个对象不是可获得的,那么程序将无法使用它,所以可以回收。

如果想继续持有对某个对象的引用,希望以后可以访问到该对象,但是也希望能够允许垃圾回收器释放它,这时就应该使用Reference对象。

以Reference对象作为你和普通引用之间的媒介,一定不能有普通引用指向那个对象(普通引用是指没有经Reference对象包装过的引用),这样就能达到上述目的。如果连接回收器发现某个对象通过普通引用是可获得的,该对象就不会释放。

12.7.1 WeakHashMap

它用来保存WeakReference。它使得规范映射更易于使用。在这种映射中每个值值保存一份实例以节省存储空间。当程序需要哪个值的时候,便在映射中查询现有的对象,然后使用它(而不是重新再创建)。映射可将值作为其初始化的一部分,不过通常是需要的时候才生成。

这是一种节约存储空间的技术,因为WeakHashMap运行垃圾回收器自动清理键和值,所以它显得十分便利。对于想其中添加键和值的操作,则没有什么特殊要求。映射会自动使用WeakReference包装它们。允许清理元素的触发条件是,不再需要此键了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值