Java集合
注:题目抄的,理解和代码测试是自己写的
List,Set和Map差别
- List的存储对象不唯一,可以有多个相同的对象存储进去
- Set的存储对象唯一,不能存储已经有的一样的对象
- Map有key(键)和value(值),键会映射到值上面。key是不能重复的,但是value可以重复。一个键只能映射一个值。但是一个值可以有多个映射。
ArrayList和LinkedList区别
ArrayList
- 线程安全方面:都是不同步的,都是线程不安全的。
- 底层的结构:ArrayList的底层是Object数组,LinkedList的底层是双向链表。
- 增删改查的时间复杂度:对于ArrayList,增加为O(1),删除和插入都为O(n-i),i为插入或者删除的位置。因为要先到第i个位置,然后再
移动后面的n-i个对象。对于LinkedList,增加为O(1),删除或者插入都为O(n),因为每次都只要移动到第i个位置就可以了。
4.是否支持快速访问:ArrayList底层为数组,所以支持get(index),快速访问。但是LinkedList不支持下标快速访问。有RandomAccess接口声明的类就支持快速访问
5.占内存的原因:ArrayList因为底层是数组,所以每次结尾都会有多余空间空出。LinkedList链表的话,每个对象要有first,next等,所以对象相对ArrayList占空间。
6.遍历方式的选择
ArrayList因为被RadomAccess接口声明,它是支持快速访问的。所以优先选择for循环遍历。
LinkedList不支持快速访问,所以可以用iterator或者foreach循环(foreach也是用iterator实现的)。
public static void main(String[] args) throws IOException {
List<Integer> list = new LinkedList<>();
list.add(8);
list.add(7);
list.add(6);
list.add(4);
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
ArrayList和Vector
ArrayList
线程不同步的,所以效率搞。单线程适用。
Vector
线程同步,所以效率低。多线程适用。
ArrayList的扩容机制
ArrayList底层是数组,这个数组初始值为10。size用来表示该list存储对象的个数。如果len用来表示数组的大小。每次调用add方法就会调用扩容方法。扩容方法会判断size+1是否>len,如果大于就会扩容,一般是将数组大小扩容至原来的1.5倍大小。
HashMap和HashTable区别
1.HashMap线程不安全,HashTable线程安全
2.HashMap运行效率高于HashTable
3.HashMap的key支持一个null值,HashTable不支持。HashTable基本淘汰
4.HashMap的对于的链表长度大于阈值8时就会转变为红黑树。
注意:HashMap的哈希数组大小总是为2的幂-1,这样可以最大程度减少hash值的碰撞
HashMap的线程安全化
Map map = new HashMap<String,String>();
List list = new ArrayList();
Set set = new HashSet();
Collections.synchronizedMap(map);
Collections.synchronizedList(list);
Collections.synchronizedSet(set);
HashMap和HashSet的区别
1.接口:
HashMap实现了Map接口,HashSet实现了Set接口。HashSet是HashMap实现的。代码简单。
2.存储方式:
HashMap采用键值对存储,键唯一。HashSet采用唯一对象存储数据。
3.hashcode值计算:
HashMap的hashCode是通过key值计算的。HashSet是通过传入的对象计算的。当对象的hash值一样时使用equals判断是否为同一个对象。
4.添加元素区别:
HashMap使用put(key,value)。HashSet使用add(value)添加对象。
HashSet如何检查
当对象加入HashSet的时候,先会判断这hashcode值有没有已经存在,若是不存在则认为没有数据重复,可以添加进去。当hashcode值有重复,则会调用equals方法,如果还是返回true就会报错,添加不进去。
public class Test2 {
public static void main(String[] args) throws IOException {
//接下来测试
//因为重写了equals和hashcode方法,才可以实现。若是没有重写hashcode方法就不能实现
//因为系统的hashcode方法只要引用不一致,hash值就不可能相等
Set<Demo> set = new HashSet();
Demo demo1 = new Demo("sb?",50);
Demo demo2 = new Demo("sb?",50);
Demo demo3 = new Demo("sb?",66);
set.add(demo1);
set.add(demo2);
set.add(demo3);
System.out.println(set.size());//大小只有2
}
}
class Demo{
public String name;
public int age;
public Demo(){
}
public Demo(String name,int age){
this.name = name;
this.age = age;
}
//重写equals方法
@Override
public boolean equals(Object obj) {
return (this.name.equals(((Demo)obj).name)) && (this.age == ((Demo)obj).age);
}
//重写equals后不重写hashCode方法是没意义的
//当hashCode方法重写后,内容一样,指向不一的对象放入Set才会报错
@Override
public int hashCode() {
//随便写的
return (this.age+this.name.length())*68;
}
}
HashMap底层实现
jdk1.8以前
HashMap底层使用的是数组加链表,也就是链表散列。这里是如何得到hash值的呢。通过key来获取hashcode值,然后得到的hashcode通过扰动函数后得到hash值.hash值&(n-1)得到的数值就是数组下标的位置了。得到下标后就会到对应的链表,然后判断有没有key一样的map,如果有则会覆盖,没有则会在链表最后面添加。
jdk1.8即以后
再这之后当链表的长度大于阈值8的时候,链表就会为红黑书,这样可以加快搜索的时间。
HashMap的数组长度为2的幂-1
原因:
这样可以减少hash的碰撞,防止一个下标的链表存放过多数据。
多线程下的HashMap死循环问题和解决
问题
在多线程下使用HashMap的话容易导致链表变为循环链表,从而出现死循环问题。jdk1.8以后解决了这个问题,但是多线程下不推荐使用HashCode
解决
不使用HashMap,使用ConcurrentHashMap。
ConcurrentHashMap,HashTable,HashMap
1.ConcurrentHashMap和HashTable相对HashMap来说,都实现了线程同步。而HashMap线程不同步。
2.ConcurrentHashMap使用的是散列数组,所以锁也是针对散列数组的散列锁。将一个数组分为了多个散列数组,然后分开加锁,可以提高多线程的运行效率。而HashTable没有散列数组,虽然也是线程安全的,但是一个锁将整个数组全部锁起来,导致了运行效率低下问题。
ConcurrentHashMap的底层实现
jdk1.7:(使用散列数组,Segment可重入锁数组和HashEntry链表)
首先底层是一个散列的Segment数组,Segment基础了ReentrantLock,是可重入锁。
一个ConcurrentHashMap里面只有一个Segment数组,每个Segment元素下面又存放着HashEntry,用于链表存放。
jdk1.8(采用CAS比较并交换算法和syncronized实现)
1.8即以后取消了Segment可重入锁,而是使用CAS和syncronized实现线程同步。同样但阈值超过8也会将链表转换为红黑树。而且syncronized只会锁住每个节点的头结点。这样效率就会高上N倍。这样只要hash不冲突就不会有线程阻塞。
Comparable和Comparator的区别和使用
注意:Comparable的compareTo方法默认只有一个参数,所以不能用匿名内部类方式。Comparator的有两个参数,所以哟个匿名内部类.
Comparable:要排序的对象实现这个接口,并重写compareTo方法
public class Test2 {
//运行结果是从小到大排序
public static void main(String[] args) throws IOException {
Demo demo1 = new Demo("sdf",656);
Demo demo2 = new Demo("gwe",515);
Demo demo3 = new Demo("hrw",8871);
List<Demo> list = new ArrayList<>();
list.add(demo1);
list.add(demo2);
list.add(demo3);
Collections.sort(list);
for(int i = 0;i < list.size();i++){
System.out.println(list.get(i).age);
}
}
}
class Demo implements Comparable<Demo>{
public String name;
public int age;
//重写compareTo方法
@Override
public int compareTo(Demo o) {
return this.age - o.age;
}
public Demo(){
}
public Demo(String name,int age){
this.name = name;
this.age = age;
}
}
Comparator
public class Test2 {
运行结果是从小到大排序
public static void main(String[] args) throws IOException {
Demo demo1 = new Demo("sdf",656);
Demo demo2 = new Demo("gwe",515);
Demo demo3 = new Demo("hrw",8871);
List<Demo> list = new ArrayList<>();
list.add(demo1);
list.add(demo2);
list.add(demo3);
Collections.sort(list, new Comparator<Demo>() {
@Override
public int compare(Demo o1, Demo o2) {
return o1.age - o2.age;
}
});
for(int i = 0;i < list.size();i++){
System.out.println(list.get(i).age);
}
}
}
class Demo{
public String name;
public int age;
public Demo(){
}
public Demo(String name,int age){
this.name = name;
this.age = age;
}
}
集合底层数据结构组成和使用总结
数据结构组成
Collection
List
- ArrayList:底层为Object数组
- Vector:底层为Object数组
- LinkedList:底层为双向链表
Set - HashSet:底层是基于HashMap实现的
- LinkedHashSet:底层基于LinkedHashMap
- TreeSet:底层是红黑书(有序,唯一)
Map
- HashMap:数组+链表,jdk1.8以后,链表长度大于阈值8后会自动转换为红黑树
- LinkedHashMap:在原来HashMap的基础上又加了一个双向链表
- HashTable:数组+链表
- TreeMap:底层是红黑树
使用场合
- 当需要用到键值对的时候就要用Map结构。需要排序用TreeMpa,不需要排序用HashMap
- 当不需要用到键值对,但是要有唯一值的时候用Set。TreeSet排序,HashSet不排序
- 当不需要键值对也不需要值唯一的时候就用List。常用ArrayList,因为可以实现快速查询。
注意:TreeSet和TreeMap实现对象的自动排序都要让对象继承Comparab接口,重写compareTo方法
public class Test2 {
public static void main(String[] args) throws IOException {
Demo demo1 = new Demo("sdf",656);
Demo demo2 = new Demo("gwe",515);
Demo demo3 = new Demo("hrw",8871);
Set<Demo> set = new TreeSet();
set.add(demo1);
set.add(demo2);
set.add(demo3);
Iterator iterator = set.iterator();
while(iterator.hasNext()){
//输出结果为 515 , 656 , 8871
System.out.println(((Demo)iterator.next()).age);
}
}
}
class Demo implements Comparable<Demo>{
public String name;
public int age;
@Override
public int compareTo(Demo o) {
return this.age - o.age;
}
public Demo(){
}
public Demo(String name,int age){
this.name = name;
this.age = age;
}
}