- 当链表中没有数据时,first 和 last 是同一个节点,前后指向都是 null;
3、你在工作中对 ArrayList 和 LinkedList 是怎么选型的?
如果项目中有需要快速的查找匹配,但是新增删除不频繁我一般使用的是 ArrayList 数组结构,但是如果查询比较少,新增和删除比较多我一般用的是 LinkedList 链表结构。(ps:结合它们的原理回答为什么)
4、 ArrayList 在多线程使用应该注意什么?
在多线程使用 List 要注意线程安全问题,解决的办法通常有两种来解决。第一种也是最简单的一种直接使用 Collections.synchronizedList(list) ,但是其性能不好,因为它的实现原理相当于委托模式,交于另一个类来处理,而且内部将每个函数都加了 synchronized , 另一种实现是 java.util.concurrent##CopyOnWriteArrayList 。
延伸:用过 CopyOnWriteArrayList 吗?它是怎么实现线程安全的 ?
答:有用过,它的基本原理和 ArrayList 是一致的,底层也是基于数组实现。它的基本特性总结有以下几点:
1、线程安全的,多线程环境下可以直接使用,无需加锁;
2、通过锁 + 数组拷贝 + volatile 关键字保证了线程安全;
3、每次数组操作,都会把数组拷贝一份出来,在新数组上进行操作,操作成功之后再赋值回去。
Map
1、描述一下HashMap中 put(K key, V value) 这个 API 的存储过程 。
1、根据 key 通过该公式 (h = key.hashCode()) ^ (h >>> 16) 计算 hash 值
2、判断 HashMap table 数组是否已经初始化,如果没有初始化,那么就按照默认 16 的大小进行初始化,扩容阀值也将按照 size * 0.75 来定义
3、通过该公式 (n - 1) & hash 拿到存入 table 的 index 索引,判断当前索引下是否有值,如果没有值就进行直接赋值 tab[index] , 如果有值,那么就会发生 hash 碰撞 💥 ,也就是俗称 hash冲突 , 在 JDK中的解决是的办法有 2 个,其一是链表,其二是 红黑树。
4、当发送 hash 冲突 首先判断数组中已存入的 key 是否与当前存入的 key 相同,并且内存地址也一样,那么就直接默认直接覆盖 values
5、如果 key 不相等,那么先拿到 tab[index] 中的 Node是否是红黑树,如果是红黑树,那么就加入红黑树的节点;如果 Node 节点不是红黑树,那么就直接放入 node 的 next 下,形成单链表结构。
6、如果链表结构的长度 >= 8 就转为红黑树的结构。
7、最后检查扩容机制。
图解如下:
2、说说HashMap的工作原理
简单来说:HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存Entry对象。当两个对象的hashcode相同时,它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry会存储在链表中,当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。
网上文章很多,可以去我的B站看视频讲解:HashMap原理解析
延伸:如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?
答:默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。
3、说一下你对 ArrayMap 的了解
ArrayMap 底层通过两个数组来建立映射关系,其中 int[] mHashes 按大小顺序保存 Key 对象 hashCode 值,Object[] mArray 按 mHashes 的顺序用相邻位置保存 Key 对象和 Value 对象。mArray 长度 是 mHashes 长度的 2 倍。
存储数据是根据 key 的 hashcode() 方法得到 hash 值,计算出在 mArrays 的 index 值,然后利用二分查找找到对应的位置进行插入,当出现哈希冲突时,会在 inde 的相邻位置插入。
取数据是根据 key 的 hashcode() 方法得到 hash 值,然后通过 hash 值根据二分查找拿到 mHashes 的 index 索引,最后在根据 index + 1 索引拿到 mArrays 对应的 values 值。
4、你在工作中对 HashMap 和 ArrayMap 还有 SparseArray 是怎么选型的 ?
好的,我总结了一套性能对比,每次需求我都是参考如下的总结。
序号 | 需求 | 性能选择 |
---|---|---|
01 | 有 1K 数据需要装入容器 | key 是 int 选择 SparseArray 节省 30% 内存,反之选择 ArrayMap 节省 10% |
02 | 有 1W 数据需要装入容器 | HashMap |
Android进阶必备Java高阶知识点
文章篇幅原因,中高级的部分内容过多,文章篇幅原因,所以我整理了241页PDF。虽然短小,但是精悍!看完这份电子书,保你可以随意应付Android面试中的Java问题,如果不行,请私信我给我寄刀片。文档领取方式:点赞+关注,然后私信关键词 【666】即可获得免费领取方式!
一、深入理解Java泛型
1、泛型的作用与定义
2、通配符与嵌套
3、泛型的上下边界
4、RxJava中深入理解泛型
二、注解
- 注解(ANNOTATIONS)概念、什么是注解
- 元注解;什么是元注解、元注解的使用
- 自定义注解
- 默认参数值(DEFAULT PARAMETER VALUES)
- 什么是APT,如何在Android Studio中构建一个APT项目?
- 插桩
- 反射
- Retrofit中的注解
三、并发编程
1、基础概念
1)CPU核心数、线程数
2)CPU时间片轮转机制
2、线程之间的共享
1)synchronized内置锁
2)volatile 关键字
3)线程私有变量 ThreadLocal
3、线程间的协作
1)什么是CAS?
2)CAS(Compare And Swap)导致的ABA问题
4、线程池的使用
1)Java中的ThreadPoolExecutor类
2)深入剖析线程池实现原理
3)如何合理配置线程池的大小
四、数据传输与序列化
1、Serializable原理
1.概念
- 序列化:把Java对象转换为字节序列的过程。
- 反序列化:把字节序列恢复为Java对象的过程。
2.用途 对象的序列化主要有两种用途:
1)把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中。
2)在网络上传送对象的字节序列。
2、Parcelable的原理和使用方法
- Android源码中的Parcelable
3、Json
一种数据传输格式,与开发语言无关,轻量级。
一开始是javaScript的,但是后面比较流传,几乎所有语言都有相应的使用API。
五、Java虚拟机原理
1、垃圾回收
1)如何判断对象已“死”
2)回收方法区
3)垃圾回收算法
2、内存分配策略
1)对象优先在Eden分配
2)大对象直接进入老年代
3)长期存活的对象将进入老年代
4)动态对象年龄判定
5)空间分配担保
3、Dalvik虚拟机
1)Dalvik指令
2)Dalvik启动过程
3)Dalvik上的垃圾回收
六、反射与类加载
1、反射
1)反射基本概念与三种获取Class对象的方式
2)获取构造器实例化对象与属性信息
3)Android 配置打包签名信息的两种方法
4)Hook动态注入代码
2、类加载
1)动态代理模式
2)Android 中的Dalvik和ART
3)ClassLoader 的构造函数
4)双亲委派机制
结语
看到这篇文章的人不知道有多少是和我一样的Android程序员。
35岁,这是我们这个行业普遍的失业高发阶段,这种情况下如果还不提升自己的技能,进阶发展,我想,很可能就是本行业的职业生涯的终点了。
我们要有危机意识,切莫等到一切都成定局时才开始追悔莫及。只要有规划的,有系统地学习,进阶提升自己并不难,给自己多充一点电,你才能走的更远。
千里之行始于足下。这是上小学时,那种一元钱一个的日记本上每一页下面都印刷有的一句话,当时只觉得这句话很短,后来渐渐长大才慢慢明白这句话的真正的含义。
有了学习的想法就赶快行动起来吧,不要被其他的事情牵绊住了前行的脚步。不要等到裁员时才开始担忧,不要等到面试前一晚才开始紧张,不要等到35岁甚至更晚才开始想起来要学习要进阶。
给大家一份系统的Android学习进阶资料,希望这份资料可以给大家提供帮助。
才慢慢明白这句话的真正的含义。
有了学习的想法就赶快行动起来吧,不要被其他的事情牵绊住了前行的脚步。不要等到裁员时才开始担忧,不要等到面试前一晚才开始紧张,不要等到35岁甚至更晚才开始想起来要学习要进阶。
给大家一份系统的Android学习进阶资料,希望这份资料可以给大家提供帮助。
[外链图片转存中…(img-aCZx5wFY-1643872305616)]