您的位置:首页 > 编程语言 > Java开发

再读thinking in java -- 第十七章 容器深入研究(二)

2018-03-30 16:37 344 查看
Map中任何键必须有个equals方法,保证键的唯一性。若键被用于HashMap,则必须实现HashCode方法。用于TreeMap必须实现Comparable。LinkedHashMap散列化所有元素,但遍历时,返回插入顺序。
反射实例:利用类型标签Class<t>,type(type是Class对象),type.getConstructor(Class对象),再用得到的对象.newInstance()。
Object中的hashCode方法生成的散列码默认是使用对象地址得到的。
对于判断对象是否相等,仅覆盖hashCode方法是不够的,必须同时覆盖equals方法。它是Object中的一部分。
正确equals方法的五个条件:
1、自反性:x.equals(x);    2、对称性:y.equals(x)为true,那么x.equals(y)也为true;    3、传递性;    4、一致性:比较多少次都是一个结果;    5、对任何不适null的x,x.equals(null)返回false
用equals方法比较应该比较类型(instanceof)以及内容。其中instanceof会检查对象是否为null。
Map.entrySet()必须产生一个Map.Entry(这是一个接口)对象集。
存储一组元素最快的数据结构是数组,所以用它表示键的信息(而不是键本身)。但数组不能调整容量。其实数组并不保存键本身,而是通过键对象生成一个数组(散列码),将其作为数组下标。
不同的键可以产生相同的下标,即可能会有冲突。通常,冲突由外部链接处理。数组不直接保存值,而是保存值的List。然后对List值使用equals方法进行线性查询。(这是链地址法,还有一种是开放地址法,开放地址方法中有三种方法,线性探测(找到对应位置若有元素,则一个一个往下找到空位插入),二次探测(找到相应位置若有元素,则找相邻的元素,若也占有了,找第四个位置,……以平方为查找),再哈希法)
散列表中的槽位(slot)通常称为桶位(bucket),所以将表示实际散列表的数组命名为bucket,桶的数量通常用质数。
设计hashCode方法最重要的因素是:对同一对象调用hashCode方法应生成同样的值,但不应该使hashCode方法具有唯一性的对象信息。尤其使用this的值(比较的是地址的值,而不是内容)
散列码不必是独一无二的,但通过hashCode方法和equals方法必须能完全确定对象的身份。
ArrayList和LinkedList都实现了List接口。ArrayList底层由数组支持,而LinkedList是由双向链表实现。
Set可被实现为HashSet、LinkedHashSet、TreeSet。HashSet最常用,查询速度最快,LinkedHashSet保持元素插入的顺序。TreeSet基于TreeMap,生成一个排序状态的Set。
在列表中插入新的元素,对于ArrayList,当列表变大时,开销将变得很高昂。对于LinkedList较为低廉,且不随列表尺寸变化。
对于随机访问的get方法和set方法,背后有数组支撑的list比ArrayList稍快。避免使用Vector,最好将ArrayList作为首选,当需要从表中间经常插入和删除时,可以选择LinkedList
Math.random()产生[0,1)
HashSet的性能基本上总比TreeSet好,特别是在添加和查询元素时。只有当需要一个排好序的set才会用到TreeSet。TreeSet的迭代通常比HashSet快。
除了IdentityMap,所有Map实现的插入操作都会随Map尺寸变大而明显变慢。
可通过Arrays.binarySearch方法在排序数组中快速查找对象。

HashMap:
容量:表中的桶位数;    初始容量:表创建时所拥有的桶位数,默认为16;    尺寸:表中当前存储的项数;    负载因子:尺寸除以容量,默认为0.75
当负载情况到达负载因子水平,容器自动增加其容量,实现方式是使容器大致加倍,并重新将现有对象分布到新的桶位集中(再散列)
List排序与查询所使用的方法与对象数组所使用的相应方法有相同的名字与语法。
使用Collections.unmodifiableList(参数)创建容器为只读,利用Collections.synchronizedList(新建容器)能实现同步。
快速报错(fail-fast):java容器的一种保护机制,能防止多个进程同时修改同一个容器的内容。
快速报错机制会探查容器上的任何除了进程所进行的操作以外的所有变化。一旦发现其他进程修改了容器,它会立即抛出ConcurrentModificationException异常。其中ConcurrentHashMap,CopyOnWriteArrayList和CopyOnWriteArraySet都使用了避免ConcurrentModificationException。
java.lang.ref类库中包含了三个继承自抽象类Reference的类:SoftReference,WeakReference、PhantomReference,为垃圾回收提供了更大的灵活性。存在可能会耗尽内存的对象时,这些类就很有用。
当GC正在考察的对象只能通过某个Reference对象才“可获得”,不同派生类为GC提供不同级别的简介指示。
对象时可获得的“reachable”,是指对象可在程序中的某处找得到。若一个对象时“可获得的”,GC就不能释放它,因为要被程序使用,如果不是可获得的,那回收就是安全的。
如果想继续持有对某个对象的引用,希望以后还能访问到该对象,但是也希望能允许GC释放它,这时就应该使用Reference对象。这样就可以继续使用该对象,而在内存消耗殆尽的时候又允许释放对象。
SoftReference,WeakReference、PhantomReference,由强到弱排序,对应不同级别的可获得性,第一个用于实现内存敏感的高速缓存,第二个是为实现规范映射而设计的。最后一个用以调度回收前的清理工作。前俩个可以选择是否加入ReferenceQueue。而PhantomReference只能依赖于ReferenceQueue。

ReferenceQueue总是生成一个包含null对象的Reference。

WeakHashMap用来保存WeakReference,它是的规范映射更易使用。这种映射中,每个值只保存一份实例以节省空间。当程序需要那个“值”的时候,便在映射中查询现有的对象,然后使用它(而不是重新创建)。WeakHashMap允许GC自动清理键和值。
Vector是唯一可自我扩展的序列。基本上可以看做是ArrayList。枚举接口比Iterator小,只有两个方法:boolean hasMoreElements()和Object nextElement().
Collections.enumeration()将Collection生成一个Enumeration。基本的HashTable与HashMap很相似。Stack拥有Vector的所有特点和行为。Stack继承自Vector。若要栈的行为,可以应用LinkedList。
如果想高效的存储大量“开关”信息,BitSet是很好的选择。但他的效率只是对空间而言,对于时间,本地数组更快一点。
BitSet的最小容量是long:64位。任何生成的随机数都不会导致BitSet扩充容量。
使用BitSet而不是Enumeration的理由:1、在运行时才知道需要多少个标志;    2、对标志命名不合理;    3、需要BitSet的某种特殊操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: