一、 不定选择题(每题4分)
-
Java 容器框架主要分为 Collection 和 Map 两种。其中,Collection 又分为()
A、List
B、Set
C、Queue
D、以上都是 -
以下哪一个是线程安全的()
A、Vector
B、HashMap
C、TreeMap
D、ArrayList -
HashSet的特性有哪些()
A、HashSet 实现了 Cloneable, Serializable。所以支持克隆和序列化。
B、HashSet 中存储的元素是无序的。
C、HashSet 允许 null 值的元素。
D、HashSet 通过继承 AbstractSet 实现了 Set 接口中的骨干方法。是线程安全的 -
关于TreeSet说法正确的是()
A、TreeSet 中存储的元素是有序的。排序规则是自然顺序或比较器(Comparator)中提供的顺序规则。
B、TreeSet实现了 Cloneable, Serializable。所以支持克隆和序列化。C、TreeSet 不是线程安全的。
D、TreeSet 通过继承 AbstractSet 实现了 NavigableSet 接口中的骨干方法。 -
关于LinkedHashMap说法正确的是()
A、Key 和 Value 都允许 null
B、Key 重复会覆盖、Value 允许重复
C、按照元素插入顺序存储
D、线程安全
二、 填空题(每空1分)
- Java 容器通过()技术来保证其数据的类型安全
- 容器在迭代操作中改变元素个数(添加、删除元素)都可能会导致()
- List 是一个接口,它继承于 () 的接口。它代表着有序的队列。
- ArrayList 是一个数组队列,相当于动态数组。ArrayList 默认初始容量大小为 (),添加元素时,如果发现容量已满,会自动扩容为原始大小的()倍。
- ArrayList 访问元素是通过数组下标访问数组元素,其时间复杂度为 ()
- Map 接口提供三种 Collection 视图,允许以()、()或() 的形式访问数据。
- HashMap 有两个影响其性能的参数:() 默认为 () 和 ()默认为 ()
- 果哈希碰撞导致链表过长(大于等于 TREEIFY_THRESHOLD,数值为 ()),就把链表转换成红黑树;
- TreeMap 基于() 树实现。
- ArrayDeque 用一个()实现了栈和队列所需的所有操作。
- ArrayList扩容操作实际上是调用()把原数组拷贝为一个新数组,因此最好在创建 ArrayList 对象时就指定大概的容量大小,减少扩容操作的次数。
- () 基于双向链表实现,不存在容量限制。
- Hashtable使用了() 关键字修饰,保证了线程安全但是影响读写速度。
- HashSet ,它实际上是通过 ()实现的。HashSet 中的元素是无序的、散列的。
三、 判断题(每题2分)
-
如果有一个 List 容器,Java 编译器在编译时会对原始类型进行类型安全检 ()
-
Collection 接口扩展了 Iterable 接口。 ()
-
fail-fast 是 Java 容器的一种错误检测机制。当多个线程对容器进行结构上的改变的操作时,就一定触发 fail-fast 机制 ()
-
ArrayList 基于动态数组实现,存在容量限制,当元素数超过最大容量时,会自动扩容;LinkedList 基于双向链表实现,不存在容量限制。 ()
-
ArrayList 随机访问速度较快,随机插入、删除速度较慢;LinkedList 随机插入、删除速度较快,随机访问速度较慢。 ()
-
初始化 ArrayList 时,指定数组初始大小,有助于减少数组的扩容次数,从而提高系统性能。 ()
-
LinkedList 基于双链表结构实现。由于是双链表,所以顺序访问会非常高效,而随机访问效率比较低。 ()
-
Arrays.asList 返回的 List 支持增删操作。 ()
-
Map 中不能包含重复的键;每个键最多只能映射到一个值。 ()
-
HashMap 允许使用空值和空键。 ()
-
TreeMap 是有序的。它的排序规则是:根据 map 中的 key 的自然语义顺序或提供的比较器(Comparator)的自定义比较顺序。 ()
-
Set 集合允许有重复元素。 ()
-
TreeSet 是基于 TreeMap 实现的。 ()
-
LinkedHashSet 中存储的元素是按照插入顺序保存的。 ()
-
Deque 接口是 double ended queue 的缩写,即双端队列。Deque 继承 Queue 接口,并扩展支持在队列的两端插入和删除元素。 ()
四、简答题(每题10分) -
如何解决 fail-fast
-
ArrayList 包含了两个重要的元素:elementData 和 size,简述他们的作用
-
为什么计算 hash 使用 hashcode 无符号位移 16 位。
参考答案
D、A、ABC、ABCD、ABC
1.泛型
2. fail-fast
3. Collection
4.10 1.5
5.On(1)
6. 键集 值集 键-值映射关系集
7. 初始容量 16 负载因子 0.75
8. 8
9.红黑树
10.动态数组
11. Arrays.copyOf()
12. LinkedList
13. synchronize
14. HashMap
1.×2.√3.×4.√5.√6.√7.√8.×9.√10.√11.√12.×13.√14.√15.√
在遍历过程中所有涉及到改变容器个数的地方全部加上 synchronized 或者直接使用 Collections.synchronizedXXX 容器,这样就可以解决。但是不推荐,因为增删造成的同步锁可能会阻塞遍历操作,影响吞吐。
使用并发容器,如:CopyOnWriterArrayList。
size - 是动态数组的实际大小。
elementData - 是一个 Object 数组,用于保存添加到 ArrayList 中的元素
假设要添加两个对象 a 和 b,如果数组长度是 16,这时对象 a 和 b 通过公式 (n - 1) & hash 运算,也就是 (16-1)&a.hashCode 和 (16-1)&b.hashCode,15 的二进制为 0000000000000000000000000001111,假设对象 A 的 hashCode 为 1000010001110001000001111000000,对象 B 的 hashCode 为 0111011100111000101000010100000,你会发现上述与运算结果都是 0。这样的哈希结果就太让人失望了,很明显不是一个好的哈希算法。
但如果我们将 hashCode 值右移 16 位(h >>> 16 代表无符号右移 16 位),也就是取 int 类型的一半,刚好可以将该二进制数对半切开,并且使用位异或运算(如果两个数对应的位置相反,则结果为 1,反之为 0),这样的话,就能避免上面的情况发生。这就是 hash() 方法的具体实现方式。简而言之,就是尽量打乱 hashCode 真正参与运算的低 16 位