接口和抽象类的区别
1.接口只能存在public方法,抽象类可以有普通成员函数
2.接口中成员变量只能是public static final类型,抽象类的成员变量可以是任何类型
3.接口可以实现多个,抽象类只能继承一个
接口设计的目的是对类的行为进行约束,不对如何实现行为进行限制
抽象类的设计的目的是代码复用,当不同的类具有相同行为,且其中一部分的行为方式一致,可以让这些类都派生于一个抽象类,让这个抽象类实现这个行为,避免让所有的子类来实现这个行为,这就达到了代码复用的目的
==和equals比较
==对比的栈中的值,基本数据类型是变量值,引用数据类型比较的是堆中内存的地址
equals在Object中默认采用==,但是可以重写equals\
String、Stringbuffer和StringBuilder区别
1.String是不可变的,修改会新生成一个字符串对象,Stringbuffer和StringBuilder是可变的
2.StringBuffer是线程安全的,StringBuilder是线程不安全的,所以单线程环境下StringBuilder效率更高
StringBuffer append方法是使用sychronized修饰的,是并发安全的
String是final修饰的,是不可变的,每次操作都会产生新的对象
场景:经常需要改变字符串内容使用StringBuffer和StringBuilder
优先使用StringBuilder,多线程场景下使用StringBuffer
ArrayList和linkedList区别
ArrayList基于动态数组,连续内存存储,适合下标访问(随机访问)。扩容机制:因为数组长度固定,超过长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会涉及到尾部的移动(往后复制一位,插入新元素),使用尾插法并指定初始容量大小可以极大提升性能,甚至超过LinkedList(需要创建大量的node对象)
LinkedList基于链表,可以存储在分散的内存中,适合做数据插入和删除操作,不适合查询,需要逐一遍历
遍历LinkedList必须使用iterator不能使用for循环,因为每次for循环体内通过get(i)取得某一个元素时都需要对list重新进行遍历,性能消耗极大。另外不要试图使用indexOf等返回元素索引,并利用其进行遍历,使用indexOf对list进行遍历,当结果为空时会遍历整个列表。
HashMap底层
1.1.7底层是数组+链表,1.8中是数组+链表+红黑树,加红黑树的目的就是提高插入和查询的整体效率
2.1.7中链表使用头插法,1.8中链表使用尾插法,因为1.8插入key和vlue需要遍历链表元素个数,所以需要遍历链表统计链表元素个数,所以正好直接使用尾插法,提升性能
3.1.7中哈希算法比较复杂,存在各种右移与异或运算,1.8中进行了简化,因为复杂的哈希算法目的就是提高散列性,来提供HashMap的整体效率,而1.8中新增了红黑树,所以可以适当的简化哈希算法,节省cpu资源
HashMap1.8为什么使用红黑树
可以利用链表对内存的使用率和红黑树的高效检索,AVL树是(一种自平衡的二叉搜索树,它在插入和删除操作时通过一系列的旋转和调整来保持树的平衡,从而提高了查询效率。)是一个高度平衡的二叉树,所以查找的效率非常高,但是有利就有弊,二叉树每次进行插入删除操作,都要做调整,复杂,耗时,所以使用红黑树
红黑树的优势
红黑树是“近视平衡”的
红黑树相比AVL树,在检索的时候其实效率差不多,都是通过平衡来二分查找,但对于插入删除操作效率提高很多。红黑树不像AVL树追求绝对的平衡,它允许局部很少不完全的平衡,这对效率影响不大,但省去了很多不必要的调平衡操作,AVL树有时候调平衡代价较大,所以有时候效率不如红黑树
concurrentHashMap底层
jdk1.7
数据结构:ReentrantLock+Segment+HashEntry 一个Segment是一个HashEntry数组,每个HashEntry又是一个链表结构
元素查询:二次Hash,第一次Hash定位到Segment,第二次Hash定位到元素所在链表的头部
锁:Segment分段锁,Segment继承ReentrantLock,锁定操作的Segment,其它的Segment不受影响,并发度为Segment的个数,可以通过构造函数指定,数组扩容不会影响其它的Segment
get方法无需加锁,volatile保证
jdk1.8
数据结构:synchronized+CAS+Node+红黑树 Node的val和next都用Volatile修饰,保证可见性
查找、替换、赋值操作都用CAS
锁:锁链表的head节点,不影响其它元素的读写,锁粒度更细,效率更高,扩容时,阻塞所有的读写操作、并发扩容
读操作无锁:
Node的val和next使用volaite修饰,读写线程对该变量互相可见
数组用volaite修饰,保证扩容时被度线程感知