速通Java集合

1 概论

【强制】Java中常见的集合。

Java集合主要包括Collection(集)和Map(映射)两大类,Collection接口又有Set(集合)和List(列表)两个子接口。

List接口的主要实现类有:ArrayList、LinkedList、Stack、Vector等。

Set接口的主要实现类有:HashSet、TreeSet、LinkedHashSet等。

Map接口的主要实现类有:HashMap、TreeMap、HashTable、ConcurrentHashMap等。

【强制】集合和数组的区别。

数组的长度固定,集合的长度可变。

数组可以存放基本数据类型,集合不可以。

【推荐】集合是否可以存储null?

List接口的实现类都可以存储多个null。

Set接口的实现类中,HashSet、LinkedHashSet可以存储一个null,TreeSet不能存储null。

Map接口的实现类中,HashMap、LinkedHashMap的key和value都可以为null,TreeMap的key不可用为null,value可以为null,HashTable、ConcurrentHashMap的key和value都不能为null。

【推荐】transient关键字。

用transient修饰的成员变量不参与序列化过程。

集合类通常会预留一些容量,当容量不足时再进行扩容,这样,有一部分空间就没有实际存储元素。

在集合类中使用transient关键字的目的是为了保证只序列化集合中实际存储的元素,而不是整个集合,从而节省时间和空间。

2 List

【强制】ArrayList。

1) ArrayList和LinkedList的区别。

    ArrayList的底层实现是动态数组,随机访问的效率较高;

    LinkedList的底层实现是双向链表,增加或删除结点的效率较高。并且实现了Deque接口,可以用作双端队列。

2) Vector和ArrayList的区别。

    Vector是线程同步的,Vector的方法加了synchronized关键字;

    ArrayList是线程不同步的。

3) 遍历ArrayList。

List<String> list = new ArrayList();
list.add("hello");
list.add("world");

// 使用迭代器遍历
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

// foreach遍历
for (String s : list) {
    System.out.println(s);
}

4) ArrayList的扩容机制。

    当ArrayList对象第一次调用add方法时,初始化定义数组容量为10;

    当ArrayList容量已满,再次调用add方法时就会发生扩容。

    ArrayList扩容时,newCapacity = oldCapacity + (oldCapacity >> 1),即新容量是老容量的1.5倍。

    当ArrayList的长度大于Integer.MAX_VALUE时,抛出OutOfMemoryError。

3 Map

【强制】HashMap。

1) 数据结构。

    HashMap的数据结构为 数组+链表/红黑树。

    HashMap中的每个结点(Node)都包含4个属性:key、value、next、hash。

2) 遍历HashMap的时间复杂度。

    HashMap根据key查找数组下标的时间复杂度为O(1),不影响遍历整体的时间复杂度。

    然后遍历链表的时间复杂度是O(n),遍历红黑树的时间复杂度是O(logn)。

3) 向HashMap中插入结点。

    向HashMap中插入结点时,会先使用对象的key调用哈希函数(hashCode高低16位做异或运算)计算得到一个hash值。

    然后使用hash值和[数组长度-1](数组长度恒为2的n次幂,所以[数组长度-1]的每一位都是1)做位与运算得到一个数组下标。

    如果数组该下标的位置为空,就插入,如果下标位置上已有其他结点,说明发生了hash冲突(也叫hash碰撞),就把要插入的结点和该位置上的结点通过next属性连接起来形成链表。

    链表的插入方式是尾插入。如果采用头插入方式,在并发场景下,扩容时可能会出现循环链表。

    以上步骤完成后,判断当前HashMap中结点的数目是否超过了阈值,如果超过就调用resize方法进行扩容(先插入再扩容)。

4) HashMap的扩容机制。

    新建的HashMap的容量默认为16。

    HashMap扩容的阈值是容量乘以负载因子,负载因子默认是0.75。0.75是一个折衷的选择,如果负载因子较大,发生哈希碰撞的概率就更大,如果负载因子较小,存储消耗的空间就更多。

    当HashMap中结点的数目超过了阈值时,将桶数组的大小扩充到2倍(实际是创建了一个新的数组)。

    然后遍历已存储的结点,用结点的hash值与原数组大小做与运算(原数组大小是2的倍数,只有最高位是1,其他位都是0),若结果为0,将结点放到新数组的当前位置,若结果不为0,将结点放到新数组下标为 当前下标+原数组大小 的位置。这么做的好处是省略了重新计算每个结点下标的步骤。

5) HashMap是线程不安全的。

    如果想在高并发环境下使用HashMap,有两种方案:

    第一种方案是使用java.util.Collenctions里面提供的包装方法synchronizedMap(Map<K,V> m)来包装HashMap,得到一个SynchronizedMap。

    第二种方案是使用java.util.concurrent.ConcurrentHashMap。

【强制】红黑树。

1) 数据结构。

    红黑树是平衡的二叉搜索树。

    二叉搜索树的规则是任意结点的左子树(如果有)上的所有结点的值均小于该结点的值,右子树(如果有)上的所有结点的值均大于该结点的值。

    红黑树在二叉搜索树的基础上做了平衡,保证每个结点的左子树和右子树的高度差最大为2,如果超过了就进行调平衡。

    调平衡操作包括左旋、右旋和变色。红黑树的任何不平衡问题都能在三次旋转之内解决。

2) HashMap中链表与红黑树之间的转换。

    链表长度大于8且数组长度大于等于64时,链表转换成红黑树。

    链表长度小于6时,红黑树转换成链表。

    链表转红黑树的阈值设为8的原因是:理想情况下使用随机hashCode算法,所有桶中的结点遵循泊松分布,在同一个桶位发生8次哈希碰撞的概率微乎其微,将阈值设为8可以保证链表在大多数情况下不会转红黑树。

    红黑树转链表的阈值设为6的原因是:避免红黑树和链表之间频繁地来回转换。

【强制】ConcurrentHashMap。

1) 数据结构。

    JDK 8以后,数组结构为 数组+链表/红黑树。

    对链表/红黑树的头/根结点加synchronized锁,在同一时间,只能有一个线程对该链表/红黑树进行操作。

2) ConcurrentHashMap的key和value不支持null。

    在并发环境下,使用get方法得到了null,无法判断是value为null,还是没有找到对应的key。

【强制】LinkedHashMap。

LinkedHashMap.Entry除了继承HashMap.Node外,还有before和after两个指针,用于标识前置结点和后置结点。

【强制】TreeMap。

TreeMap的数据结构是一棵红黑树。

TreeMap的key按照自然顺序或者Comprator的顺序进行排列。

4 Set

【强制】HashSet。

HashSet本质上是一个HashMap的keySet。

private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();

public HashSet() {
    map = new HashMap<>();
}

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
}

public boolean contains(Object o) {
    return map.containsKey(o);
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值