P474)Arrays.asList()会生成一个List,它基于一个固定大小的数组,仅支持那些不会改变数组大小的操作。任何会引起底层数据结构的尺寸进行修改的方法都会产生一个UnsupportedOperationException异常,以表示对未获支持操作的调用。
应该把Arrays.asList()的结果作为构造器的参数传递给任何Collection(或者使用addAll()方法,或Collection.addAll()的静态方法),这样可以生成允许使用所有的方法的普通容器。Collections类中的“不可修改”的方法将容器包装到了一个代理中,只要你执行任何试图修改容器的操作,这个代理都会产生UnsupportedOperationException异常。
P477)Set
Set(interface) | Set不保存重复元素,加入Set的元素必须定义equals()方法以确保对象的唯一性。Set和Collection有完全一样的接口。Set接口不保证维护元素的次序。 |
HashSet | 查找速度块。存入元素必须定义hashCode()。 |
TreeSet | 保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。元素必须实现Comparable接口。 |
LinkedHashList | 具有HashSet的查询速度,且内部用链表维护元素的顺序(插入的次序)。元素也必须定义hashCode()方法。 |
P479)在CompareTo()中比较两个整型数值的大小时,不要用(i1 - i2)作为返回值(除非有unsigned int,但Java没有),因为一个大正数减一个大负数可能会溢出导致返回一个大负数(老实用<、>和==)。
P485)Map
HashMap | Map基于散列表的实现(它取代了HashTable)。插入和查询“键值对”的开销是固定的。可以通过构造器设置容量和负载因子。以调整容器的性能。 |
LinkedHashMap | 类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点;而在迭代访问是反而更快,因为它使用链表维护内部次序。 |
TreeMap | 基于红黑树的实现。查看“键”或“键值对”时,他们会被排序(次序由Comparable或者Comparator决定)。TreeMap的特点在于,所得到的结构是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。 |
P489)散列和散列码
Object的hashCode()方法生成散列码默认是使用对象的地址计算散列码。同时,Object.equals()只是比较对象的地址。因此,如果要用自己的类作为HashMap的键,必须同时重载 hashCode()和equals()。
P496)覆盖hashCode()
要想hashCode()实用,它必须速度块,并且必须有意义。也就是说,它必须基于对象的内容生成散列码(应该更关注生成速度,而不是唯一性)。但是通过hashCode()和equals(),必须能够完全确定对象的身份。另外,好的hashCode()应该产生分布均匀的散列码。以下是写出像样hashCode()的基本指导:
1)给个int变量result赋予某个非零值常量
2)为对象内每个有意义的域f计算出一个int散列码c:
boolean | c = (f ? 0 : 1) |
byte、char、short或int | c = (int) f |
long | c = (int) (f ^ (f >>> 32)) |
float | c = Float.floatToIntBit(f); |
double | long l = Double.doubleToLongBits(f); c = (int) (I ^ (I >>> 32)) |
Object,其equals()调用这个域的equals() | c = f.hashCode() |
数组 | 对每个元素应用上述规则 |
3)合并计算得到的散列码: result = 37 × result + c;
4)返回result。
5)检查hashCode()最后生成的结果,确保相同的对象有相同的散列码。
P499)选择接口的不同实现
ArrayList底层由数组支持;而LinkedList是由双向链表实现的,其中的每个对象包含数据的同时还包含指向链表中前一个与后一个元素的引用。因此,如果要经常在表中插入或删除元素,LinkedList就比较适合(LinkedList还有建立在AbstractSequentialList基础上的其他功能);否则,应该使用速度更快的ArrayList。
P507)Math.random()的范围是[0,1);
P512)Collections类内部的静态方法
rotate(List, int distance):所有元素向后移动distance个位置,将末尾的元素循环到前面来。
swap(List, int i, int j):交换List中位置i与位置j的元素。通常比你自己写的块。
P515)设定Collection或Map为不可修改
Collections.unmodifiableCollection(Collection);
Collections.unmodifiableList(List);
Collections.unmodifiableSet(Set);
Collections.unmodifiableSortedSet(Set);
Collections.unmodifiableSortedMap(Map);
无论哪一种,在将容器设为只读之前,必须填入有意义的数据。
P517)快速报错
Java容器有一种保护机制,能够防止多个进程同时修改同一个容器的内容。如果你在迭代遍历某个容器的过程中,另一个进程介入其中,并且插入、删除或修改此容器内的某个对象,那么就会出现问题。
Java容器类类库采用快速报错机制。它会探查容器上的任何除了你的进程所进行的操作以外的所有变化,一旦它发现其他进程修改了容器,就会立刻抛出ConcurrentModificationException异常。
ConcurrentMap、CopyOnWriteArrayList、CopyOnWriteArraySet都使用了可以避免ConcurrentModificationError的技术。
P518)持有引用
java.lang.ref类库包含了一组类,这些类为垃圾回收提供了更大的灵活性。当存在可能会耗尽内存的大对象的时候,这些类显得特别有用。有三个继承自抽象类Reference的类:SoftReference、WeakReference和PhantomReference。
如果想继续持有某个对象的引用,希望以后还能够访问到对象,但是也希望能够允许垃圾回收器释放它,这时就应该使用Reference对象。以Reference对象作为你和普通引用之间的媒介(代理),另外,一定不能有普通的引用指向那个对象,这样就能达到上述目的。
SoftReference、WeakReference、PhantomReference由强到弱排列,对应不同级别的“可获得性”。
使用SoftReference和WeakReference时,可以选择是否要将他们放入ReferenceQueue(用作“回收前的清理工作”的工具)。而PhantomReference只能依赖于ReferenceQueue。