归纳总结在这:https://blog.csdn.net/u014044812/article/details/48325307
collection不能直接继承,只能通过继承它的子接口set List;
如何遍历collection集合的元素?
Iterator it = collection.iterator(); // 获得一个迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一个元素
}
set接口是无序的,但元素不可重复;
hashset的底层就是hashmap,但是改写了put方法(变成了add方法):就算是equal和hashcode都相同,也不会更改原来的存进去的数据,从而实现了hashset中无重复元素;
List接口是有序的,但元素能重复;
Map就是依靠名-值;
ArrayList不是线程安全的,基于数组实现了一个自动扩容的动态数组;
ArrayList扩容机制:
默认的list长度是10;
每次调用add的时候,会去通过ensureCapacityInternal方法确保容量够了;
arraylist中实现的容量增加的方法;
在ensureCapacityInternal方法中可以看到每次都是扩容1.5倍;且采用的是Arrays.copyOf方法进行整体拷贝,如果频繁的扩容就会降低性能
可以在已知list长度的时候,指定arraylist初始化的长度,这样可避免自动扩容操作;
ArrayList和Vector都是采用数组来储存数据,都是采用下标查询数据,都不便于插入和删除数据(要前移和后移其他数据);Vector采用了synchronize(多线程情况下,会等前一个线程执行完毕在执行下一个线程),使得它比ArrayList效率更低;
LinkedList:底层是双向链表,遍历数据只能从头或者尾开始查询,非常不便;插入数据和删除数据不需要移动其他数据;
若想线程安全的用ArrayList的话可以用 copyonwriteArrayList:
copyonwriteArrayList总结:普通情况下和正常的arraylist增删改查一样,但是当进行了增删改操作时,这时copyonwirteArraylist会先copy出一个容器,然后对这个容器进行刚刚的增删改操作,如果再这个操作期间进行读操作,那么依旧读的是原来的值,而不是正在操作容器里的值,当增删改操作结束后,会把新容器的引用地址赋给原来旧容器的地址;
这个新容器是一个volatile数组,增删改操作是先获取互斥锁,修改数据完毕后再释放互斥锁,将数据更新到volatile数组达到保护数据效果;
Q&A:
- CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
- 那为什么不直接修改,而是要拷贝一份修改呢?
这是为了在“读”的时候不加锁。(以空间换时间的策略)
- 为了提升读取的效率,修改时不在原数据上修改,而是在复制的数组上修改,改完之后再设置回来,这样做就不会阻塞读的线程
栈Stack和队列Queue都可以用Deque来表示:
都用Deque<类型> deque = new LinkedList();来进行初始化
Deque当作栈使用:
入栈 push(E e)
出栈 poll()
查看栈顶 peek()
Deque当队列使用:
入队列 offer(E e)
出队列 poll()
查看队列首个 peek()