Java容器(二)Collection集合

在这里插入图片描述

一、 Set

TreeSet : 内部维护了一个TreeMap,插入的值作为map的key,value用默认值。特性是:排序,去重。

HashSet: 内部维护了HashMap,特点是插入和查找,时间复杂度 o(1), 但是迭代的顺序是乱序。

LinkedHashSet:内部维护了LinkedHashMap,特点是去重,可以按照插入的顺序进行迭代。

Set没啥好讲的,原理都是Map,因此到时候理解Map即可。

二、 List

2.1 ArrayList

2.1.1 数据域

elementData是存放数据的地方,它使用了transient修饰,不参与序列化。具体序列化看下面。

// 用于序列化
 	private static final long serialVersionUID = 8683452581122892189L;
 	
// 默认的容器大小
    private static final int DEFAULT_CAPACITY = 10;
    
// 构造方法指定的容器大小 == 0, 使用这个数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    
 //构造方法不指定容器大小,使用这个数组 
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
// 实际存放元素的数组,它用transient关键字修饰,意思是不参与序列化
// 因为这个数组经常放不满,因此序列化的时候会调用write,把不为空的数据序列化
// 不对这个数组序列化是因为效率问题。
    transient Object[] elementData; // non-private to simplify nested class access

// 数据的实际大小
    private int size;
2.1.2 扩容

扩容的代码非常高,每次都使用elementData = Arrays.copyOf(elementData, newCapacity);来进行赋值,因此最好在程序一开始就指定好需要用的长度。

https://blog.csdn.net/qq_37591656/article/details/88134813

2.1.3 删除元素

ArrayList是基于数组的,它的删除调用了System.arraycopy()将index+1往后的值都复制到index的位置上。删除的代价很高。

 public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
2.1.4 序列化

1)序列化对于size(实际数据量)进行,而不是对完整的Object[] data。
2)反序列化中,先获取modcount, 在最后对modCount进行比较,如果两个数不相同,那么抛出异常。

int expectedModCount = modCount; 
///
  if (modCount != expectedModCount) {
    throw new ConcurrentModificationException();
}
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new Concurrenv

2.2 Vectory

vectory是同步的,访问速度比ArrayList要大。同步完全可以由程序员自行控制
vectory的扩容是2倍,而ArrayList是1.5倍。

2.3 CopyOnWriteArrayList

List<String> list = new CopyOnWriteArrayList<>();

可以用来替代Vectory方法。

2.3.1 读写分离

写操作是加锁的,而且是新复制了一个数组。
适用于多读少写的操作,由于每次写都重新开辟一个数组,因此写的代价非常大。
缺点还有读操作不能读取实时性的数据,因为部分写操作的数据还未同步到读数组中。

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}
final void setArray(Object[] a) {
    array = a;
}
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
    return (E) a[index];
}

2.4 LinkedList

继承了双向链表的接口。

在这里插入图片描述

2.4.1 成员域
transient int size = 0;

//指向头结点
    transient Node<E> first;

 //指向最后一个结点
    transient Node<E> last;
2.4.2 与ArrayList的比较
  • ArrayList支持随机访问,而LinkedList不支持
  • LinkedList的添加删除效率比ArrayList来的快(因为不需要扩容,整体挪动数组)
2.4.3 remove(int index)、remove(Object obj)方法

支持这两种从某个位置删除的方法,源码中的处理方式都是遍历得到该位置的Node,然后调用unlink(node)的方式,维护pre、next指针。

public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
 public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
2.4.3 迭代器Iterator

测试了迭代器的删除。正序和逆袭都是可以的。


public class LinkedListTest {
   static void PrintLinkedList(LinkedList<Integer> list) {
       Iterator<Integer> iterator = list.iterator();
       System.out.print("begin : ");
       while (iterator.hasNext()) {
           System.out.print(" " +iterator.next());
       }
       System.out.println();
   }
   public static void main(String[] args) {
       LinkedList<Integer> list = new LinkedList<>();
       list.add(2);
       list.add(3);
       list.add(5);
       list.add(0);
       Iterator<Integer> iterator = list.iterator();
       //Iterator<Integer> iterator = list.descendingIterator();
       int t = 0;
       while (iterator.hasNext()) {
           // 打印完毕后, 游标指向下一位, 打印5 指向0
           System.out.println(iterator.next());
           t++;
           if (t == 3) {
               // 删除上一位,也就是删除5
               iterator.remove();
               PrintLinkedList(list);
           }
       }
   }
}
总结LinkedList

从头尾进行遍历,有两种方式,第一种是使用头尾指针,不断地进行遍历。 另一种是使用迭代器。
LinkedList封装了remove方法,如果希望从头尾进行删除,那么使用其包装好的remove(), removeLast()方法。 如果是想边迭代边删除,就要使用迭代器中的remove,删除的是游标上一个结点。

2.5 PriorityQueue

2.5.1 数据域
// 堆数组
transient Object[] queue; // non-private to simplify nested class access

// 大小
    private int size = 0;

   // 比较器,默认小根堆,可以通过构造方法进行修改
    private final Comparator<? super E> comparator;

2.5.2 对象的 add、poll、peek

都是堆相关的方法

add、offer:添加到size的位置,然后向上调整堆。

 public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
        if (i == 0)
            queue[0] = e;
        else
            siftUp(i, e);
        return true;
    }

poll: 把元素0 和 元素 --size 进行交换, 之后siftDown(0,x)向下调整堆

 public E poll() {
        if (size == 0)
            return null;
        int s = --size;
        modCount++;
        E result = (E) queue[0];
        E x = (E) queue[s];
        queue[s] = null;
        if (s != 0)
            siftDown(0, x);
        return result;
    }

此外,还提供了remove(object) , 这个方法也好理解,遍历堆数组找到i,然后把i和最后一位进行交换,然后调整i位置。

private E removeAt(int i) {
        // assert i >= 0 && i < size;
        modCount++;
        int s = --size;
        if (s == i) // removed last element
            queue[i] = null;
        else {
            E moved = (E) queue[s];
            queue[s] = null;
            siftDown(i, moved);
            if (queue[i] == moved) {
                siftUp(i, moved);
                if (queue[i] != moved)
                    return moved;
            }
        }
        return null;
    }
2.5.3 迭代器Iterator

迭代器的iterator遍历到的是堆数组的顺序,并不具备有序性。因此最好不用

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值