Collection接口

56 阅读1分钟

image.png

List

Vector

  1. Vector底层维护一个Object类型数组elementData
protected Object[] elementData;
  1. 线程安全的,Vector类的操作方法带有synchronized
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

示例代码:

Vector<Integer> vector = new Vector<>();
vector.add(1);
vector.add(2);
vector.add(3);
vector.add(null);
vector.add(null);
System.out.println(vector);

结果输出:
[1, 2, 3, null, null]

ArrayList

底层结构与源码分析之扩容机制

  1. ArrayList底层维护一个Object类型数组elementData
transient Object[] elementData;
  1. 当创建ArrayList对象时,如果使用的是无参构造器,则初始化elementData容量为0,第一次添加,则扩容elementData为10,如果再次扩容,则扩容elementData为1.5倍
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 当创建ArrayList对象时,如果使用的是无参构造器,则初始化elementData容量为0
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
 * Default initial capacity.
 */
private static final int DEFAULT_CAPACITY = 10;

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 第一次添加,则扩容elementData为10
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    // 如果再次扩容,则扩容elementData为1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}
  1. 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍

示例代码:

// 使用无参构造器,初始化elementData容量为0
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
    // i==1 第一次添加,则扩容elementData为10
    list.add(i);
}
for (int i = 1; i <= 15; i++) {
    // i==1 如果再次扩容,则扩容elementData为1.5倍
    list.add(i);
}
list.add(100);
list.add(200);
list.add(null);
list.add(null);
System.out.println(list);

结果输出:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 100, 200, null, null]

LinkedList

  1. LinkedList底层维护一个双向链表,其中first和last两个属性分别指向首节点和尾节点。每个节点(Node对象)维护prev、next、item三个属性。
  2. 添加和删除元素操作,效率高 image.png

示例代码:

LinkedList<Integer> linkedList = new LinkedList<>();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
linkedList.add(null);
linkedList.add(null);
System.out.println(linkedList);

结果输出:
[1, 2, 3, null, null]

Vector和ArrayList比较

image.png

ArrayList和LinkedList比较

image.png

如何选择?

  • 查询,修改操作多,选择ArrayList
  • 增加,删除操作多,选择LinkedList
  • 一般情况下,在程序中多数情况下都是查询操作,因此大部分情况下会选择ArrayList
  • 根据业务情况灵活选择

Set

HashSet

  1. 可以存放null,但只能存放一个null
  2. 不能有重复元素
  3. 不保证存放元素的顺序和取出顺序是一致的(可用linkedHashSet保证存放和取出的顺序一致)

示例代码:

HashSet set = new HashSet<>(); 
System.out.println(set.add(new String("lxw")));//true 
System.out.println(set.add(new String("lxw")));//false,加入不了

结果分析: String 重写了 hashCode 方法,其 hashCode 值是由内容决定的,而不是地址决定的。所以两次new String("lxw"),返回了两个不同引用的对象地址,但这两个引用指向同一内容,所以引用地址不同,但hashcode相同的。

实现机制

  1. HashSet底层实现是HashMap
public HashSet() {
    map = new HashMap<>();
}
  1. 添加一个元素时,由hash值转换成索引值
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
  1. 在存储数据表table,判断索引位置是否已存放有元素,如果没有,直接加入;如果有,调用equals比较,相同则丢弃,不同则添加
  2. 在Java8中,当一个链表的元素个数超过8,table>=64,会进化成红黑树。否则,采用数组扩容机制。
public class HashSetDemo {
    static class A {
        private int i;
        public A(int i) {
            this.i = i;
        }
        @Override
        public int hashCode() {
            return i;
        }
    }
    public static void main(String[] args) {
        
        HashSet<A> set = new HashSet<>();
        for (int i = 1; i <= 12;i++) {
            set.add(new A(i));
        }
        
        //单链表为8
        for (int i = 1; i <= 7; i++) {
            set.add(new A(1));
        }
        //单链表为9
        for (int i = 1; i <= 8; i++) {
            set.add(new A(2));
        }
        System.out.println();
    }
}

LinkedHashSet

  • LinkedHashSet是HashSet的子类
public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
    // ...
}
  • LinkedHashSet底层是一个LinkedHashMap,底层维护数组+双向链表
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
  • LinkedHashSet根据元素的hashCode值决定元素的存储位置,同时使用链表维护元素的次序,使元素看起来是以插入顺序保存的

TreeSet

  1. 底层实现是TreeMap
public TreeSet() {
    this(new TreeMap<E,Object>());
}

2.排序的