List 与Set接口
Collection 接口存储一组不唯一、无序的对象
List接口存储一组不唯一、有序(插入顺序)的对象
Set接口存储一组唯一、无序的对象
Map接口存储一组键值对象,提供key到value的映射
set接口中的实现类
HashSet:采用Hashtable哈希表存储结构
优点:添加速度快,查询速度快,删除速度快
缺点:无序
TreeSet
采用二叉树(红黑树)的存储结构
优点:有序(排序后的升序)查询速度比List快
缺点:查询速度没有HashSet快
java 容器 collection 和map的区别。collection中存储了一组对象,而map存储的是key、value,并且key不能重复。
Map:
HashMap:非线程安全、无序、key不重复。
TreeMap:非线程安全、有序、key不重复。
Hashtable:线程安全、无序、不重复。
ConcurrentHashMap:线程安全,有序、不重复。
Collection
set: 有序、不重复对象
TreeSet:非线程安全、有序、不重复。
HashSet:非线程安全、有序、不重复。
List: 按插入顺序、对象可重复
TreeList:非线程安全、按插入顺序、可重复。
ArrayList:非线程安全、插入顺序、可重复。
LinkedList:非线程安全、插入顺序、可重复。
Vector:线程安全、插入顺序、可重复。
Arraylist : 初始大小为10,原大小+ 原大小右移1位 = 1.5 扩容。oldCapacity + (oldCapacity >> 1)。插入删除时都会copy一份。
Vector: 直接2倍扩容。oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity)
HashMap: 初始16 ,2倍(2的n次幂)扩容.左移1位.newCap = oldCap << 1
为什么hashmap容量是2的n次幂?
1.方便进行 与 运算 (HashMap: 376行),与运算比取模运算效率高
2.在扩容之后涉及到元素的迁移过程,迁移的时候只需要判断二进制的前一位是0或者1即可。
如果是0,表示新数组和旧数组的下标位置不变,如果是1,只需要将索引位置加上旧的数组的长度值即为新数组的下标
ArrayList 源码分析
add(Object o )
/**
* 将当前数组大小+1传入 ensureCapacityInternal方法
* 将e 对象添加到 elementData 数组的 当前数组+1位置。数据的最后一位,非数组的末尾!!!
* 并且每次添加对象,数组的大小都会1.5倍扩张
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 当前数组大小 + 1
elementData[size++] = e;
return true;
}
/**
* 判断下 elementData 是否是空的,空的就取最小值10
* 如果当前数组+1 的大小大于10,就取当前数组+1
* @param minCapacity
*/
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 如果这个数组是空的
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //初始化一个 10 的大小,或者数组+1 个
}
ensureExplicitCapacity(minCapacity);
}
public static int max(int a, int b) {//a=10 ,b=当前数组大小
return (a >= b) ? a : b;
}
/**
* 如果当前数组+1 大于 elementData的length 执行grow
* @param minCapacity
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) //一般是true。当前数组+1 大于 当前数组大小
grow(minCapacity);
}
/**
* 先获取 elementData 的length,然后*1.5倍。如果这个值比当前数组+1 小,就取当前数组+1 minCapacity
* 如果 newCapacity 比 最大值大,就取 最大值或者 最大值减8
* 将 elementData 数组扩大至 当前数组的 1.5 倍
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //当前数据的大小
int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍。右移运算,不懂的可以for循环打出,数组新值
if (newCapacity - minCapacity < 0) //如果 新值 小于当前数组+1
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 大于最大值 就返回 integer最大值,否则返回integer最大值-8
*/
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
通过源码可以看到,arraylist 添加对象的时候,初始大小是10,然后会进行每次1.5倍的扩张。
而且添加的时候,是将现有的数组进行copy一份的。所以插入效率不高。
remove(object o)
/**
* 先进行遍历,获取到要删除的位置的索引。
* 然后调用系统的删除方法
*/
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
/**
* 进行copy删除
*/
private void fastRemove(int index) {
modCount++;
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
}
可以发现,remove效率也不高,先遍历再copy一份删除。
LinkedList
/**
* 先要取出next,然后再取出next的item进行比较
*/
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
/**
* 直接链到最后面
*/
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
插入效率高、删除速度快。遍历和随机访问效率低。
为什么说linkedlist是双向链表?
因为在linkedlist中存储的Node,会记录上一个下一个node
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
Vector
add: 添加时进行当前大小的*2扩增,并且是线程安全的。
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity)
* 两倍扩增
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
Queue 和 list的区别
Queue 和list 的区别。queue 添加了很多对线程友好的api : offer、peek、poll。 put、take 会阻塞
Queue :当队列满了的时候调用 add方法会抛出异常,同理remove也会抛出异常。
queue提供了offer、peek、poll的支持。
offer 添加,返回值boolean,不会报错
peek 返回队列的第一个(头)元素,不会删除
poll 返回队列头元素并删除
ArrayBlockingQueue 实现了 BlockingQueue, BlockingQueue 继承自Queue
ArrayBlockingQueue 有take方法,得到头元素并删除,如果queue是空的就进行等待(阻塞),需要捕获异常
ArrayBlockingQueue 有put方法,添加时如果满了就进行等待(阻塞),需要捕获异常。
ArrayBlockingQueue 是线程安全的,方法里面加入了 ReentrantLock
Arrays
用于对数组操作的工具类
java.util.Collections 类!! 而不是接口 Collection
这个类是专门对实现了List的类提供了一些静态方法支持,以及提供了将非同步Set、List、Map转换成同步的方法
sort()排序: Collections.sort(list);
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
如果是自定义的对象,需要实现 Comparable 接口
binarySearch : 对半查找
//很明显会调用 iteratorBinarySearch
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
/**
* 这里使用了一个无符号右移(逻辑右移) int mid = (low + high) >>> 1;
* 上面一行的 运算结果 为((low + high)) 的一半
* 所以这个查找也叫对半查找
*/
private static <T>
int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
{
int low = 0;
int high = list.size()-1;
ListIterator<? extends Comparable<? super T>> i = list.listIterator();
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = get(i, mid);
int cmp = midVal.compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
//遍历查找
private static <T> T get(ListIterator<? extends T> i, int index) {
T obj = null;
int pos = i.nextIndex();
if (pos <= index) {
do {
obj = i.next();
} while (pos++ < index);
} else {
do {
obj = i.previous();
} while (--pos > index);
}
return obj;
}
shuffle:随机排序
public static void shuffle(List<?> list) {
Random rnd = r;
if (rnd == null)
r = rnd = new Random(); // harmless race.
shuffle(list, rnd);
}
reverse:倒序
public static void reverse(List<?> list) {
int size = list.size();
if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {
for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)
swap(list, i, j);
} else {
// instead of using a raw type here, it's possible to capture
// the wildcard but it will require a call to a supplementary
// private method
ListIterator fwd = list.listIterator();
ListIterator rev = list.listIterator(size);
for (int i=0, mid=list.size()>>1; i<mid; i++) {
Object tmp = fwd.next();
fwd.set(rev.previous());
rev.set(tmp);
}
}
}
copy:从一个集合复制到另一个集合
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}