Because TreeNodes are about twice the size of regular nodes, we
use them only when bins contain enough nodes to warrant use
(see TREEIFY_THRESHOLD). And when they become too small (due to
removal or resizing) they are converted back to plain bins. In
usages with well-distributed user hashCodes, tree bins are
rarely used.
get实现
get就是put的反向实现,但是逻辑比put简单很多。
将key hash化。
在table这个entry里找这个hash的node,找到后对node(tree or list)查找(用的是e.hash == hash和key.equals())。O(n) for LinkedList, O(nlgn) for tree.
红黑树实在Java8之后才加入,之前都只是纯粹的list。但是如果list比较长,O(n)是很慢的。不过红黑树空间占用比较多,所有加了一个TREEIFY_THRESHOLD作为妥协。
get和put中index的实现
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
...
// n 是table的大小
(n - 1) & hash
...
虽然学习使用Java很多年,但是很多学校里学的数据结构实现和原理的的东西却忘得七七八八,偶尔复习一下,苟日新,日日新,又日新。我们就来一起复习一下这些基础知识吧!
数据结构
1. String, StringBuilder, StringBuffer
String 原理:
String做为一个Class,它的数据由一个final修饰的char array 存储
private final char value[];
有一点要注意,在Java 7以前,String的object pool一般都放在Permanent generation里,和static待遇相同。Java 7开始String就丢到了heap里,因为Perm太小了(默认4M),而且Perm有取消的趋势。String s = new String("abc")
会创建2个object, 一个放在heap,一个在常量池(JDK 7开始在heap)。StringBuilder 原理:
StringBuilder继承了abstract修饰的AbstractStringBuilder类,它的数据也是由char array存储(初始默认长度是16)。
char[] value;
. StringBuilder在每次append新的String进来的时候,做了2件事 (1) 确认这个StringBuilder的容量是否足够,如果不足,就扩容。扩容操作会新建一个char[],大小为可以刚好存储加入新String。 核心代码:ensureCapacityInternal(count + len);
Arrays.copyOf(value, newCapacity(minimumCapacity));
(2) 把要加入的String里的char一个个复制进来。 核心代码:
str.getChars(0, len, value, count);
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
StringBuffer 原理:
StringBuffer就有意思了。它总的来说是一个thread-safe的StringBuilder,其方法有synchronized注释。
对比
String小巧,生成速度快,但是修改会产生大量Object,加大GC的工作量,影响性能,适用于immutable的数据。 StringBuilder生成速度比String慢,线程不同步,但是可以修改,修改不会显著加大GC工作量,默认推荐使用。 StringBuffer修改速度比StringBuilder要慢,但是线程同步,安全。
2. HashMap
我们一般会使用键值对(不重复的key,可重复的value)的时候用上Map类。
HashMap 原理
继承关系
数据存储
重要方法 put实现
至于为什么一开始就不用搜索更快的tree作为node,注释里这么解释
get实现 get就是put的反向实现,但是逻辑比put简单很多。
get和put中index的实现
使用注意:
sorted: HashMap不是sorted,但是TreeMap和LinkedHashMap是sorted。
3. Vector, List
Vector同步,线程安全。
4. LinkedList, ArrayList
LinkedList本质是linked nodes,。ArrayList本质是数组,但是有index,这个数组在填满后需要新建复制一个大的。两者都有space的浪费。LinkedList每个node需要更多空间,ArrayList的数组tail需要预留一定冗余。 LinkedList使用场景:不会随意对list不同位置的value进行访问。希望在list中或者head加入删除元素。希望按顺序访问其中元素。 ArrayList使用场景:会对list随意位置频繁访问,希望在tail增加元素。
Keywords
并发编程
yield: 该线程让步于其他相同优先级的线程。
Thread.yield()
sleep: 暂停线程。帮助所有线程获得运行机会。指定的睡眠时间是最短时间。Thread.sleep(123);
join: 让一个线程加入另外一个线程的尾部。 非静态。GC
GC的工作原理
reference在stack,object在heap,没有reference的object就会被回收。 优先级较低的后台进程。
Young gen, Old gen,Permanent gen
Young gen会放刚new的object,并且频繁快速gc符合条件的对象。 Young gen里又分Eden,Fron Survivor和To Survivor (8:1:1),survivor有一个一定是空的。但是如果object超大,一次GC就可能被丢进Old gen。 Old gen放多次GC后还存在的对象(MaxTenuringThreshold默认15次)。 空间Young gen:Old gen约等于1:2. Permanent gen放一些static,String(为了解决String内容重复问题)。针对Permanent gen的Major GC,触发条件苛刻。
Minor GC,Major GC = Full GC
Minor GC是发生于Young gen的GC。 Major GC是发生于Old gen和Permanent gen的GC,时间更久。
有哪些GC
Serial(UseSerialGC)串行收集器,复制算法。 SerialOld (UseSerialGC)串行收集器,标记整理算法。 CMS(Concurrent Low Pause Collector)经典的Stop Tow World(STW)。STW initial mark -- concurrent marking -- concurrent precleaning -- STW remark -- concurrent sweeping -- concurrent reset。 缺点:因为标记清理算法,会有内存碎片。需要更多CPU资源。需要更大的heap,默认old gen 68%启动GC。 ParNew (UseParNewGC)并行收集器,复制算法。 G1 GC
哪些算法
根搜索 标记-清除:扫描--标记--清除,没有对活着的对象进行整理,会有内存碎片。 复制:在标记-清除后,将存活对象往一端空闲空间移动,更新stack里的reference,成本高,但是解决了内存碎片的问题。 Young gen的Minor GC一般用复制算法,Major GC一般用标记-清除算法。
System.gc() 可以触发full GC但不是马上执行,比较影响性能。
Reflection
原理:
Exception
Throwable Exception & Error RunTimeException & other Exceptions