面试问题总结之Java基础篇
HashMap、Hashtable
Java文档里提到的Hashtable和HashMap的区别:一是Hashtable是同步的,二是HashMap可以接受null作为key和value。Hashtable的同步机制实际并不能保证线程安全,有多线程操作时应当选用ConcurrentHashMap。
还有一些资料列举出来的区别:
- 数据遍历方式,Hashtable有Iterator和Enumeration,HashMap只有Iterator;
- Hashtable和HashMap用Iterator遍历时都支持fast-fail机制,但是Hashtable用Enumeration遍历时不支持fast-fail;
- HashMap的根据hash值计算数组下标的算法要优于Hashtable,通过对Key的hash做移位运算和位的与运算,使其能更广泛地分散到数组的不同位置;
- Entry数组的长度:
- Hashtable 缺省初始长度为11,初始化时可以指定initial capacity
- HashMap 缺省初始长度为16,长度始终保持2的n次幂,初始化时可以指定initial capacity,若不是2的幂,HashMap将选取第一个大于initial capacity 的2的n次幂值作为其初始长度。
GC机制
这东西有点复杂,不敢说懂了多少……只能先记录一些自己的理解。主要参考的是Java编程思想的第5章。
首先GC有两个关键问题,第一是如何确定某个对象需要被回收,第二是回收的具体算法。
确定某个对象是垃圾:
最直接的方法是引用计数法,每个对象都有一个引用计数器,当有一个引用连接到对象上时,计数器加1。当引用离开作用域或被置为null时,计数器减1。但是这种办法不能解决循环引用的问题。
为了解决循环引用,引入了另一种办法,主要思想是:对任何有用的对象,一定能追溯到其存活在堆栈或静态存储区中的引用。因此,如果从堆栈和静态存储区开始,遍历所有的引用,就能找到所有“活”的对象。
垃圾回收算法:
参考:
http://www.cnblogs.com/dolphin0520/p/3783345.html
“标记-清扫”:通过上面方法找出所有存活对象,每找到一个就给对象设一个标记,当全部标记完成的时候开始清理,没有标记的对象被释放。这种方法会造成可用内存空间不连续,因此需要清理完以后再进行整理,也就是“标记-整理”。
“停止-复制”:将所有存活对象从当前堆复制到另一个堆,没有复制的都是垃圾,然后把当前堆的内存全部清理掉。这种方法会把内存分为两个堆,相当于降低了一半的内存使用率;同时如果产生的垃圾很少,那么每次都要复制大量对象到另一处,会很浪费。
自适应的分代的算法:主流采用的算法,根据对象存活时间将内存分为“新生代”、“老年代”,新生代里存放生命周期短的临时性对象,老年代存放稳定的长期对象。新生代每次回收都要回收大量垃圾,适合用“停止-复制”算法;老年代回收的垃圾较少,适合用“标记-清扫”算法。这就是一种“自适应”技术。(另外有一种说法是把堆区外的方法区定义为“永久代”,现在好像要改变这个概念,待后续研究。)
http://www.cnblogs.com/hnrainll/archive/2013/11/06/3410042.html
这篇提到了很多内存分配相关的内容,因此也放上来作为参考。
集合类
Java集合类归根结底有三种,List,Map,Set。Java编程思想上把Queue也作为一个基本的集合类,其实我不太懂为什么。Queue和Stack都是由LinkedList实现的,应该也算是从List衍生出去的吧,不懂为什么要和List分开。
- 扩容问题
ArrayList扩容时,是用Arrays.copyOf()方法,传入原数组和新容量大小,返回一个新的数组。再查看copyOf方法时,是调用的System.arraycopy()方法。这个方法是一个native的方法,看不到源码了……
扩容时新容量是max{原数组容量的1.5倍,传入的参数}。
<未完待续>