Thinking in Java:容器深入研究

1.虚线框表示Abstract类,图中大量的类的名字都是以Abstract开头的,它们仅仅是部分实现了特定接口的工具,因此创建时能够选择从Abstract继承。

Collections中的实用方法:挑几个经常使用的:

1. reverse(List):逆转次序

2. rotate(List,int distance)全部元素向后移动distance个位置,将末尾元素循环到前面来(用了三次reverse)

3.sort(List,Comparator) 排序,可依照自己的Comparator

4.copy(List dest,List src) 复制元素(引用)

5.fill(List,T x)同Arrays一样。复制的是同一引用

6.disjoint(Collection。Collection) 两个集合没有不论什么元素时返回ture

7.frequency(Collection, Object x)返回Collection中等于x的元素个数

8.binarySearch()折半查找(要求有序)

Collection的功能方法:

boolean add(T) 可选方法。若没将參数加入进容器,则false

boolean addAll(Collection )可选方法,仅仅要加入了随意元素就true

void clear() 可选方法

boolean contains(T) 若容器中已经持有具有泛型T參数。true

Boolean containsAll(Collection )

boolean isEmpty()

Iterator iterator()

Boolean remove(Object) 可选方法

boolean removeAll(Collection) 可选方法

Boolean retainAll(Collection) 容器交集,可选方法

int size()

Object[] toArray()

T[] toArray(T[] a)返回包括容器中全部元素的数组

PS:这里不包括随机訪问所选择元素的get()方法,由于Collection包括Set。而Set是自己维护内部顺序的(这使得随机訪问变得没有意义)

2上述执行各种不同的加入和移出的方法在Collection接口中都是可选操作,意味着实现类并不须要为这些方法提供功能定义

未获得支持的操作

1.Arrays.asList()会生成一个List,它基于一个固定大小的数组。仅支持哪些不会改变数组大小的操作,不论什么会引起对底层数据结构的尺寸进行改动的方法都会产生一个UnsupportedOperationException异常(这里Arrays.asList返回的是List的代理类)。若想生成循序使用全部的方法的普通容器,能够将其作为參数传入到新的容器中(如addAll,或构造器等)

2Collections类中的“不可改动”方法(unmodified*方法)将容器包装到了一个代理中,该代理类不支持不论什么试图改动容器的操作

Set和存储顺序:

1:Set(interface):加入Set的元素必须定义equals()方法以确保对象的唯一性,Set接口不保证维护元素的次序

2:HashSet:存入HashSet的元素必须定义hashCode()

3:TreeSet:保证有序,元素必须实现Comparable接口

4:LinkedHashSet 具有HashSet的查询速度,且内部使用链表维护元素的顺序,元素必须定义hashCode()

尽管hashCode不是必须实现的。可是对于良好的编程风格而言,应该在覆盖equals方法时,总是同一时候覆盖hashCode()方法。

3.假设一个对象被用于不论什么种类的排序容器中。如SortedSet(TreeSet是其唯一实现),则其必须实现Comparable接口

PS:在接口的实现方法compareTo()中,不应该使用return i-i2这种形式,错误编程。由于这样没有考虑到i-i2数值溢出的问题,应该

return (arg.i < i ?

-1 : (arg.i == i ? 0 : 1))

4若在HashSet容器中元素并没有又一次定义hashCode(),将它们放置到不论什么散列实现中都可能会产生反复值,这甚至不会产生执行时错误。

唯一的可靠方法就是写单測。

5.SortedSet的意思为“依照对象的比較函数对元素排序”,而不是“元素插入的次序”。

6.优先级队列。其元素排序顺序也是通过实现Comparable而进行控制的。

理解Map:

1:HashMap : 基于散列表的实现。

能够通过构造器设置容量和负载因子调整容器性能。

2:LinkedHashMap :与HashMap却别在于迭代遍历时。取得键值对顺序是插入次序。或是LRU次序。除了迭代訪问时其余性能要慢一点

3:TreeMap:基于红黑树的实现。查看“键”或者“键值对”的时候,它们会被排序(依照Comparable方法),其唯一的带有subMap()方法的Map,能够返回一个子树。

4:WeakHashMap:弱键映射。同意释放映射所指向的对象;假设映射之外没有引用指向某个“键”,则此键能够被垃圾收集器回收

5:IdentityHashMap:使用==取代equals()对”键“进行比較的散列映射

PS:不论什么键都必须具有一个equals方法。假设键被用于散列Map。则还必须有一个恰当的hashCode方法。若键被用于TreeMap,则它必须实现Comparable

7.map中能够直接打印values方法的结果,该方法会产生一个包括Map中全部值得Collection。由于这些Collection背后是由Map支持的,所以对Collection的不论什么改动都会反映到与之相关联的Map。

8.LinkedHashMap散列化全部的元素,可是在遍历键值对时,却又以元素的插入顺序返回键值对。此外,能够在构造器中设定LinkedHashMap,使之採用基于訪问的LRU算法,于是没有被訪问过的元素就会出如今队列的前面。对于须要定期清理元素以节省空间的程序来说,此功能使得程序非常easy得以实现。

散列与散列码:

1.Object的hashCode()方法生成散列码,而它默认是使用对象的地址计算散列码。

2.可能你会觉得,仅仅需编写恰当的hashCode()方法的覆盖版本号就可以。可是它仍然无法正常执行,除非你同一时候覆盖equals()方法。他也是Object的一部分。

HashMap使用equals()推断当前的键是否与表中存在的键同样。

PS:正确的equals()方法必须满足下列5个条件:

1)自反性:x.equals(x)一定返回true

2)对称性:x.equals(y)返回true当且仅当y.equals(x)

3)传递性:x.equals(y)且y.equals(z)。则x.equals(z)为true

4)一致性:若x.equals(y)返回true,则不改变x,y时多次调用x.equals(y)都返回true

5)对于随意的非空引用值x。x.equals(null)一定返回false。

为了速度而散列:

1.由于瓶颈位于键的查询速度。因此解决方式之中的一个就是保持键的排序状态,然后使用Collections。binarySearch()进行查询

2.散列则更进一步,它将键保存在某处,以便能够非常快找到。

存储一组元素最快的数据结构是数组。所以使用它来表示键的信息。信息就是散列码。

3.通常。冲突由外部链接处理,数组并直接保存至,而是保存值得list,然后对list中的值使用equals方法进行线性查询。

4.进来,Java的散列函数都使用2的整数次方。

对现代的处理器来说,除法与求余数是最慢的操作。使用2的整数次方长度的散列表,可用掩码取代除法。由于get是使用最多的操作。求余数的%操作是其开销最大的部分。而是用2的整数次方能够消除此开销。

9.设计hashCode()时最重要的因素就是:不管何时。对同一个对象调用hashCode()都应该生成同样的值

若你的hashCode()方法依赖于对象中易变的数据,用户就要当心了。

同一时候,也不应该使hashCode()依赖于具有唯一性的对象信息,尤其是使用this的值,这仅仅能产生非常糟糕的hashCode()由于这样无法生成一个新的键使之与put中原始的键值对中的键同样。

10.要想使hashCode使用。他必须速度快,而且有意义,也就是说,他必须基于对象的内容生成散列码

散列码不必是独一无二的。可是通过hashCode()和equals,必须能够全然确定对象的身份。

性能:

1.应该避免使用Vector(能正常工作的唯一原因,仅仅是为了向前兼容,被适配成了List)

2TreeSet迭代通常比用HashSet要快。另外。对于插入操作,LinkedHashSet比HashSet代价更高,这是由维护链表所带来额外的开销造成的

3.Hashtable的性能大体上与HashMap相当。

由于HashMap是用来替代Hashtable的。

因此它们使用了同样的底层存储和查找机制。

4.IdentityHashMap则具有全然不同的性能,由于它使用==而不是equals来比較元素。

HashMap的性能因子:

容量:表中桶位数(slot)

初始容量:表在创建时所拥有的桶位数。HashMap和HashSet都具有同意你仅仅能初始容量的构造器。

尺寸:表中当前存储的项数。

负载因子:尺寸/容量。

HashMap和HashSet都具有同意你指定负载因子的构造器。表示负载情况达到该负载因子的水平时,容器将自己主动加入其容量(桶位数)。实现方式是使容量大致加倍,并又一次将现有对象分布到新的桶位数集中(这被称为再散列)

Collection或Map的同步控制:

1.Collections类有办法能够自己主动同步整个容器。其语法与“unmodified*”类似。

List a = Collections.synchronizedList(new ArrayList(data));

最好如上所看到的,是直接将新生成的容器传递给了适当的“同步”方法。这样做就不会有不论什么机会会暴漏出不同步的版本号。

高速报错:

Java容器有一种保护机制,能够防止多个进程同一时候改动同一个容器的内容。Java容器类类库採用高速报错(fail fast)机制。

它会探查容器上的不论什么除了你的进程所进行的操作以外的全部变化,一旦发现其它进程改变了容器,就会立马抛出ConcurrentModificationException异常。如:在容器取得迭代器之后,又有东西呗放入到了该容器中

ConcurrentHashMap,CopyOnWriteArrayList、CopyOnWriteArraySet都使用了能够避免ConcurrentModificationException的技术

持有引用:

1.java.lang.ref类库包括一组类。当存在可能会耗尽内存的大对象的时候,这些类显得特别实用。有SoftReference、WeakReference、PhantomReference。

当垃圾回收期正在考察的对象仅仅能通过某个Reference对象才“可获得时”,上述这些不同的派生类为垃圾回收器提供了不同级别的间接性指示

2.假设想继续持有对某个对象的引用。希望以后还能够訪问到该对象。可是也希望能够同意垃圾回收器释放它,这时就应该使用Reference对象。这样,你能够继续使用对象,而在内存消耗殆尽的时候又同意释放该对象。

3.以Reference对象作为你和普通引用之间的媒介(代理),另外一定不能有普通的引用指向那个对象。这样就能达到上述目的

4.对于SoftReference、WeakReference、PhantomReference三个引用的差别和功能,详见JVM.

5.使用SoftReference、WeakReference时,能够选择是否要将它们放入ReferenceQueue(用作“回收前清理工作”的工具,见JVM)。而PhantomReference仅仅能依赖于ReferenceQueue。

6.WeakHashMap:见JVM。它被用来保存WeakReference。这该映射中,每一个值仅仅保存一份实例以节省存储空间。且WeakHashMap同意垃圾回收器自己主动清理键和值。所以它显得十分便利。

Java 1.0/1.1容器(了解):

1.Enmuberation:老版本号的迭代器。其接口比Iterator小,可是不管怎样代码中应该尽量使用Iterator。能够通过使用Collections.enumeration()方法来从Collection生成一个Enumeration

2.Hashtable:如前所说,主要的Hashtable与HashMap非常类似。

尽量使用HashMap(多线程同步时仍然有非常多其它的选择)

3.BisSet:假设想要高效率地存储大量“开/关”信息,BitSet是非常好的选择。只是它的效率仅是对空间而言。假设须要高效訪问时间,BitSet比本地数组稍慢一些,BitSet最小容量是Long:64位,若存储的内容比較小,如8位。则BitSet浪费了一些空间。BitSet也会想普通容器一样随着元素的加入而扩充其容量。总的来说。若拥有一个能够命名的标志集合。那么EnumSet一般是一种更好的选择,由于EnumSet同意你依照名字而不是数字位的位置进行操作,因此能够降低错误。

时间: 2024-10-04 23:11:17

Thinking in Java:容器深入研究的相关文章

java 容器深入研究之使用Abstract类定制

package org.rui.collection3.map; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.Linke

容器深入研究 --- 散列与散列码(一)

通常的: 当标准类库中的类被作用HashMap的键.它用的很好,因为它具备了键所需的全部性质. 当你自己创建用作HashMap的键的类,有可能会忘记在其中放置必须的方法,而这时通常会犯的一个错误. 例如:考虑一个天气系统,将Groundhog对象与Prediction对象联系起来. class Groundhog { protected int number; public Groundhog(int n) { number = n; } public String toString() { r

容器深入研究 --- 散列与散列码(二)

为速度而散列: SlowMap.java说明了创建一个新的Map并不困难.但正如它的名称SlowMap所示,它不会很快,如果有更好的选择就应该放弃它.它的问题在于对键的查询,键没有按照任何特定的顺序保存,所以只能使用简单的线性查询,而线性查询是最慢的查询方式. 散列的价值在于速度: 散列使得查询得以快速进行.由于瓶颈在于键的查询速度,因此解决方案之一就是保持键的排序状态,然后使用Collections.binarySearch()进行查询. 散列则更进一步,它将键保存在某处,以便能够很快的找到.

容器深入研究 --- 理解Map

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

JAVA 虚拟机深入研究(三)——Java内存区域

JAVA 虚拟机深入研究(一)--关于Java的一些历史 JAVA 虚拟机深入研究(二)--JVM虚拟机发展以及一些Java的新东西 JAVA 虚拟机深入研究(三)--Java内存区域 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的围城,城外的人想进去,城里的人想出来. Java运行的时候会把内存分为若干个,他们各有各的用途,每块区域的创建和销毁都是相对独立的,有的跟虚拟机一起混,有的则抱着用户的大腿同生共死. 按照第七版的<Java虚拟机规范>规定,JVM所管理的内存包括以下

Java 容器 & 泛型:一、认识容器

Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket 容器是Java语言学习中重要的一部分.泥瓦匠我的感觉是刚开始挺难学的,但等你熟悉它,接触多了,也就"顺理成章"地知道了.Java的容器类主要由两个接口派生而出:Collection和Map. 一.Collection vs Collections 首先,Collection 和 Collections 是两个不同的概念.之所以放在一起,是为了更好的比较.Collection是容器层次结构中

容器深入研究 --- 散列与散列码(三)

如何覆盖hashCode(): 明白了如何散列之后,编写自己的hashCode()就更有意义了. 首先,你无法控制bucket数组的下标值的产生.这个值依赖于具体的HashMap对象的容量,而容量的改变与容器的充满程度和负载因子有关.hashCode()生成的结果,经过处理后称为桶位的下标. 设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值.如果在将一个对象用put()添加进HashMap时产生一个hashCode()值,而用get()

C#和Java交互相关研究

之前总觉得C#和Java可以交互应用,但是由于时间以及其他方面的原因,一直没有调研.今天抽空搜了一下,终于折腾出来了. 以下是我自己就C#和Java整合的一些提问和分析,如果有不对的地方,请路过的各位大虾给予指出.3Q! 问题来了: 1.C#和Java如何整合? 2.为什么C#(.Net)要和Java整合? 3.Java和C#整合时,Java主要扮演什么角色?C#扮演什么角色? 答案一一揭晓: 1.C#和Java整合大致有4条路可以走. 1.1 把Java包转换为DLL或者EXE后注册为com组

Java 容器

在实际问题中我们经常需要处理数据,单纯依靠数组来存储数据对开发来说非常困难,java提供了一套容器来方便我们编程.对java容器有一个整体的了解对我们来说非常重要,这样在需要特定容器时,不会手忙脚乱,本文主要介绍java的一些基本容器,而不要仅知道使用ArrayList.下面的图片是java中的简单容器分类   --图片来自网络 java定义了四种容器类型,List.Set.Queue和Map. 其中List.Set.Queue都实现了Collection接口,下面来看看这4中类型的容器. Co