1、请说一下HashMap,SparseArray原理,及优缺点。另外ConcurrentHashMap如何实现线程安全?
hashmap的原理
- hashmap内部是一个
默认容量为16的数组。数组每个元素又是一个链表头节点jdk1.8以后,达到指定数量之后,链表会转化为红黑树。 - 当为hashmap里put数据时,达到一定容量限制就自动扩容。当达到容量的
0.75(扩容因子)之后。就扩容到2倍。然后重新hash - 增:先计算key的hash(装箱拆箱)每个key都会
hash&(当前数组容量-1),然后看适合放在哪个数组的链表上。 - 查:先计算key的hash(装箱拆箱),
找到hash值找到数组的位置,再遍历链表,找到对应的节点 - 删除:同理查
- 哈希冲突?没事,先比较哈希再比较key。双重保险。
- Java8以前头插法,java8 尾查法
sparseArray的原理
-
sparseArray比hashmap更加省内存,某些条件下性能更好,主要避免了对key额自动装箱(int转为integer)。
默认容量为0,不是10。建议容量1000一下使用 -
内部时
两个数组,一个存key,用来定位,一个存value。key只能时int类型。查找方便,插入可能就麻烦,因为可能移动数组。 -
增:扩容逻辑,size或以上时,小于等于4则为8,
否则扩容到2倍- 存储容量大小为4或者8,会放入缓存中
-
查:key数组时有序的,通过
二分查找来定位Key数组种对应值的位置 -
删:使用数组就要面临删除数据时数据搬移的问题,引入了
delete标记,减少删除数据时造成的搬运,以此做到O(1).查找到delete标记时,表示数据已被删除- 因为delete为了做到删除O(0),但会影响空间,所以就有
GC机制,当和size相关操作,以及和数组扩容相关操作,都会有道gc来清除delete标记的数据和index。
- 因为delete为了做到删除O(0),但会影响空间,所以就有
-
装箱和拆箱:SparseArray的设计目标是节约内存空间,所以用给基本数据类型,因为比类更省空间。
-
其实用到二分查找所以保证key有序,基本数据类型比对大小也很方便。
-
有序度。例如 1 2 3 4 就被称为满有序度,而相反的 4 3 2 1 则被称为满逆序度。如果满有序度就直接用append加载后面比put会更有效率
-
省内存,也就是和hashmap比
- hahsmap最多用75%(为了防止都在一个链条上),sparearray可以挤满。
- hashmap相比与sparseArray的属性变量之多不少。SparseArray从头到尾就两个数组
hashmap和sparseArray的优缺点
-
内存空间使用率:1、sparseArray不用装箱 2、sparseArray而且数组可用满
-
查询效率:1、当数据量大时,hashMap比SparseArray更快。
-
插入效率:1、正序插入,sparseArray强。2、倒叙插入hashmap强
-
key的兼容性:sparsearray 的key要int,hashmap的key能hash就行
-
使用场景:要省内存空间、key为int、数量少于1000。用sparseArray。否则用hashmap。
-
hashMap和SparseArray都是存储key-value类型数据
- 数据结构:方面 hashMap是数组+链表+红黑树。SparseArray用的双数组
- 性能:Hashmap默认是16个长度,会自动装箱,key是int的话,hashmap就要先封装成Integer。SparseArray用的限制是key是int。数量小于1k。如果key不是int,也小于1k。建议用Arraymap。Array有两个数组,一个存储index,一个存储key和value。
ConcurrentHashMap如何实现线程安全
- jdk1.7锁的时Segment,一个segment包含多个HashEntry。jdk 1.8锁的颗粒度就是HashEntry
- 使用了synchronized。
2、请说一说HashMap原理,存取过程,为什么用红黑树,红黑树与完全二叉树对比
- hashmap内部是靠数组+链表或者红黑树维护。每个链表头存在数组里。jdk1.8 当链表长度大于等于8,就把链表转为红黑树。红黑树长度小于等于6以后,又变为链表。
- 之所以用红黑树是因为查询效率高。时间复杂度是O(logN),链表是O(N)
- 完全二叉树是除去最后一层是满二叉树,最后一层从左到右分布是完全二叉树
- 红黑树是平衡二叉树的一种。相比于普通二叉树,极端情况会成一个线性结构,和链表一样了,没意义。
- 平衡二叉树左右高度不会超过1,红黑树最长路径不会超过最短的两倍。虽然事件复杂度都是O(logN),但平衡二叉树会更快。
- 插入删除操作,平很二叉树需要大量旋转来维持平衡。红黑树,通过改变颜色和旋转就好了。虽然复杂度都是O(logN),但明显红黑树更快。
3、请说一下hashmap put()底层原理,发生冲突时,如何去添加
- 对key的hashCode()进行hash后计算数组下标index
- 如果table为null,则resize初始化
- 没碰撞直接放到对应下标的bucket
- 碰撞了,找节点,先比hash(key)后比key。替换value
- 如果是树结构就挂在树上。
- 如果是链表,判断长度,加上新put的内容,长度大于等于8就转为树。
- 数据put后,如果达到或者超过扩容因子之后。resize重新弄hash,重构hashmap
4、arraylist如何保证线程安全?
- 在写个类继承arrayList,在重写各个方法上加synchronized关键字
- 在arraylist的各个方法里可重入锁,ReentrantLock
- 用Collection.SynchronizedList实现
- 用CopyOnWriteArrayList类。多读少些。因为写时拷贝。
5、请说一下ArrayList\LinkedList\HashMap\LinkedHashMap的底层原理
- ArrayList内部维护一个Object数组,初始大小为10,容量不足时扩展为1.5倍。找第N个元素,按照 数组很快,增删就慢了,因为要移动数组。
- LikedList内部维护一个双向链表,好处时不用扩容,查第N个元素事件 复杂度为O(N),比Arraylist慢。增删只要改链表就好了很快
- hashmap内部时数组+链表+红黑树的结构。初始容量为16,增长因子0.75。达到增长因子之后就扩容。数组遍历出来是无顺序的
- linkedhashmap在hashmap的基础上,遍历出来时有序的。单项链表保存数据,另外实现了双向链表记录迭代顺序,
迭代顺序可以是插入顺序或者是访问顺序(类似LRU)
5.1、LinkedList和ArrayList的区别
- LinkedList是双向链表,ArrayList是可变数组
- LinkedList不允许随机访问,查询效率低,ArrayList允许随机访问,查询效率高
- linkedList插入\删除快。ArrayList插入、删除慢
6、请说一下HashMap实现原理,扩容条件,链表转红黑树的条件。
- hashmap底层维护了一个数组,默认16,数组元素是链表头或者红黑树根节点。
- hashmap存入 和内容是key value。如果是存的null key,存在下标为0的桶。否则时候将hash(key)&(数组长度-1),找到适合的下标,如果桶里没东西,直接存入,否则先遍历,有了就覆盖,没有的话,jdk8以前是插在链头,然后把链表头放到桶里。jdk8是尾插法,放到尾部。
- 扩容条件是hashmap元素数达到增长因子hashmap bucket数的0.75时,就扩容干一倍数 ,不超过int最大值。准确说道道2^30就不会增长了
- 链表转红黑树的条件。当链表长度大于等于8就转红黑树,纪律很小千分之6还是8。小于等于6就转回来。
7、请说一下二叉树遍历的遍历步骤
- 二叉树就是,有序树,并且各个节点的度不能超过2.
深度优先(根是一条斜线),有空用非递归写一下
- 前序遍历 根左右
void bianli(struct TreeNode* root){
//根
if(!root) return;
printf("%d\n",root->data);
//左
bianli(roor->leftchild);
//右
bianli(roor->rightchild);
}
- 中序遍历 左根右//同理
- 后序遍历 右左根//同理
广度优先
- 层次优先遍历
void bianli(TreeNode root){
Queue<TreeNode> queue = new ArrayDeque<>();
quque.add(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
System.our.println(node.value);
if(node.left!= null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
}
9、对称加密和非对称加密,MD5的原理
- 对称加密就是加密端和解密端用的同一套秘钥,作为私钥。可能被拦截,客户端永远是不安全的。比如DES,3DES,AES.
- 非对称加密,机密和解密用的不同秘钥,公钥加密信息,私钥解密。密钥在服务端非常安全。比如RSA和ECC
- MD5 128位,其中4位组成一个16进制字符。所以我们看到的是32个16进制的字符组成的字符串。
-
md5基本不可逆运算,为什么基本呢?用户用彩虹表
-
对所有的数据加密结果都是32位长字符
-
同数据,加密同结果
-
不同数据推成同MD5是很困难的,已经元数据和md5,想伪造不同数据有md5是非常困难的
-