Thinking in Java 读书笔记 (一)

  • System.arrayCopy
  • Set
  • Map
  • 免锁容器
  • BitSet
  • 序列化
  • String format
  • ByteBuffer
  • FileLock
  • 持久化

System.arrayCopy
需要的参数有:源数组,表示从源数组的什么位置开始复制的偏移量,目标数组,表示从目标数组的什么位置开始复制的偏移量,复制的元素个数,对数组的任何越界操作都会导致异常。浅复制(shallow copy)

Set
存入Set的每个元素必须是唯一的,加入Set的元素必须定义equals方法已确保对象的唯一性。不保证元素的次序。HashSet 一般情况下都应该使用此实现,因为它对速度做了优化,存入HashSet的元素必须定义hashCode,不然元素会重复TreeSet 有序的Set,底层为树结构,元素必须实现Comparable接口,不然会得到一个cannot be cast to java.lang.Comparable的异常LinkedHashSet 具有HashSet的查询速度,使用链表维护元素的插入顺序,元素必须定义hashCode,不然元素会重复对于好的编程风格而言,应该在覆盖equals方法的时候,总是同时覆盖hashCode。

Map
容量:表中的桶位数。
初始容量:容器在创建时所拥有的桶位数。HashMap和HashSet都具有允许你指定初始容量的构造器。
尺寸:表中当前存储的项数
负载因子:尺寸/容量。空表的负载因子是0,而半满表的负载因子是0.5,以此类推。负载轻的容器产生冲突的可能性小,因此对于插入和查找都是理想的(但是会减慢使用迭代器进行遍历的过程)。
HashMap和HashSet都具有允许你指定负载因子的构造器,表示当负载情况达到该负载因子的水平时,容器将自动增加其容量(桶位数),实现方式是使容量大致加倍,并重新将现有对象分布到新的桶位集中。(这被称为再散列)HashMap使用的默认负载因子是0.75(只有当容器达到四分之三满时,才进行再散列),这个因子在时间和空间代价之间达到了平衡。更高的负载因子可以降低表所需的空间,但是会增加查找代价,这很重要,因为查找是我们在大多数时间里所做的操作(包括get()和put())。
如果你知道将要在HashMap中存储多少项,那么创建一个具有恰当大小的初始容量将可以避免自动再散列的开销。
HashMap Map基于散列表的实现。插入和查询“键值对 ” 的开销是固定的。可以通过构造器设置容量和负载因子,以调整容器的性能。
LinkedHashMap 类似于HashMap,但是迭代遍历它时,取得“键值对 ”的顺序是其插入次序,或者是最少使用(LRU)的次序。只比HashMap慢一点,而在迭代访问时反而更快,因为它使用链表维护内部次序。
TreeMap 基于红黑树的实现。查看“键”或“键值对 ”时,它们会被排序(次序由Comparable或Comparator决定)。TreeMap的特点在于,所得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树.
ConcurrentHashMap 一种线程安全的Map,不涉及同步加锁
IdentityHashMap 使用==代替equals()对“键”进行比较的散列映射,为解决特殊问题而设计。
散列是映射中存储元素时最常用的方式。对Map中使用的键的要求与对Set的元素的要求一样,任何键都必须具有一个equals方法,如果键被用于散列,必须具有合适的hashCode方法,
如果键被用于TreeMap,那么必须实现Comparable.
WeakHashMap 弱键映射,允许释放映射所指向的对象,为解决某类特殊问题而设计。如果映射之外没有引用指向某个“键”,则“键”可以被垃圾收集器回收。
清空table中无用键值对。原理如下:
(01) 当WeakHashMap中某个“弱引用的key”由于没有再被引用而被GC收回时,被回收的“该弱引用key”也被会被添加到”ReferenceQueue(queue)”中。
(02) 当我们执行expungeStaleEntries时,就遍历”ReferenceQueue(queue)”中的所有key然后就在“WeakReference的table”中删除与“ReferenceQueue(queue)中key”对应的键值对
最后的原理来自这个博客.

免锁容器
Vector和Hashtable这类早期容器具有许多synchronized方法,当它们用于非多线程的应用程序中时,便会导致不可接受的开销。
在Java1.2中,新的容器类库是不同步的,并且Collections类提供了各种static的同步的装饰方法,从而来同步不同类型的容器。
尽管这是一种改进,因为它使你可以选择在你的容器中是否要使用同步,但是这种开销仍旧是基于synchronized加锁机制的。
Java SE5特别添加了新的容器,通过使用更灵巧的技术来消除加锁,从而提高线程安全的性能。

这些免锁机制容器背后的通用策略是:对容器的修改可以与读取操作同时发生,只要读取者只能看到完成修改的结果即可。
修改是在容器数据结构的某个部分的一个单独的副本(有时是整个数据结构的副本)上执行的,并且这个副本在修改过程中是不可观的。
只有修改完成时,被修改的结构才会自动地与主数据结构进行交换,之后读取者就可以看到这个修改了。

在CopyOnWriteArrayList中,写入将导致创建整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全的执行。当修改完成时,一个原子性的操作将把新的数组换入,使得新的读取操作可以看到这个新的修改。CopyOnWriteArrayList的好处之一是当多个迭代器同时遍历和修改这个列表时,不会抛出ConcurrentModificationException,因此你不必编写特殊的代码去防范这种异常,就像你以前必须做的那样。

CopyOnWriteArraySet将使用CopyOnWriteArrayList来实现其免锁行为。

ConcurrentHashMap和ConcurrentLinkedQueue使用了类似的技术,允许并发的读取和写入,但是容器中只有部分内容而不是整个容器可以被复制和修改。然而,任何修改在完成之前,读取者仍旧不能看到它们。ConcurrentHashMap不会抛出ConcurrentModificationException异常。

BitSet
如果想要高效率地存储大量“开/关”信息,BitSet是很好的选择。不过它的效率仅是对空间而言;如果需要高效的访问时间,BitSet比本地数组慢一点。BitSet的最小容量是long:64位。如果存储的内容比较小,例如8位,那么BitSet就浪费了一些空间。

序列化
serialVersionUID的详细工作机制是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的文件中(也有可能是其他媒介),当反序列化的时候系统会去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致就说明序列化的类和当前类的版本是相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变化,比如成员变量的数量,类型可能发生了变化,这个时候是无法正常反序列化的,因此会报错。手动指定serialVersionUID可以某种程度的避免反序列化的失败。如果不指定serialVersionUID,当数据结构发生变化,系统重新计算当前类的hash值,反序列化就会失败,程序会挂掉。

String format
printf()等价于format()
%d 整数型(十进制)
%c Unicode字符
%b Boolean值
%s String
%f 浮点型(十进制)
%e 浮点型(科学计数)
%x 整数(十六进制)
%h 散列码(十六进制)
% 字符“%”

ByteBuffer
ByteBuffer使用allocate分配空间
每次read之后,就会将数据输入到缓冲器中
flip则是准备缓冲器以便数据可以由write提取
write操作之后,数据仍在缓冲器中,接着使用clear操作对所有的内部指针重新安排,以便缓冲器在另一个read操作期间能够做好接受数据的准备
rewind返回到数据开始的地方

FileLock
通过对FileChannel调用tryLock()或lock(),就可以获得整个文件的FileLock。tryLock是非阻塞式的,它设法获取锁,但是如果不能获得(当其他一些进程已经持有相同的锁,并且不共享时),它将直接从方法调用返回。lock是阻塞式的,它要阻塞进程直至锁可以获得,或调用lock的线程中断,或调用lock的通道关闭。使用release可以释放锁。
也可以使用如下方法对文件的一部分上锁
tryLock(long position, long size, boolean shared)或者
lock(long position, long size, boolean shared)
加锁的区域由size-position决定,第三个参数指定是否是共享锁。
无参数的锁随文件的变化而变化,始终对整个文件枷锁
有参数的锁不随文件的变化而变化。
对独占锁或者共享锁的支持必须由底层的操作系统提供。如果操作系统不支持共享锁并为每一个请求都创建一个锁,那么它就会使用独占锁。
isShared查询锁的类型(共享,独占)

持久化
持久化意味着一个对象的生存周期并不取决于程序是否正在执行。
对于Serializable,对象完全以它存储的二进制位为基础来构造,而不调用构造器。
而对于一个Externalizable对象,所有普通的默认构造器都会被调用(包括在字段定义时的初始化),然后调用readExternal。
Externalizable extends java.io.Serializable


猜你喜欢

转载自blog.csdn.net/jthou20121212/article/details/72905098