ArrayList与LinkedList异同
1.是否保证线程安全:ArrayList和LinkedList都是不同步的,也就是不保证线程安全。
2.底层数据结构:ArrayList底层使用的是Object数组;LinkedList底层使用的是双向循环链表数据结构。
3.插入和删除是否受元素位置的影响:1.ArrayList采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。
比如:执行add(E e) 方法的时候,ArrayList会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)
2.LinkedList采用链表存储,所以插入、删除元素时间复杂度不受元素位置的影响,都是近似O(1)而数组为近似O(n)。
4.是否支持快速随机访问:LinkedList不支持高效的随机元素访问,而ArrayList支持。快速随机访问就是通过元素的序号快速
获取元素对象(对应于get(int index)方法)。
5.内存空间占用:ArrayList的空间浪费主要体现在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在
它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
补充内容:RandomAccess接口
public interface RandomAccess{}
这个接口里什么都没有,其实标识实现这个接口的类具有随机访问功能。
在binarySearch()方法中,它要判断传入的list是否RamdomAccess的实例,如果是,调用indexedBinarySearch()方法,如果不是
那么就调用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);
}
ArrayList实现了RandomAccess接口,而LinkedList没有实现。并不是说ArrayList实现RandomAccess接口才具有快速随机访问功能的!
补充list的遍历方式:1.实现了RandomAccess接口的list,优先选择普通for循环,其次foreach
2.未实现RandomAccess接口的list,优先选择iterator遍历(foreach遍历底层也是通过iterator实现的),大size 的数据千万不要使用foreach循环。
LinkedList:数据结构基础之双向链表
双向链表也叫双链表,是链表的一种,它的每个数据节点都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
ArrayList与Vector区别:
Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上
耗费大量时间。
ArrayList不是同步的,所以在不需要保证线程安全时建议使用ArrayList。
HashMap的底层实现:
static final int hash(Object key) {
int h;
// key.hashCode():返回散列值也就是hashcode
// ^ :按位异或
// >>>:无符号右移,忽略符号位,空位都以0补齐
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
HashMap和HashTable的区别:
1.线程是否安全:HashMap是非线程安全的,HashTable是线程安全的;HashTable内部的方法基本都经过synchronized修饰。
2.效率:因为线程安全的问题,HashMap要比HashTable效率高一点。另外,HashTable基本被淘汰,不要在代码中使用它。
3.对Null key和Null Value的支持:HashMap中,null可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为null。
但是在HashTable中put进的键值只要有一个null,直接抛出NullPointerException。
4.初始容量大小和每次kuo扩充容量大小的不同:1.创建时如果不指定容量初始值,HashTable默认的初始大小为11,之后
每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
2.创建时如果给定了容量初始值,那么HashTable会直接使用你给定的大小,而HashMap会将其扩充为2的幂次方大小。
HasMap 中带有初始容量的构造函数:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
HashSet和HashMap区别:
HashSet底层就是基于HashMap实现的(HashSet的源码非常非常少,因为除了clone()方法、writeObject()方法、readObject()方法就是HashSet自己不得不实现之外,其他方法都是直接调用HashMap中的方法)。
HashMap HashSet
实现了Map接口 实现了Set接口
存储键值对 仅存储对象
调用put()向map中添加元素 调用add()方法向Set中添加元素
HashMap使用键(key)计算Hashcode HashSet使用成员对象来计算hashcode值,对于两个对象 来说hashcode可能相同,所以equals()方法来判断对象的
HashMap 相对于HashSet较快,因为它使用唯一的键获取对象 相等性,如果两个对象不同的话,那么返回false。
ConcurrentHashMap和Hashtable的区别:
ConcurrenHashMap和Hashtable的区别主要体现在实现线程安全的方式上不同。
1.底层数据结构:JDK1.7的ConcurrenHashMap底层采用分段的数组+链表实现,JDK1.8采用的数据结构跟HashMap1.8的结构一样,
数组+链表/红黑二叉树。
2.实现线程安全的方式(重要):1.在JDK1.7的时候,ConcurrentHashMap(分段锁)对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。(默认分配16个Segment)
集合框架底层数据结构总结:
Collection
1.List
ArrayList:Object数组
Vector:Object数组
LinkedList:双向循环链表
2.Set
HashSet(无序,唯一):基于HashMap实现的,底层采用HashMap来保存元素
LinkedHashSet:inkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的.
TreeSet(有序,唯一):红黑树。(自平衡的排序二叉树)
3.Map
HashMap:JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链 法”解决冲突)
LinkedHashMap:LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成详细可以查看:https://www.imooc.com/article/22931
HashTable:数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的。
TreeMap:红黑树(自平衡的排序二叉树)