容器
1. Java 容器都有哪些?
1)Collection:一个独立元素的序列,这些元素都服从一条或者多条规则。 List必须按照插入的顺序保存元素,而set不能有重复的元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
2)Map:一组成对的“键值对”对象,允许你使用键来查找值。
java容器结构图:
2. Collection 和 Collections 有什么区别?
Collection :是定义集合的接口类
Collections :是操作集合的一个工具类
3. List、Set、Map 之间的区别是什么?
List 必须按照插入的顺序保存元素,
set 不能有重复的元素
Map 根据键值对的形式保存数据元素
4. HashMap 和 Hashtable 有什么区别?
HashMap :继承于AbstractMap,允许存放null key,null value,线程不安全(多线程访问时,若外部未加同步限制,扩容、空值替换等原因可能出现数据错乱丢失)
Hashtable :继承于Dictionary,不允许存放null key,null value,操作方法均为synchronized,无需外部同步,线程安全
两者均是Map的实现类,都是采用了 hash/rehash 算法,尾插法,在jdk 1.8之后hashMap加入红黑树(链表长度达到8时使用红黑树存放,当在6个以下时切换回链表),查询优化效率大幅提升。
5. 如何决定使用 HashMap 还是 TreeMap?
根据排序场景决定
TreeMap(红黑树) :基于树形结构排序(key 排序)
HashMap : 不保证序列
LinkedHashMap (链表+hash表) : 保证插入顺序
6. 说一下 HashMap 的实现原理?
HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。底层数据结构基于数组与链表实现
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
transient Node<K,V>[] table;
HashMap 基于 Hash 算法实现的:
当我们往Hashmap中put元素时,利用key的hashCode重新hash计算出当前对象的元素在数组中的下标
//通过key计算hash值
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
存储时,没有key直接新增到下标第一个,如果出现hash值相同的key,此时有两种情况。(1)如果key相同,则覆盖原始值;(2)如果key不同(出现冲突),则将当前的key-value放入链表中第一个
获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。
理解了以上过程就不难明白HashMap是如何解决hash冲突的问题,核心就是使用了数组的存储方式,然后将冲突的key的对象放入链表中,一旦发现冲突就在链表中做进一步的对比。
7. 说一下 HashSet 的实现原理?
HashSet是基于HashMap实现的,内部是存放了一个HashMap,
构造函数:
public HashSet() {
map = new HashMap<>();
}
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
当进行存储数据时,会将数据存放到map的key,从而实现可存放null,数据不重复,同时继承了hashMap无序的特性。
(TreeSet默认使用TreeMap,也可指定其他排序map(NavigableMap))
//PRESENT == new Object() 无意义
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
8. ArrayList 和 LinkedList 的区别是什么?
ArrayList :底层是数组
transient Object[] elementData;
LinkedList :底层是链表(双向链表)
transient Node<E> first;
transient Node<E> last;
9. 如何实现数组和 List 之间的转换?
(循环、遍历不做演示。)
1.数组转集合 arr -> list 均为String类 使用Arrays.asList
List<String> list = Arrays.asList(arr);
2.数组转集合 arr -> list int <-> Integer 使用stream
list = Arrays.stream(arr).boxed().collect(Collectors.toList());
3.集合转数组 list -> arr 均为String类 使用toArray
arr = list.toArray(new String[list.size()]);
4.集合转数组 list -> arr Integer <-> int 使用stream
arr = list.stream().mapToInt(i->i).toArray()
10. ArrayList 和 Vector 的区别是什么?
Vector 是线程安全的集合,内部操作方法均使用synchronized修饰,
ArrayList 是非线程安全集合,内部未采用同步化机制
11. Array 和 ArrayList 有何区别?
Array数组可以包含基本类型和对象类型,
ArrayList 是数组的加强版,只能包含对象类型。可以进行自动扩容。
12. 在 Queue 中 poll()和 remove()有什么区别?
poll(): 返回此队列的开头,如果此队列为空,则返回null
remove():返回此队列的开头,如果此队列为空,它将抛出异常NoSuchElementException。
13. 哪些集合类是线程安全的?
Vector:就比Arraylist多了个同步化机制(线程安全)。
Hashtable:就比Hashmap多了个线程安全。
ConcurrentHashMap:是一种高效但是线程安全的集合。
Stack:栈,也是线程安全的,继承于Vector。
14. 迭代器 Iterator 是什么?
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
15. Iterator 怎么使用?有什么特点?
(1) iterator()方法是java.lang.Iterable接口,被Collection继承。所有Collection下集合使用方法iterator()要求容器返回一个Iterator,第一次调用Iterator的next()方法时,它返回序列的第一个元素。而Map需要调用entrySet或keySet或values(键值set,键set,值Collection)再继续调用iterator()操作
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
16. Iterator 和 ListIterator 有什么区别?
Iterator是Java迭代器最简单的实现,
ListIterator是为List设计的具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
(foreach不能操作内部集合插入、删除)
17. 怎么确保一个集合不能被修改?
利用Collections和Guava提供的类可实现的不可变对象
Collections:
map.put(1, "one");
map.put(2, "two");
map = Collections.unmodifiableMap(map);
map.put(1, "three");
Guava:
ImmutableList<Integer> list = ImmutableList.of(1, 2, 3);
list.add(4);
ImmutableMap<Integer, Integer> map2 = ImmutableMap.<Integer, Integer>builder().put(1,2).put(3,4).put(5,6).build();
list.put(1,8)
如果进行修改均会抛出UnsupportedOperationException