关于list,map,set的区别
首先他们都是集合,这里就先引申一下集合跟数组的区别,主要的区别还是在于数组是静态的,类型固定的。而集合是动态的,存放的类型可以不是一种。
接下来说一下集合三者的区别
他们都是一个接口
list有三个实现类,分别是:
LinkedList,基于链表实现,链表内存是散列的,增删快,查找慢;
ArrayList,基于数组实现,非线程安全,效率高,增删慢,查找快;
Vector,基于数组实现,线程安全,效率低,增删慢,查找慢;
下面来说一下ArrayList怎么个线程不安全,阅读ArrayList的add操作的源码,可知:
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
这里是因为扩充size跟加集合是两步操作,既然是两步,有多个线程操作就会出现不同步,例如数据会被后一个线程覆盖,或者出现越界
现在说一下什么是链表,顾名思义,就是链式的,链表中的每一个元素成为每一个节点,节点就包括了储存数据元素的数据域跟储存下一个节点的指针域
链表又分为单向链表,双向链表,循环链表,双向链表的有两个链域分别是存储了前驱结点地址的左链域跟存储了后继节点地址的右链域。
关于链表的算法运用可以参考约瑟夫问题
cpu访问数据的顺序--》寄存器--》缓存--》内存--》外存
链表跟数组的对比,链表存储是散列的,而数组是连续的内存地址,所以查找操作他们的时间复杂度都是O(n),但是数组明显是要快的,因为CPU缓存会把一片连续的内存空间读入,而缓存的速率要比内存要快,CPU取数据,处理数据,都要寄存在寄存器中处理,缓存就是把内存中提取出来的数据暂时保存。但是插入跟删除操作,由于链表是是不连续的内存地址,而数组是连续的内存地址,其他结点都要同时向前或者向后移动,链表则不需要。
这里再说一下vector,因为他为了实现线程安全加了synchronize,所以查找就耗费很多时间,所以都比arraylist慢
下面再说一下map
map.entrySet()会把map的所有键值对拿出来放到集合里面
然后我们就可以这样遍历map
for(Map.Entry<String,Object>entry:map.entrySet())
map主要有两大实现类:Hashmap跟TreeMap
实际中我们用得最多得就Hashmap
简单说下HashMap的实现原理:
首先有一个每个元素都是链表(可能表述不准确)的数组,当添加一个元素(key-value)时,就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度太长时,链表就转换为红黑树,这样大大提高了查找的效率。
这里讲一下如何提高hashmap的性能,通常构建HashMap的时候都会初始化数组,最消耗性能的时候也就是扩容数组的时候,如果我们能一开始大概估算到插入数据的量,然后可以自定义初始化数组长度
现在来讲一下什么叫红黑树