1. Vector,ArrayList,LinkedList区别
a.版本号:Vector是 JDK1.0, 后面两个是JDK1.2
b.底层实现:前两个底层是数组,LinkedList是链表
c.线程安全:Vector属于线程安全的集合,arrayList,LinkedList线程不安全
// vector的 add(),remove(),get()方法加的 synchronized锁
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
/**
* Removes the first occurrence of the specified element in this Vector
* If the Vector does not contain the element, it is unchanged. More
* formally, removes the element with the lowest index i such that
* {@code (o==null ? get(i)==null : o.equals(get(i)))} (if such
* an element exists).
*
* @param o element to be removed from this Vector, if present
* @return true if the Vector contained the specified element
* @since 1.2
*/
public boolean remove(Object o) {
return removeElement(o);
}
/**
* Inserts the specified element at the specified position in this Vector.
* Shifts the element currently at that position (if any) and any
* subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index > size()})
* @since 1.2
*/
public void add(int index, E element) {
insertElementAt(element, index);
}
/**
* Removes the element at the specified position in this Vector.
* Shifts any subsequent elements to the left (subtracts one from their
* indices). Returns the element that was removed from the Vector.
*
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
* @param index the index of the element to be removed
* @return element that was removed
* @since 1.2
*/
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
int numMoved = elementCount - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--elementCount] = null; // Let gc do its work
return oldValue;
}
e:扩容机制:vector2倍扩容,arrayList1.5倍扩容
2.HashSet(无序存储,实际上就是HashMap):底层为数组
允许为空,且不能重复,元素乱序存储
判断重复依据 HashCode() + equals()
Object: public native int hashCode():返回对象地址的hash码
两个对象相等判断:必须hashCode()与equals()均返回为true才认为相等
3.TreeSet(有序存储):底层为红黑树(平衡二叉树的一种)
TreeSet不允许为NULL,有序存储
TreeSet判断重复元素由CompareTo()来实现
自定义类要想使用TreeSet,必须覆写Comparable接口,无需再覆写 equals(),hashCode()方法
3.HashTable()
版本号:JDK 1.0
HashTable() key,value 均不为空
线程安全:在增删改查等方法上加锁(synchronized,同Vector)
Hashtable 存储的内容式键值对(key-value)映射,其底层实现是一个Entry数组+链表
key,value都不可以为空。此外,Hashtable中的映射不是有序的
底层是:采用数组+链表来实现的,负载因子设置0.75
4. HashMap子类(*****)
HashMap允许key,value为null,value允许多个为null
源码解析:
HashMap内部实现:数组+链表
数组被分为一个个的桶(bucket),通过hash址决定键值对在数组的寻址;hash相同的键值对,以链表形式存储
当链表大小超过阈值(TREEIFY_THRESHILD=8),将其树化成(红黑树)
初始化默认负载因子为 0.75
HashMap采用lazy_load,在初次使用时对table初始化
resize() 方法:
负责创建初始化存储表格或者在容量不满足需求时扩容
初始化时桶大小为16
HashMap判断何时需要扩容:
当向容器中添加元素的时候,会判断当前容器的个数,如果大于等于阈值,即大于等于门限值的时候,就要自动扩容了
a.门限值 = 负载因子 * 容量 (什么时候需要调正容量)
b.门限值通常以倍数调整(newThr = oldThr << 1),当元素个数超过门限值大小时,调整Map大小
c. 扩容后将老数组元素拷贝到新数组
容量和负载因子值的设置:
容量: public HashMap(int initialCapacity):设置容量
预设容量大小需要满足 > “预估元素数量/负载因子” 同样是2的幂次方
负载因子:
a.不推荐改变,默认负载因子符合通用场景需求
b. 不要设置超过0.75的值,否则会显著增加Hash冲突,降低 HashMap性能
c.也不要设置小于0.75的值,否则会引发频繁的扩容,影响访问性能
树化: JDK1.8 为什么引进树化
安全问题:当对象的Hash值冲突时,会放在一个桶中以链表的形式存储,链表过程会严重影响存取的性能
同一个桶中元素个数 > 8,会尝试进行树化
如果容量小于 MIN_TREEIF_CAPACITY,只会再次调用resize()进行扩容
如果容量大于 MIN_TREEIF_CAPACITY,才会进行树化改造
非树化:当链表变为红黑树后,元素删除 n 次后,如果红黑树的节点个数 < UNTREEIF_THRESHOLD(默认为6),在下一次调用resize()后,又会将红黑 树变为链表
5. HashMap与Hashtable区别
Hashtable是早期Java类提供的一个哈希表实现的,本身是同步的,不支持null键和值,由于同步导致的性能开销,所以很少被推荐使用
HashMap JDK1.2主要区别在于HashMap不是同步的,支持null键和值,通常情况下,HashMap进行put或者get操作,可以达到常数时间的性能,所以它是绝大多数利用键值对存取场景的首选
6.ConcurrentHashMap JDK1.7的实现
a.分离锁。 将内部结构分段 (16个:Segment),存放的HashEntry数组,hash相同的条目也按照链表存储。并发操作的时候,只锁定条目对应的Segment 段,有效避免类似Hashtable整体同步的问题,大大提高性能
b. HashEntry内部使用被volatile修饰的value字段来保证内存可见性
构造时,Segment数量由concurrentcylevel决定,默认为16,必须为2的幂次方
6.1 ConcurrentHashMap JDK1.8实现
1.总体结构上与HashMap变得非常相似,大的桶数组,内部为链表结构。虽然内部仍有Segment定义,但是无任何结构上的用途
2.因为不再使用Segment,初始化简单,修改成为 lazy_load,有效避免初始开销
3.数据存储使用volatile关键字,保证内存可见性
4.使用CAS操作,在特定场景下使用无锁并发操作
7. Map的遍历: entrySet,迭代器
a.通过Map.keySet遍历 key 和 value
for(String key : map.keySet()){
String value = map.get(key);
System.out.println(key+value);
}
b.通过 Map.entrySet 使用迭代器 iterator 遍历 key 和 value
Iterator<Map.Entry<String,String>> iterator =
map.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry<String,String> entry = iterator.next();
System.out.println(entry.getKey()+entry.getValue());
}