·ArrayList和Vector的区别
这两个类都实现了List接口(List接口继承了Collection接口),都是有序集合
1.线程安全
Vector使用了Synchronized来实现线程同步,是线程安全,ArrayList是非线程安全
2.性能
ArrayList在性能方面优于Vector
3.扩容:ArrayList和Vector都会根据实际的需求动态扩容,Vector每次增加1,ArrayList每次增加50%
Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量时间。
ArrayList不是同步的,所以不需要保证线程安全时建议使用ArrayList
·插入数据时,ArrayList、LinkedList、Vector的速度,三种方法的性能和特性
这三种方法底层的实现都是使用数组方式存储数据,数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素 要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢
1.Vector中的方法优于加了synchronized修饰,因此Vector是线程安全的容器,但性能比ArrayList差
2.LinkedList是双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但插入数据时只需要记录当前项的前后项即可,因此,LinkedList插入速度较快
·多线程场景下如何使用ArrayList
ArrayList不是线程安全的,如果遇到多线程场景可以通过Collections的synchronizedList方法将其转换成线程安全的容器后再使用
List<String>synchronizedList=Collections.synchronizedList(list); synchronizedList.add("AAA"); synchronizedList.add("bbb"); for(int i=0;i<synchronizedList.size();i++){ S.o.p(synchronizedList.get(i)); }
·List和Set的区别
List和Set都继承自Collection接口
特点:
List:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引,常用的实现类有ArrayList, LinkedList和Vector。
Set:无序(存入和取出顺序有可能不同)容器,不可以存储重复元素,只允许存储一个null元素,必须保证元素的唯一性。Set接口常用实现类:HashSet、 LinkedHashSet、以及TreeSet。
List支持for循环,可以通过下标来遍历,也可以使用迭代器,但Set只能用迭代,因为无序,无法用下标来获取想要的值。
对比:
Set:检索元素效率低,删除和插入效率高,插入和删除不会引起元素位置改变
List:与数组类似,List可以动态增长,查找元素效率高,插入和删除元素效率低,会引起其他元素位置改变
·为何Set查找效率低
1.获取set元素,需要调用iterator方法,返回元素无序,因此需要遍历集合,时间复杂度O(N),效率低。
2.添加元素,调用add方法,底层实际上是调用HashMap的put()方法添加key-value,时间复杂度O(1),效率高
3.移除set元素,调用remove方法,底层实际是调用HashMap的remove方法删除Entry,时间复杂度O(1),效率高
·为何list插入和删除元素效率低,查询效率快。
list的实现类采用数组结构保存对象,所以查询效率高,list是有序的,数组的查询可以通过下标来遍历,而用list进行元素插入和删除则效率低,因为当
指向索引位置插入对象时,会同时将索引位置及之后的所有对象相应地往后移动一位,当删除指定索引对象的位置时,会同时将指定索引位置之后的所有对象往前移动一位,,如果指定索引位置的后面有大量对象时,会严重影响集合的操作效率。
·为何ArrayList的查询比LinkedList快,而LinkedList插入删除操作比ArrayList快
1.ArrayList的查询比LinkedList快
ArrayList的底层是数组,且数据在内存中是连续的一片空间,查询时,可以根据数组的(首地址+偏移量)下标,直接计算出想访问的第index个元素在内存 的位置,
LinkedList是数据结构中的链表,在内存中开辟的不是一段连续的空间,而是每个元素有一个[元素|下一个元素地址]的内存结构,当我们要查询元素时, 只能从首元素开始,依次获得下一个元素的地址。
时间复杂度表示,ArrayList的get(n)是O(1),LinkedList的get(n)是O(n)。
2.LinkedList插入删除比ArrayList快
·LinkedList底层是链表,就是当前元素和他的前一元素和后一元素有关,添加操作时只要把目的位置的前一元素和后一元素关联到要添加的元素即可,删 除元素只要把要删除元素的前一元素和后一元素的关联断掉即可,垃圾回收器会自动回收删除的元素。这种接链和断链就是链表的添加和删除
·ArrayList插入删除的时候需要把修改的节点之后的所有数据都向后移动,或者向前移动。
补充:LinkedList中的数据在内存中是松散的,每一个节点都有一个指针指向下一个节点,而增删的时候就是断开一个节点,然后插入删除之后再接起来。
·什么是哈希
将任意长度的消息压缩到某一固定长度的消息摘要的函数,散列值的空间通常远小于输入空间,不同的输入可能有相同的输出
特点:
所有散列函数都有如下特性,根据同一散列函数计算出的散列值如果不同,那么输入值肯定不同,根据同一散列函数计算出来的散列值如果相同,输入值也不一定相同
·什么是哈希冲突
当两个不同的输入值,根据同一散列函数计算出相同的散列值的现象,叫做哈希碰撞。
·HashMap的数据结构
数组加链表加红黑树(链地址法可以解决哈希冲突)
可以将拥有相同哈希值的对象组织成一个链表放在hash值所对应的bucket下
·hash()函数
对hashcode的优化,如果使用hashcode取余,那么相当于参与运算的只有hashcode的低位,高位没有起到任何作用,让hashcode取值出的高位也参与运算,进一步降低hash碰撞的概率,将这种操作称为扰动
static final int hash(Object key){ int h; return(key==null)?0:(h=key.hashCode())^(h>>16)//扰动计算,防止不同hashcode的高位不同,但低位相同,导致hash冲突 } tab[(n-1)&hash] static int indexFor(int h,int length){//h为hash计算,hash计算是将hashcode的高16位进行 return h&(length-1); }
JDK1.7中有4次位运算,5次异或运算,1.8中只有1次位运算和一次异或运算
作用:
1.对key的hashcode进行扰动计算,防止不同hashcode的高位不同,但低位相同导致hash冲突
2.如果不进行扰动计算,直接用hashcode来计算key在HashMap中的table[]中的index,那么只用hashcode的低位和数组长度进行与运算,很容易发生hash冲突
ex:
计算indexFor(key在数组中的位置)
tab[(n-1)&hash]
假设length为8,HashMap的默认初始容量为16
那么length-1=7二进制位0111
假设一个可以的hashcode=78897121二进制转换后为10010110011110111111110001
(n-1)&hash
后为
0000010010110011110111111110001 &
0000000000000000000000000000111
结果只是hash值的第三位和length进行与运算,如果让hash值的低三位更加随机,那么结果就更加随机,如何让hash值的低三位更加随机,让其与高位异或
当length=8,下标运算结果取决于哈希值的低三位
当length=16,结果取决于低四位
32,下标结果取决于低5位
2的N次方 结果取决于哈希值的低N位
由于和length-1运算,length绝大多数情况小于2的16次方,所以始终和hashcode的低16位参与运算,如果让hashcode的高16位也参与运算,会让下标更加散列。
如何让hashcode的高16位也参与运算,这时就用到hash方法,让他的hashcode和自己的高16位^运算
为什么hashmap的扩容为2的幂次
h&(length-1)
HashMap容量为16二进制位10000,length-1=01111
哈希值为1111
1111
&01111
01111 索引为15
如果HashMap的容量为15二进制位01111
length-1位1110
哈希值为1111和1110分别进行与运算
1111 1110
& 1110 &1110
1110 1110
不同的hashcode放在数组同一位置,造成hash冲突
2的幂次-1都是11111结尾,所以碰撞几率减少
如果length为2的幂次那么 length-1转为二进制为11111的形式,操作效率非常快
·在HashMap中要找到某个元素,需要根据key的hash值求得对应数组中的位置,如何算出这个位置,就是hash算法。
HashMap的
可以使用hashcode对数组长度取模运算,这样元素的分布相对来说比较均匀,但是模运算消耗很多,所以采用hashcode和长度与运算。
h&(length-1)
·h&(length-1)的作用
这是计算
·HashMap如何解决hash冲突的
1.使用链地址法来链接拥有相同hash值的数据
2.使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均
3.引入红黑树进一步降低遍历的时间复杂度,遍历更快
HashMap中的hash()方法,将hashcode的高位和低位混合起来,降低冲突概率
HashMap的扩容
当HashMap中的元素个数超过数组*0.75时就会扩容两倍
·为什么使用hashcode后,再进行equals,而不直接使用equals方法?
1.hashcode是计算对象的哈希值并返回整型数据,而计算机可以将整型数据转成二进制的形式如0101,计算机计算二进制数据是最快的。如果直接进行 equals 进行比对,那么需要浪费很多时间,先进行hashcode计算可以节省大部分的时间。
2.hashcode是计算对象的哈希值然后分配对象的位置,而equals方法是比较真正的对象。
Array和ArrayList有何区别
·Array可以存储基本数据类型和对象,ArrayList只能存储对象
·Array是指定固定大小的,ArrayList大小是自动拓展的
·Array内置方法没有ArrayList多,addAll,removeAll,iteration等方法只有ArrayList有
·Array转List:Arrays.asList(array)
·List转Array:list.toArray()
·Vector类
数据结构:动态数组,与ArrayList类似,但有所区别:
1.Vector是同步访问
2.Vector包含许多传统方法,这些方法不属于集合框架
·Collections和Collection的区别
1.java.util.Collection是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法,其直接继承接口有List和Set
2.Collection则是集合类的一个工具类。其中提供了一系列的静态方法,用于对集合中的元素进行排序、搜索以及线程安全等各种操作、
·compaeable和comparator的区别
1.comparable接口出自java.lang包,它有compareTo(Object obj)方法用来排序
2.comparator接口出自java.util包,它有compare(Object obj1,Object obj2)方法排序
总结:当我们需要对一个集合使用自定义排序时,我们要重写compareTo方法或compare方法,当我们需要对某一个集合实现两种排序方法,我们可以重写
compareTo方法和使用自制的Comparator方法,或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的 Collections.sort().
TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?
1.TreeSet
TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的comparaTo()方法,当插入元素时会回调该方法比较元素的大小。
2.TreeMap
TreeMap要求存放的键值对映射的键必须实现Comparable接口,从而根据键对元素进行排序
3.Collections工具类sort方法有两种重载形式
1.要求传入的待排序的对象实现了Comparable接口以实现元素的比较
2.不强制要求容器中的元素必须可以比较,但要求传入第二个参数,参数是Comparable接口的子类型(需要重写compare方法实现元素的比较),相当于一个 临时定义的排序规则,就是通过接口注入比较元素大小的算法,对回调模式的应用。