ArrayList 和 Vector 的区别

Vector 和 ArrayList 的实现几乎是一样的,区别在于:

1)最重要的区别: Vector 在方法上使用了 synchronized 来保证线程安全,同时由于这个原因,在性能上 ArrayList 会有更好的表现。

2) Vector 扩容后容量默认变为原来 2 倍,而 ArrayList 为原来的 1.5 倍。

有类似关系的还有:StringBuilder 和 StringBuffer、HashMap 和 Hashtable。

ArrayList 和 LinkedList 的区别

  1. ArrayList 底层基于动态数组实现,LinkedList 底层基于双向链表实现。
  2. 对于随机访问(按 index 访问,get/set 方法):ArrayList 通过 index 直接定位到数组对应位置的节点,而 LinkedList 需要从头结点或尾节点开始遍历,直到寻找到目标节点,因此在效率上 ArrayList 优于 LinkedList。
  3. 对于随机插入和删除:ArrayList 需要移动目标节点后面的节点(使用 System.arraycopy 方法移动节点),而 LinkedList 只需修改目标节点前后节点的 next 或 prev 属性即可,因此在效率上 LinkedList 优于 ArrayList。
  4. 对于顺序插入和删除:由于 ArrayList 不需要移动节点,因此在效率上比 LinkedList 更好。这也是为什么在实际使用中 ArrayList 更多,因为大部分情况下我们的使用都是顺序插入。
  5. 两者都不是线程安全的。
  6. 内存空间占用: ArrayList 的空间花费主要体现在,list 列表的结尾会预留一定的容量空间。而 LinkedList 的空间花费体现在,它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)

HashSet 是如何保证不重复的

HashSet 底层使用 HashMap 来实现,见下面的源码,元素放在 HashMap 的 key 里,value 为固定的 Object 对象。当 add 时调用 HashMap 的 put 方法,如果元素不存在,则返回 null 表示 add 成功,否则 add 失败。

由于 HashMap 的 key 值本身就不允许重复,HashSet 正好利用 HashMap 中 key 不重复的特性来校验重复元素。

// 被 transient 修饰的成员变量不参与序列化过程
private transient HashMap<E,Object> map;
 
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
 
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

TreeSet 清楚吗?能详细说下吗?

TreeSet 底层默认使用 TreeMap 来实现。而 TreeMap 通过实现 Comparator(或 Key 实现 Comparable)来实现自定义顺序。

private transient NavigableMap<E,Object> m;
 
private static final Object PRESENT = new Object();
 
TreeSet(NavigableMap<E,Object> m) {
    this.m = m;
}
public TreeSet() {
    this(new TreeMap<E,Object>());
}
 
public boolean add(E e) {
    return m.put(e, PRESENT)==null;
}

Comparable 和 Comparator 比较

  1. Comparable 是排序接口,一个类实现了 Comparable 接口,意味着“该类支持排序”。Comparator 是比较器,我们可以实现该接口,自定义比较算法,创建一个 “该类的比较器” 来进行排序。
  2. Comparable 相当于“内部比较器”,而 Comparator 相当于“外部比较器”。
  3. Comparable 的耦合性更强,Comparator 的灵活性和扩展性更优。
  4. Comparable 可以用作类的默认排序方法,而 Comparator 则用于默认排序不满足时,提供自定义排序。

耦合性和扩展性的问题,举个简单的例子:

当实现类实现了 Comparable 接口,但是已有的 compareTo 方法的比较算法不满足当前需求,此时如果想对两个类进行比较,有两种办法:

  1. 修改实现类的源代码,修改 compareTo 方法,但是这明显不是一个好方案,因为这个实现类的默认比较算法可能已经在其他地方使用了,此时如果修改可能会造成影响,所以一般不会这么做。
  2. 实现 Comparator 接口,自定义一个比较器,该方案会更优,自定义的比较器只用于当前逻辑,其他已有的逻辑不受影响。

介绍下 CopyOnWriteArrayList

CopyOnWriteArrayList 是 ArrayList 的线程安全版本,也是 copy-on-write(COW,写时复制)的一种实现。

在读操作时不加锁,跟 ArrayList 类似;在写操作时,复制出一个新的数组,在新数组上进行操作,操作完了,将底层数组指针指向新数组。适合使用在读多写少的场景。

例如 add(E e) 方法的操作流程如下:使用 ReentrantLock 加锁,拿到原数组的 length,使用 Arrays.copyOf 方法从原数组复制一个新的数组(length+1),将要添加的元素放到新数组的下标 length 位置,最后将底层数组指针指向新数组。

List、Set、Map 三者的区别

  • List(对付顺序的好帮手): 存储的对象是可重复的、有序的。

  • Set(注重独一无二的性质):存储的对象是不可重复的、无序的。

  • Map(用 Key 来搜索的专业户): 存储键值对(key-value),不能包含重复的键(key),每个键只能映射到一个值。

Map、List、Set 有哪些线程安全类和线程不安全的类

Map

  • 线程安全:CocurrentHashMap、Hashtable

  • 线程不安全:HashMap、LinkedHashMap、TreeMap、WeakHashMap

List

  • 线程安全:Vector(线程安全版的 ArrayList)、Stack(继承 Vector,增加 pop、push 方法)、CopyOnWriteArrayList

  • 线程不安全:ArrayList、LinkedList

Set

  • 线程安全:CopyOnWriteArraySet(底层使用 CopyOnWriteArrayList,通过在插入前判断是否存在实现 Set 不重复的效果)

  • 线程不安全:HashSet(基于 HashMap)、LinkedHashSet(基于 LinkedHashMap)、TreeSet(基于 TreeMap)、EnumSet

Collection 与 Collections 的区别

  • Collection:集合类的一个顶级接口,提供了对集合对象进行基本操作的通用接口方法。Collection 接口的意义是为各种具体的集合提供了最大化的统一操作方式,常见的 List 与 Set 就是直接继承 Collection 接口。

  • Collections:集合类的一个工具类/帮助类,提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。