前言
学习了一些java中的容器,记下总结。
正文
List
ArrayList
ArrayList 底层是由数组实现的,数组的优点是查询效率高,但是插入删除的效率很低,因为每删除一个元素就将该元素后面的所有元素整体往前移动。
ArrayList中声明了Object[] elementData数组,默认大小为10,每当数据插入时首先验证数组是否已经满了,若是数组已满则进行扩容,每次扩容大小为数组当前大小的一半,使用移位运算符>>1。
扩容则是新建一个数组,大小为新长度,然后将原数组数据拷贝到新数组,再指向新数组。类似如下
//在数据满的时候进行扩容
if (i == elementData.length) {
Object[] newArray = new Object[elementData.length + (elementData.length>>1)];
System.arraycopy(elementData, 0, newArray, 0, elementData.length);
elementData = newArray;
}
LinkedList
LinkedList是由双向链表实现的,链表的好处和数组互补,链表插入删除等操作效率很高,因为不需要像类似ArrayList一样将数组整体的移动,同样的查询效率就不高了,需要一个一个遍历。
LinkedList有个Node内部类,包含前一个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;
}
}
其他类似于ArrayList。
Vector
Vector底层是用数组实现的List,相关的方法都加了同步检查,因此线程安全效率低。
List使用
1.需要线程安全时,用Vector。
2.不存在线程安全问题时,查找较多的使用ArrayList。
3.不存在线程安全问题时,增加或删除较多时使用LinkedList.
Map
HashMap
HashMap是键值对来存储的,是综合了数组和链表的存储方式,无序。添加数据时,先对数据key值进行计算得出数组存储的下标,再放入数组相应位置,如果相应位置不空,则链接在单向链表的最后面。
put方法类似于下面代码:
public void put(K key, V value) {
Node<K,V> newNode = new Node();
newNode.key = key;
newNode.value = value;
newNode.next = null;
boolean keyRepeat = false;
Node lastNode = null;
newNode.hash = myHash(key.hashCode(), table.length);
Node temp = table[newNode.hash];
if (temp == null) {
table[newNode.hash] = newNode;
size ++;
} else {
//遍历进行操作
while (temp != null) {
//重复就覆盖
if (temp.key.equals(newNode.key)) {
temp.value = newNode.value;
keyRepeat = true;
break;
} else {
lastNode = temp;
temp = temp.next;
}
}
if (!keyRepeat) {
lastNode.next = newNode;
size ++;
}
}
}
//通过hashcode计算返回hash,length必须满足为2^n。
private int myHash(int hash, int length) {
System.out.println("my hash is:" + (hash & (length-1)) );
return (hash & (length - 1));
}
hash值一开始计算是按除取余来算,但是除法效率不高,
后面采用hashcode &(length-1)提高效率,再后来先移位>>再位与运算&
目的是让得出的数组下标更均匀,提高查询效率。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
对应链表长度大于8时单向链表就转换为红黑树,大大提高了查询效率。
TreeMap
TreeMap的实现是红黑树进行实现的,是有序的map。所以如果key值是自定义的对象,需要类实现Comparable接口,如Entry:
class Entry implements Comparable<Entry>{
int total;
int score;
@Override
public String toString() {
return "total:" + total + "score:" + score;
}
public Entry(int total, int score) {
this.total = total;
this.score = score;
}
@Override
public int compareTo(Entry o) {
if (this.score > o.score) {
return 1;
} else if (this.score < o.score){
return -1;
} else {
if (this.total > o.total) {
return 1;
} else {
return -1;
}
}
}
}
Set
Set的底层使用的其实就是Map,利用了Map键不能重复的性质。set的每一个元素都是存储在Map的Key值上,最多包含一个空值。
HashSet
底层使用HashMap实现。
TreeMap
底层使用TreeMap实现。
Collections辅助工具类
Collections是辅助工具类,可以对Collection接口下的集合进行排序和查找。
如对ArrayList:
List<String> list = new ArrayList<>();
for (int i = 0;i<10;i++) {
list.add("gao" + i);
}
System.out.println(list);
//按key值排序
Collections.sort(list);
System.out.println(list);
//逆序
Collections.reverse(list);
System.out.println(list);
//使无序
Collections.shuffle(list);
System.out.println(list);
Collections.sort(list);
System.out.println(list);
//Collections.binarySearch方法利用二分法查找元素并返回下标,是基于有序列表的查找。
// 如果使用shuffle方法后将不适用,除法再使用sort方法。
System.out.println(Collections.binarySearch(list, "gao1"));