Java-day15

目录

锁和死锁

        产生死锁的原因

        产生死锁的条件是什么?

        解决死锁

集合容器整理

        1、Collection和Map的区别

        2、List、Set、Queue的区别

        3、Array和ArrayList的区别          

        4、ArrayList和LinkedList的区别          

        5、ArrayList和Vector的区别(带一下CopyOnWriteArrayList)

        6、Array怎么转换为ArrayList,ArrayList怎么转换成Array

        7、HashSet和TreeSet的区别      

        8、HashMap和HashTable的区别(带一下concurrentHashMap)                 

                        初始容量大小和扩容容量大小的区别:

        9、HashMap和TreeMap的区别

        11、HashMap的长度为什么是2的幂次方?

        12、什么是哈希碰撞/哈希冲突,怎么解决哈希冲突,HashMap采用的是什么策略

        13、HashMap为什么不直接使用key的HashCode()函数返回的哈希码作为落槽时的索引号 ?那HashMap是怎么解决的呢?

        14、==和equals的区别

        15、为什么重写了equals()方法,必须也要重写HashCode()方法

        16、能否使用任意的数据类型作为HashMap的key

锁和死锁

        锁是Java中用来保证线程操作原子性的一种极致

        锁是数据库中用来保证事物操作原子性的一种机制

        Java中的锁有synchronized和lock锁

        synchronnized是关键字,可以锁代码块,也可以锁方法

        lock是类(官方推荐),只能锁代码块

        我们把数据类型分为安全类型和不安全类型

        如果一个数据类型需要我们自己手动加锁来保证其操作的原子性,那么他就是线程的不安全的数据类型

        如果一个数据类型能够自己在方法中加锁来保证其操作的原子性,那么他就是线程安全的

线程不安全线程安全
ArrayList1.Vector        2.CopyOnWriteArrayList
HashMap1.Hashtable        2.ConcurrentHashMap
String,StringBuilderStringBuffer
int,IntegerAtomiclnteger
其他......

        产生死锁的原因

                两个线程手上各自持有一个对象的锁,在没有释放的情况下,去请求对方手上的锁

        产生死锁的条件是什么?

                1、互斥条件:锁要具有排他性,而不是共享的

                2、请求与保持:一个线程因为请求其他资源被阻塞的时候,对已获得的资源保持不释放

                3、不可剥夺条件:一个线程没有主动开锁释放资源之前,是不能被其他线程强行剥夺的

                4、循环等待:A线程持有资源a的锁,B线程持有资源b的锁,在互相不释放自己拥有的锁情况下,去请求对方持有的锁,这时候会形成双方循环等待,造成永久阻塞。

        解决死锁

                破坏任意一个条件即可

                1、破坏互斥条件:用共享锁,在同一时刻锁可以被多个线程持有

                2、破坏请求与保持条件:一次性申请所有的资源

                3、破坏不可剥夺条件:一个线程因为请求其他资源被阻塞的时候,主动释放已获得的资源

                4、破坏循环等待条件:所有的线程按照同样的顺序请求资源

集合容器整理

        1、Collection和Map的区别

                1、collection和Map是官方提供的集合容器的两大体系的顶层接口

                2、collection代表单元素集合体系

                3、Map代表kv键值对集合体系

                4、collection体系继承了lterable迭代器接口,所有的子类都提供了迭代器的实现,Map体系没有

        2、List、Set、Queue的区别

                1、List、Set、Queue都是collection体系下的子接口,分别代表三个子体系

                2、List体系的特点是有序、不唯一

                3、Set体系的特点是无序、唯一

                4、Queue体系的特点是先入先出

        5、队列和栈的区别

                1、队列是一种FFO(first in first out)先入先出的结构

                2、栈是一种FLO(first in last out)先入后出的结构

                        Java集合体系中的LinkedList类可以实现队列和栈的结构

                        在链表的头部插入,尾部取出或者尾部插入,头部取出,就是队列(插入和取出在不同方向进行)。

                        在链表头部插入,头部取出或者尾部插入,尾部取出,就是(插入和取出在相同方向进行)。

        3、Array和ArrayList的区别          

                        Array是数组,ArrayList是类

                        Array是定长的(需要手动扩容),ArrayList长度可变(使用过程中自动扩容)

                        ArrayList的底层是Array。

        4、ArrayList和LinkedList的区别          

                        1、底层数据结构实现:ArrayList底层数据结构是动态数组,而LinkedList的底层数据结构是双向链表

                        2、随机访问(即读)效率:ArrayList比LinkedList在随机访问的时候效率要高 ,因为ArrayList底层是数组,可以通过索引号快速访问,LinkedList是通过二分查找法遍历链表节点进行查找的

                        3、增加和删除效率:在非首尾的增加和删除操作,LinkedList要比ArrayList效率要高,因为ArrayList增删操作需要大量的前移或后移,这个过程涉及到大量的赋值操作比较耗时间,LinkedList只需要修改节点对象的左右指针即可

                        4、内存空间占空:LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素

                        5、总的来说,在需要频繁读取集合中的元素时,更推荐使用ArrayList,而在插入和删除操作较多的时候,更推荐使用LinkedList。

        5、ArrayList和Vector的区别(带一下CopyOnWriteArrayList)

                        1、ArrayList是线程不安全的,vector是线程安全的。ArrayList中所有的方法都没有加同步锁,vector中所有方法都加了synchronized同步锁。官方在JDK1.5版本中又推出了一个CopyOnWriteArrayList,使用了Lock锁实现了线程安全。然后弃用了vector,因为lock锁的性能比synchronized锁的性能更好

                               在并发编程中,如果多个线程共享一个ArrayList,那么必须考虑线程安全的问题,可以自己在代码中对ArrayList操作代码加锁,或者直接用线程安全的CopyOnWriteArrayList类

                                在不考虑线程安全的环境下, 用ArrayList性能更好,因为加锁开锁很耗性能

        6、Array怎么转换为ArrayList,ArrayList怎么转换成Array

                        1、官方提供的数组工具类Arrays中提供了一个静态方法asList(),可以把数组转换为List,参数是数组,返回值是List

                        2、ArrayList类中提供了toArray()成员方法,可以把ArrayList转换为Array后,进行返回

        7、HashSet和TreeSet的区别      

                HashSet和TreeSet都是Set接口下面的子类

                        HashSet的底层是HashMap,他将数据存储在HashMap的key中

                        (HashSet是无序唯一的,因为HashMap的key是无序唯一的)

                        TreeSet的底层是TreeMap,他将数据存储在TreeMap的key中 

                        (TreeSet是有序唯一的,因为TreeMap的key是有序唯一的)

        8、HashMap和HashTable的区别(带一下concurrentHashMap)                 

                        1、HashMap是线程不安全的,HashTable是线程安全的。HashMap中所有的方法都没有加同步锁,HashTable中所有方法都加了synchronized同步锁。官方在JDK1.5版本中又推出了一个CopyOnWriteArrayList,使用了Lock锁实现了线程安全。然后弃用了HashTable,因为lock锁的性能比synchronized锁的性能更好

                        在并发编程中,如果多个线程共享一个HashMap,那么必须考虑线程安全的问题,可以自己在代码中对HashMap操作代码加锁,或者直接用线程安全的CopyOnWriteArrayList类

                        在不考虑线程安全的环境下, 用HashMap性能更好,因为加锁开锁很耗性能

                        对Null key的支持:HashMap支持key为null,但只能有一个,HashTable不支持key为null,会直接抛NPE,HashMap支持value为null,不限制个数,HashTable不支持value为null

                        ConcurrentHashMap的key和value都不支持null

                        HashMap在1.8以后,设置了阈值=8,当链表长度超过阈值的时候,将链表转换为红黑树,检索时间。HashTable被弃用了,没有更新。

                        

                        初始容量大小和扩容容量大小的区别:

                                HashMap默认初始容量是16,扩容策略是2倍;HashTable默认初始容量是11,扩容策略是原来的2倍+1

                                HashMap如果手动指定了初始容量,不是2的n次方,他也会找到最近的一个2的n次方作为初始容量

                                HashTable如果手动指定了初始容量,会直接使用给定的大小

                        

                        HashTable采用的锁全表的机制,ConcurrentHashMap采用的分段锁的设计,锁粒度更细,性能更好

        9、HashMap和TreeMap的区别

                HashMap底层是数组+链表/红黑树,key是无序且唯一的

                TreeMap的底层是红黑树,key是有序且唯一的

                HashMap的性能比TreeMap的更好,但如果需要一个有序的key集合,需要使用TreeMap

        10、HashMap的底层原理(数据结构+put()流程+resize()流程)

        11、HashMap的长度为什么是2的幂次方?

        12、什么是哈希碰撞/哈希冲突,怎么解决哈希冲突,HashMap采用的是什么策略

                如果有两个不同字符串通过同样的哈希算法计算出来的哈希码是一样的,则称他们发生了哈希碰撞,哈希冲突,解决方法:

                        1、开放地址法

                        2、再哈希法
                        3.拉链法(链地址法)HashMap默认使用的就是这种

                        4、建立公共溢出区

                        

        13、HashMap为什么不直接使用key的HashCode()函数返回的哈希码作为落槽时的索引号 ?那HashMap是怎么解决的呢?

                这题也可以这样问:HashMap的底层是如何计算key落槽时的索引的?

                        `hashCode()`方法返回的是int整数类型,其范围为-(2 ^ 31)~(2 ^ 31 - 1),约有40亿个映射空间,而HashMap的容量范围是在16(初始化默认值)~2 ^ 30,HashMap通常情况下是取不到最大值的,并且设备上也难以提供这么多的存储空间,从而导致通过`hashCode()`计算出的哈希值可能不在数组大小范围内,进而无法匹配存储位置;

                        1. HashMap自己实现了自己的`hash()`方法,通过两次扰动使得它自己的哈希值高低位自行进行异或运算,降低哈希碰撞概率也使得数据分布更平均;

        14、==和equals的区别

                ==和equals都可以用于比较,语法是a==b或者是a.equals(b)

               ==比较的是内存地址

                equals方法是Object类中的方法,可以被任意类继承或重写,通过看官方Object类原码可以知道equals()方法默认是用==比较地址

                (如果想要修改equals()方法的比较规则, 可以重写equals()方法)

                        String类就重写了equals()方法的比较规则,由默认的比较两个字符串对象的内存地址,修改为比较字符串中每个字符是否相等。

                        因为堆区中可能会出现两个一模一样的字符串对象,但内存地址不一样,所以字符串的比较必须用equals()方法,否则可能会出现两个内容一模一样的字符串,因为地址不一样比较后出现不相等的情况

        15、为什么重写了equals()方法,必须也要重写HashCode()方法

                        HashMap的底层采用了key的HashCode()来计算数组的索引

                        如果数组[index]为空说明key不存在,直接落槽插入

                        如果数组[index]不为空说明该位置有key存在,但不能一定说明已存在的key与要插入的key重复,因为可能会发生哈希碰撞,此时应该进一步用equals方法比较已存在的key与要插入的key是否相等。如果相等就说明一定是重复的,应该覆盖,如果不相等,说明发生了哈希碰撞,那么应该插入在链表中。

                        重写equals方法的目的是为了不去比较两个对象的内存地址,改为比较对象的内容。如果一个类重写了equals,没有重写hashcode,就可能出现两个地址不同的对象equals比较相等,但是hashcode比较不相等,这样会违反hashMap的唯一性,因此,重写了equals方法必须也要重写hashcode方法,且必须满足两个对象equals相等,hashcode也必须相等。

        16、能否使用任意的数据类型作为HashMap的key

                可以使用任意的数据类型作为HashMap的key。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值