JAVA开发工程师知识点总结

前言:本文撰写目的是为了给广大网友总结一份尽力涵盖所有常用的java知识点,用于笔试或面试,同时也是自己学习过程的一个记录,希望各位网友能够广开言路,各位大牛能够多给给指导。十分感谢!!

3、数组

数组实际上没啥好说的,都是老生常谈的一些特点,特别要注意的就是避免越界罢了。
1、其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
2、其元素必须是相同类型,不允许出现混合类型。
3、数组中的元素可以是任何数据类型,包括基本类型和引用类型。
4、数组变量属引用类型,数组也可以看成是对象,既然是对象那肯定是在堆中。
5、一种线性数据结构,内存地址连续,所以随机读取数据速度特别快。

注意的点

for-each循环,语法糖,反编译实际上就是一个for-index循环。

4、集合

Collection

所有集合均实现Collection接口,大部分接口都可以望文思意所以不谈,这里重点聊的是1.8的stream和parallelStream

方法 含义
stream 返回一个stream实例
parallelStream 返回一个stream实例,这个流对象的处理方式先不谈,等完成了fork/join 框架学习后
什么是Steam?

java 1.8版本开始提供的一个接口,主要提供对数据集合使用流的方式进行操作,流中的元素不可变且只会被消费一次,所有方法都设计成支持链式调用,学过JavaScript应该知道链式调用高效率、干净、简洁。
博主才疏学浅,Steam的理解均来自一下文章。
链接: JAVA进阶之Stream实现原理
链接: Stream Pipelines
steam的api包含许多,有与javaScript类似的方法sorted() 、 filter()、 map()等,steam把这些操作方法总结为两大类:中间操作(Intermediate operations)与结束操作(Terminal operations),中间操作只是对操作进行了记录,只有结束操作才会触发实际的计算(即惰性求值),这也是Stream在迭代大集合时高效的原因之一。中间操作又可以分为无状态(Stateless)操作与有状态(Stateful)操作,前者是指元素的处理不受之前元素的影响;后者是指该操作只有拿到所有元素之后才能继续下去。结束操作又可以分为短路(short-circuiting)与非短路操作,这个应该很好理解,前者是指遇到某些符合条件的元素就可以得到最终结果;而后者是指必须处理所有元素才能得到最终结果。

上诉提到的惰性求值:
IntStream.range(1, 10).peek(x -> System.out.print("\nA" + x)).limit(3).peek(x -> System.out.print("B" + x)).forEach(x -> System.out.print("C" + x));

实际上就会输出: A1B1C1 A2B2C2 A3B3C3
也就是说,集合的流式调用,并不是对集合进行多次遍历,多次调用回调函数,而是当执行到结束操作时,才会在一次循环中,把所有的操作执行完毕。

关于执行原理我们看另外一段代码:
List<String> list = Arrays.asList("China", "America", "Russia", "Britain");
List<String> result = list.stream().filter(e -> e.length() >= 4).map(e -> e.charAt(0))
.map(e -> String.valueOf(e)).collect(Collectors.toList());

实际上我们查看源码可以知道steam实际上创建的就是一个Head对象,(无状态操作)filter、map等中间操作实际上就是返回一个StatelessOp对象(下次操作,且this为创建这个StatelessOp对象的入参,StatelessOp放入到previousStage中去)

也就是说操作的调用链形成了一个stage双链表结构。
当执行到最后的collect()方法的时候,collect调用copyInto开始启动流水线,至此整个调用链开始工作,每个元素实际上只被遍历一次,分别去到不同的调用节点的回调函数中处理。

Collection之下是List和Set

list和set的区别

1、set内的元素是唯一的,而list元素是不唯一的,同时泛型中只能接受类不接受基本类型,实际上Set是通过对象的equals函数来判断对象是否相同而不是==地址。
2、list是有插入顺序的,而set是无序的。
3、我们常用的hashSet和treeSet本质上就是一个map,用set的value作key,一个预设的Object作为value,而list底层数据结构是数组或者链表。

list的主要实现类ArrayList、LinkedList和Vector

ArrayList
1、底层存储数据的是一个数组(Object[] elementData),故随机读取快,而写慢,如remove函数的实现,index位置的删除操作就是,把index+1至末尾的数据复制到index至末尾-1,然后解除末尾数据引用等待下次GC触发回收,get方法就是数组的下标操作。
2、没有synchronized关键字修饰,所以线程不安全
LinkedList
1、底层数据结构是双向链表,分别保存首(Node first)尾(Node last)节点的引用,故随机写速度较好,随机读速度较慢(较需ArrayList要遍历节点),如add函数实际上就是linkLast向链表末尾插入一个元素,remove就是找到指定位置然后进行unlink操作,就是被删除的前置节点的next指向后置节点,后置节点的prev指向前置节点。
2、没有synchronized关键字修饰,所以线程不安全
Vector
1、底层存储数据的是一个数组
2、synchronized关键字修饰,所以线程安全
Stack
stack是vector的子类,它提供了一些栈的操作方法,线程安全
Queue
queue是一个接口,如LinkedList都有实现,实现了该接口的类都支持队列操作
PriorityQueue
学习中

目前查看源码了解到区别仅此而已

set的主要实现类HashSet(LinkedHashSet)、TreeSet

HashSet
1、底层存储数据的map是一个HashMap(初始容量为16,负载因子为0.75)用set的value作key,一个预设的Object作为value,HashMap底层是一个数组(Node<K,V>[] table)+链表结构,用key的hash值去计算下标,所以HashMap的key是顺序不定的,所以set也可看做无序。
2、可以放入null
TreeSet
1、底层存储数据的是一个NavigableMap(java.util只有一个实现是TreeMap,而TreeMap是基于红黑树实现的)用set的value作key,一个预设的Object作为value,与HashMap不同的是,TreeMap提供对key排序功能(后续谈Map的时候详谈如何排序),故而TreeSet也是个排序后的结果。
2、不允许放入null值
LinkedHashSet
1、HashSet的构造器中如果第二个参数是浮点型则map则被实例化为LinkedHashMap,LinkedHashMap是,LinkedHashSet正式利用这个构造器。而且accessOrder这个参数设置为true,即是LinkedHashMap为访问顺序的倒序(如果accessOrder这个参数设置为false则一直为插入顺序)。

Map

Map的主要实现类
HashMap

1、HashMap底层是一个数组(Node<K,V>[] table)+链表结构,用key的hash值去计算下标,所以HashMap的key是顺序不定的,甚至可以重新散列,所以插入时,是先算元素该位于数组哪个位置,然后再把元素插入到链表中
2、Hash桶,hash桶实际上就是指数组中的单向链表
3、负载因子指的是当元素已到总容量多少百分比开始扩容,负载因子的用法主要是:负载因子大,导致数组装满后才扩容,牺牲时间,利用空间;假设负载因子小,导致数组装载较少内容就扩容,牺牲空间,利用时间,同时源码也说了,负载因子是0.75的时候,空间利用率比较高,而且避免了相当多的Hash冲突,使得底层的链表或者是红黑树的高度比较低,提升了空间效率。
4,扩容是原来的2倍
5、Hash回环,多线程下会出现Hash回环,线程1:不断添加数据,导致不断扩容,线程2:不断遍历,多线程下应该使用ConcurrentHashMap
6、jdk1.8
在Java1.7中,插入链表节点使用头插法。Java1.8中变成了尾插法,头插法主要存在的问题是:并发下调用transfer()方法,可能会导致链表死循环,调用get()方法时会进入死循环,以及数据的丢失

Java1.8的hash()中,将hash值高位(前16位)参与到取模的运算中,使得计算结果的不确定性增强,降低发生哈希碰撞的概率(hash碰撞:两个对象的hash值一样,导致在数组中的下标一样)。

扩容时计算数组元素下标的算法:
1.8扩容算法:
1.先生成新数组,链表不需要进行扩容
2.遍历老数组中的每个位置上的链表或红黑树
3.如果是链表,则直接将链表中的每个元素重新计算下标,并添加到新数组中去。
4.如果是红黑树,则先遍历红黑树,先计算出红黑树中每个元素对应在新数组中的下标位置
    a.统计每个下标位置的元素个数
    b.如果该位置下的元素个数超过了8,则生成一个新的红黑树,并将根节点的添加到新数组的对应位置。
    c.如果该问之下的元素个数没有超过8,那么则生成一个链表,并将链表的头节点添加到新数组的对应位置。
5.所有元素转移完成后,将新数组赋值给HashMap对象的table属性。

数组+链表+红黑树,故可以实现自平衡,当数组的某一个索引位置上的元素以链表形式存储的数据个数>8,且当前数组的长度>64时,此时此索引位置上的所有数据改为使用红黑树存储;当在树上进行删除操作,使结点数等于6时,又自动转为链表存储

数据结构 增删效率 查询效率
链表
红黑树
当HashMap中存储的数据越来越多,链表越来越长,查询数据效率就不太理想。使用红黑树代替链表,虽然牺牲了部分增删的性能,却提升了查询的性能。
LinkedHashMap

1、LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构。该结构由数组和链表+红黑树 在此基础上LinkedHashMap 增加了一条双向链表,保持遍历顺序和插入顺序一致的问题。
2、LinkedHashMap 允许使用null值和null键, 线程是不安全的,底层使用了双线链表,增删相快了。
3、链表节点的添加过程
HashMap中的put方法(putValue)会调用afterNodeInsertion,而afterNodeInsertion在LinkedHashMap有自己的实现,在这个时候完成对链表的操作
4、链表节点的删除过程
与添加类似(afterNodeRemoval)

TreeMap

底层为红黑树(可以在源码里面看到Entry包含父节点左右子节点信息,还有红黑树的状态),treeMap在初始化的时候支持设置一个Comparator,当entry通过put方法放入到map中的时候,先用初始化Comparator(如有,如果没有的话,就要求key类型自行实现Comparable接口),对已有元素进行比较,然后插入到合适的位置。
既然这么多地方提到了红黑树,我感觉红黑树的讨论不应该放到数据结构处,而是应该在此处特别讨论。

红黑树的前置知识—四阶B树

推荐连接:【数据结构】红黑树前置知识——4阶B树
(1)节点的类型
四阶B树有三种节点类型,分别是二阶节点(只有一个元素、可以连接两个子节点、元素必为黑色),三阶节点(有两个元素、可以连接三个子节点、元素为红黑或者黑红),四阶节点(有三个元素、可以连接四个子节点、元素必为红黑红)。

(2)四阶B树的性质
1、对于非叶节点,当该节点是n节点时,它必须有n-1个关键字和n个孩子
2、所有的叶子结点都位于同一层,即叶节点对齐。叶节点是没有子节点的节点。

(3)四阶B树各类型节点的成长
当元素(四阶节点成长)成长后存在上溢的情况下,应当是原有的三个元素从一个节点,转化为一个三节点的子树(三个节点均为黑色),然后新增的元素在转化后的子树处增加,可以是左子树的左右或者右子树的左右。
三阶节点成长成四阶节点后,需要对节点内的元素进行重新染色,必须要保持为红黑红

(4)四阶B树和红黑树的转换
四阶B树是由红黑树所有红色子节点上移到父节点同一高度转换而成,如下图所示

红黑树

推荐连接: 【数据结构】史上最好理解的红黑树讲解,让你彻底搞懂红黑树
(1)红黑树的优缺点
红黑树是一种自平衡的二叉查找树,查询快,相对于二叉搜索树(极端情况下变成链表查询时间复杂度是O(N))红黑树多了自平衡的操作,自己优化为一个近似平衡的二叉树,所以其查找时间复杂度是O(logN)。
但是在插入和删除的时候,相对于普通的二叉搜索树而言,红黑树多了自旋,重新染色的操作,所以插入和删除的操作花的时间会多一些。
(2)基本概念
parent:父节点
sibling:兄弟节点
uncle:叔父节点( parent 的兄弟节点)
grand:祖父节点( parent 的父节点)
(3)五大性质

  1. 节点是红色或黑色
  2. 根是黑色
  3. 叶子节点(外部节点,空节点)都是黑色,这里的叶子节点指的是最底层的空节点(外部节点),下图中的那些null节点才是叶子节点,null节点的父节点在红黑树里不将其看作叶子节点
  4. 从根节点到叶子节点的所有路径上不能有 2 个连续的红色节点,红色节点的子节点都是黑色,红色节点的父节点都是黑色
  5. 从任一节点到叶子节点的所有路径都包含相同数目的黑色节点

    (4)红黑树的自旋

    以上图为例,包含了M节点右旋和M节点左旋的情况,以左旋为例。
    M节点成为了其父节点(E节点)的父节点,E节点现在失去了右子节点(右子树),M节点多了一个左子节点,恰好M节点的左子树介于E和M之间,所以M节点的左子树成了E节点的右子树,右旋则反之。

各位看官大佬其实看《【数据结构】史上最好理解的红黑树讲解,让你彻底搞懂红黑树》就可以理解红黑树插入的逻辑,但是文章中有一点我不认同的是,原博主认为,原插入图:

优化为:

很明显,这里25和38是同为红色了,不符合红黑树原则,但是如果把25保持黑色那也不符合红黑树的原则,我认为应当把38右旋并重新染色为:

满足四五项原则(即使是转换成4阶B树也是正确的):
4. 从根节点到叶子节点的所有路径上不能有 2 个连续的红色节点,红色节点的子节点都是黑色,红色节点的父节点都是黑色
5. 从任一节点到叶子节点的所有路径都包含相同数目的黑色节点

依旧拿这个博主的例子来说明,其实20,30,36元素的插入都会导致原图的以25为根节点的子树转化成4阶B树的时候发生上溢。

48元素单独插入时,48元素应当是先左旋再右旋,并重新染色,48黑,46和50变红。

52元素单独插入时,应当50左旋,50染黑,46和52染红

如果48和52同时插入则原图以46为根节点的子树转化成4阶B树的时候发生上溢,应当是50左旋,左旋,再右旋,结果如下。

总之诀窍就是,把红黑树转化为4阶B树,当发生上溢的时候,尽量把上溢的元素上移到上一层即可。

删除的话,特别要注意的是把红黑树转化为4阶B树,删除时要注意下溢的情况,比如原图的46号节点要被删除,这个时候转化成4阶B树就有问题了,需要把50左旋重新染色,再删除46号,诀窍就是,删除如果发生下溢就要找到删除元素的代替元素才行。

HashTable

hashtable除了不允许放null的key和null的value之外(hashmap会把null的key哈希为0,但是hashtable直接取key.hashCode,同时在hashtable中如果value为null则直接抛出空指针异常),基本与hashMap无异,但是有一点值得注意的是,hashtable的方法上面都用synchronized定义,所以hashtable是线程安全的,但也是因为hashtable把方法都定义为synchronized,所以当此方法访问并发较高的时候实际上效率也是比较不理想。

ConcurrentHashMap

因为HashTable对并发较高的时候处理效率不理想,所以ConcurrentHashMap是并发量较高的一个处理方案。
java1.7的ConcurrentHashMap
数据结构是数组+链表+分段锁
java1.7采用分段锁技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问。

原理是ConcurrentHashMap内部类Segment实现了ReentrantLock,把HashEntry通过可重入锁ReentrantLock锁定起来,put过程如下:
1、对key进行第1次hash,通过hash值确定Segment的位置
2、在Segment内进行操作,获取锁
3、获取当前Segment的HashEntry数组后对key进行第2次hash,通过hash值确定在HashEntry数组的索引位置(头部)
4、通过继承ReentrantLock的tryLock方法尝试去获取锁,如果获取成功就直接插入相应的位置,如果已经有线程获取该Segment的锁,那当前线程会以自旋的方式去继续的调用tryLock方法去获取锁,超过指定次数就挂起,等待唤醒
5、然后对当前索引的HashEntry链进行遍历,如果有重复的key,则替换;如果没有重复的,则插入到链头
6、释放锁

get操作不需要加锁,因为存储元素都标记了volatile

java1.8的ConcurrentHashMap
数据结构是数组+链表+红黑树,同时抛弃了1.7的分段锁技术,采取对哈希桶进行synchronized加锁和CAS算法实现线程安全(读操作使用CAS,写操作使用synchronized 和CAS。)。
抛弃分段锁技术原因自己理解如下:
1、1.8优化了散列的算法,把key的高16位也用作散列计算数组下标,使得hash碰撞减少(哈希桶线程竞争的概率减少)。
2、分段锁在线程竞争不激烈的时候,浪费了内存资源。
3、1.8引入了红黑树使得分段锁的实现更加困难

至此,集合应该是讨论完毕了,如果有遗漏的会继续更新。

JAVA开发工程师知识点总结【数组集合篇】相关推荐

  1. 数字海南Java开发工程师实习生面试全过程及反思

    2020-12-7 数字海南Java开发工程师实习生面试全过程及试后反思 记录下人生第一次投递简历,正规实习求职面试,以及前前后后的准备与遇到的问题. 目录 2020-12-7 数字海南Java开发工 ...

  2. Java开发工程师应届生春招秋招总结

    Java开发工程师应届生春招秋招总结 本人是本科双非应届生,在秋招的过程有一些小小的心得(包括部分面试题和面试时需要注意的问题),想分享给大家,大佬们勿喷. JAVA开发面试总结 1.首先是穿着,如果 ...

  3. Java研发工程师知识点总结

    Java研发工程师知识点总结 大纲 一.Java基础(语言.集合框架.OOP.设计模式等) 二.Java高级(JavaEE.框架.服务器.工具等) 三.多线程和并发 四.Java虚拟机 五.数据库(S ...

  4. Java开发工程师,每个阶段需要掌握什么重点?

    本文着重介绍一下Java开发工程师各个层次需要掌握的重点,1-3年Java初级工程师->3-5年Java中高级工程师->5-8年以上的Java架构师的成长之路. Java初级工程师技能要求 ...

  5. Java开发工程师面试总结

    Java开发工程师面试总结 1. Java基础 1.1 接口与抽象类的区别 1.2 重写与重载的区别 1.3 集合 1.4 多线程 1.6 反射 2. Spring框架 2.1 IOC 2.2 AOP ...

  6. Java开发工程师面试经验总集

    置顶个交流群 文章觉得海星的话,可以来群里找桃子交流技术或者普通乱聊= = 挂群:820080257 文档链接:[腾讯文档]Java开发工程师 https://docs.qq.com/doc/DQlZ ...

  7. 如何才能成为高级Java开发工程师

    根据技术水平不同,Java程序员可以分为初级.中级.高级.资深等.不同级别的Java程序员,企业的要求也是有区别.那么,该如何才能成为高级Java开发工程师? 如何才能成为高级Java开发工程师 想要 ...

  8. 记 随手科技2020届实习生笔试题(Java开发工程师)笔试题

    2020届实习生笔试题(Java开发工程师) 一.选择题(共6题,每小题5分,满分30分) 1 2 3 4 5 6 总分 B B A C B C 1.下列排序算法中,初始数据集合对排序性能无影响的是( ...

  9. 作为一名Java开发工程师需要掌握的专业技能

    在学习Java编程完之后,学员们面临的就是就业问题.作为一名Java开发工程师,企业在招聘的时候,也是有一定的标准的. 为了帮助大家更好的找到适合自己的工作,在这里分享了作为一名Java开发工程师需要 ...

最新文章

  1. JAVA必备——13个核心规范
  2. 如何一次关闭所有打开的标签?
  3. NLP:利用DictVectorizer对使用字典存储的数据进行特征抽取与向量化
  4. python 理解Matplotlib 3D (三维图) 绘图函数 plot_surface 的 rstride 和 cstride参数
  5. Flink数据清洗(Kafka事实表+Redis维度表)
  6. 【WebRTC---进阶篇】(一)服务器基础编程
  7. 1736. 替换隐藏数字得到的最晚时间
  8. c# 通用类扩展方法 备注
  9. Mybatis简单入门及配置文件标签详情
  10. 度盘高速下载器,比超级VIP还要快,推荐给大家
  11. php 生成不重复的会员卡号,php生成一个不重复的会员号
  12. python调用有道翻译_Python通过调用有道翻译api实现翻译功能示例
  13. 组合导航GPS+IMU
  14. Filecoin(FIL) 交易离线签名
  15. 华为RS入门2基础命令
  16. 指数基金定投指南 思维导图
  17. 泛微:打造协同办公OA第一品牌
  18. ubuntu Git 使用教程
  19. 绘声绘影快而省时的方法:使用小日本输出
  20. Portraiture全新4.0最新版人像磨皮插件更新内容

热门文章

  1. 人均年薪80万以上,docker到底是什么?为什么这么火?
  2. python查找文字在图片中的位置_在ppt中如何用文字环绕图片,ppt用文字环绕图片的方法...
  3. STM32读取MQ4传感器、DHT11温湿度传感器、GP2Y1014AU0F夏普光学灰尘传感器数据
  4. js中indexOf()返回,find(),findIndex(),includes()的使用,遍历数组返回符合条件
  5. 为什么我十分喜欢C,却很不喜欢C++?
  6. 无法移动文件到虚拟机
  7. 【老爸语录】2021年4月11日
  8. UC神马乘风大会抵沪 助力中小企业营销升级
  9. 最新WordPress快速建站 零基础网站建设 网站建站一条龙
  10. 谷歌的视频站点地图sitemap