① 深入浅出的分析 Set集合
Set集合的特点主要有:元素不重复、存储无序的特点。
打开 Set 集合,主要实现类有 HashSet、LinkedHashSet 、TreeSet 、EnumSet( RegularEnumSet、JumboEnumSet )等等,总结 Set 接口实现类,图如下:
由图中的继承关系,可以知道,Set 接口主要实现类有 AbstractSet、HashSet、LinkedHashSet 、TreeSet 、EnumSet( RegularEnumSet、JumboEnumSet ),其中 AbstractSet、EnumSet 属于抽象类,EnumSet 是在 jdk1.5 中新增的,不同的是 EnumSet 集合元素必须是枚举类型。
HashSet 是一个输入输出无序的集合,集合中的元素基于 HashMap 的 key 实现,元素不可重复;
LinkedHashSet 是一个输入输出有序的集合,集合中的元素基于 LinkedHashMap 的 key 实现,元素也不可重复;
TreeSet 是一个排序的集合,集合中的元素基于 TreeMap 的 key 实现,同样元素不可重复;
EnumSet 是一个与枚举类型一起使用的专用 Set 集合,其中 RegularEnumSet 和 JumboEnumSet 不能单独实例化,只能由 EnumSet 来生成,同样元素不可重复;
下面咱们来对各个主要实现类进行一一分析!
HashSet 是一个输入输出无序的集合,底层基于 HashMap 来实现,HashSet 利用 HashMap 中的key元素来存放元素,这一点我们可以从源码上看出来,阅读源码如下:
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable{
}
打开HashSet的add()方法,源码如下:
public boolean add(E e) {
//向 HashMap 中添加元素
return map.put(e, PRESENT)==null;
}
其中变量PRESENT,是一个非空对象,源码部分如下:
private static final Object PRESENT = new Object();
可以分析出,当进行add()的时候,等价于
HashMap map = new HashMap<>();
map.put(e, new Object());//e 表示要添加的元素
在之前的集合文章中,咱们了解到 HashMap 在添加元素的时候 ,通过equals()和hashCode()方法来判断传入的key是否相同,如果相同,那么 HashMap 认为添加的是同一个元素,反之,则不是。
从源码分析上可以看出,HashSet 正是使用了 HashMap 的这一特性,实现存储元素下标无序、元素不会重复的特点。
HashSet 的删除方法,同样如此,也是基于 HashMap 的底层实现,源码如下:
public boolean remove(Object o) {
//调用HashMap 的remove方法,移除元素
return map.remove(o)==PRESENT;
}
HashSet 没有像 List、Map 那样提供 get 方法,而是使用迭代器或者 for 循环来遍历元素,方法如下:
public static void main(String[] args) {
Set<String> hashSet = new HashSet<String>();
System.out.println("HashSet初始容量大小:"+hashSet.size());
hashSet.add("1");
hashSet.add("2");
hashSet.add("3");
hashSet.add("3");
hashSet.add("2");
hashSet.add(null);
}
输出结果:
HashSet初始容量大小:0
HashSet容量大小:4
null,1,2,3,
===========
null,1,2,3,
需要注意的是,HashSet 允许添加为null的元素。
LinkedHashSet 是一个输入输出有序的集合,继承自 HashSet,但是底层基于 LinkedHashMap 来实现。
如果你之前了解过 LinkedHashMap,那么你一定知道,它也继承自 HashMap,唯一有区别的是,LinkedHashMap 底层数据结构基于循环链表实现,并且数组指定了头部和尾部,虽然数组的下标存储无序,但是却可以通过数组的头部和尾部,加上循环链表,依次可以查询到元素存储的过程,从而做到输入输出有序的特点。
如果还不了解 LinkedHashMap 的实现过程,可以参阅集合系列中关于 LinkedHashMap 的实现过程文章。
阅读 LinkedHashSet 的源码,类定义如下:
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
}
查询源码,super调用的方法,源码如下:
HashSet(int initialCapacity, float loadFactor, boolean mmy) {
//初始化一个 LinkedHashMap
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
LinkedHshSet没有重写add方法,而是直接调用HashSet的add()方法,因为map的实现类是LinkedHashMap,所以此处是向LinkedHashMap中添加元素,当进行add()的时候,等价于
HashMap map = new LinkedHashMap<>();
map.put(e, new Object());//e 表示要添加的元素
LinkedHashSet也没有重写remove方法,而是直接调用HashSet的删除方法,因为LinkedHashMap没有重写remove方法,所以调用的也是HashMap的remove方法,源码如下:
public boolean remove(Object o) {
//调用HashMap 的remove方法,移除元素
return map.remove(o)==PRESENT;
}
同样的,LinkedHashSet 没有提供 get 方法,使用迭代器或者 for 循环来遍历元素,方法如下:
public static void main(String[] args) {
Set<String> linkedHashSet = new LinkedHashSet<String>();
System.out.println("linkedHashSet初始容量大小:"+linkedHashSet.size());
linkedHashSet.add("1");
linkedHashSet.add("2");
linkedHashSet.add("3");
linkedHashSet.add("3");
linkedHashSet.add("2");
linkedHashSet.add(null);
linkedHashSet.add(null);
}
输出结果:
linkedHashSet初始容量大小:0
linkedHashSet容量大小:4
1,2,3,null,
===========
1,2,3,null,
可见,LinkedHashSet 与 HashSet 相比,LinkedHashSet 输入输出有序。
TreeSet 是一个排序的集合,实现了NavigableSet、SortedSet、Set接口,底层基于 TreeMap 来实现。TreeSet 利用 TreeMap 中的key元素来存放元素,这一点我们也可以从源码上看出来,阅读源码,类定义如下:
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable {
}
new TreeSet<>()对象实例化的时候,表达的意思,可以简化为如下:
NavigableMap<E,Object> m = new TreeMap<E,Object>();
因为TreeMap实现了NavigableMap接口,所以没啥问题。
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable{
......
}
打开TreeSet的add()方法,源码如下:
public boolean add(E e) {
//向 TreeMap 中添加元素
return m.put(e, PRESENT)==null;
}
其中变量PRESENT,也是是一个非空对象,源码部分如下:
private static final Object PRESENT = new Object();
可以分析出,当进行add()的时候,等价于
TreeMap map = new TreeMap<>();
map.put(e, new Object());//e 表示要添加的元素
TreeMap 类主要功能在于,给添加的集合元素,按照一个的规则进行了排序,默认以自然顺序进行排序,当然也可以自定义排序,比如测试方法如下:
public static void main(String[] args) {
Map initMap = new TreeMap();
initMap.put("4", "d");
initMap.put("3", "c");
initMap.put("1", "a");
initMap.put("2", "b");
//默认自然排序,key为升序
System.out.println("默认 排序结果:" + initMap.toString());
//自定义排序,在TreeMap初始化阶段传入Comparator 内部对象
Map comparatorMap = new TreeMap<String, String>(new Comparator<String>() {
@Override
public int compare(String o1, String o2){
//根据key比较大小,采用倒叙,以大到小排序
return o2.compareTo(o1);
}
});
comparatorMap.put("4", "d");
comparatorMap.put("3", "c");
comparatorMap.put("1", "a");
comparatorMap.put("2", "b");
System.out.println("自定义 排序结果:" + comparatorMap.toString());
}
输出结果:
默认 排序结果:{1=a, 2=b, 3=c, 4=d}
自定义 排序结果:{4=d, 3=c, 2=b, 1=a}
相信使用过TreeMap的朋友,一定知道TreeMap会自动将key按照一定规则进行排序,TreeSet正是使用了TreeMap这种特性,来实现添加的元素集合,在输出的时候,其结果是已经排序好的。
如果您没看过源码TreeMap的实现过程,可以参阅集合系列文章中TreeMap的实现过程介绍,或者阅读 jdk 源码。
TreeSet 的删除方法,同样如此,也是基于 TreeMap 的底层实现,源码如下:
public boolean remove(Object o) {
//调用TreeMap 的remove方法,移除元素
return m.remove(o)==PRESENT;
}
TreeSet 没有重写 get 方法,而是使用迭代器或者 for 循环来遍历元素,方法如下:
public static void main(String[] args) {
Set<String> treeSet = new TreeSet<>();
System.out.println("treeSet初始容量大小:"+treeSet.size());
treeSet.add("1");
treeSet.add("4");
treeSet.add("3");
treeSet.add("8");
treeSet.add("5");
}
输出结果:
treeSet初始容量大小:0
treeSet容量大小:5
1,3,4,5,8,
===========
1,3,4,5,8,
使用自定义排序,有 2 种方法,第一种在需要添加的元素类,实现Comparable接口,重写compareTo方法来实现对元素进行比较,实现自定义排序。
/**
创建一个Person实体类,实现Comparable接口,重写compareTo方法,通过变量age实现自定义排序 测试方法如下:
public static void main(String[] args) {
Set<Person> treeSet = new TreeSet<>();
System.out.println("treeSet初始容量大小:"+treeSet.size());
treeSet.add(new Person("李一",18));
treeSet.add(new Person("李二",17));
treeSet.add(new Person("李三",19));
treeSet.add(new Person("李四",21));
treeSet.add(new Person("李五",20));
}
输出结果:
treeSet初始容量大小:0
treeSet容量大小:5
按照年龄从小到大,自定义排序结果:
李二:17,李一:18,李三:19,李五:20,李四:21,
第二种方法是在TreeSet初始化阶段,Person不用实现Comparable接口,将Comparator接口以内部类的形式作为参数,初始化进去,方法如下:
public static void main(String[] args) {
//自定义排序
Set<Person> treeSet = new TreeSet<>(new Comparator<Person>(){
@Override
public int compare(Person o1, Person o2) {
if(o1 == null || o2 == null){
//不用比较
return 0;
}
//从小到大进行排序
return o1.getAge() - o2.getAge();
}
});
System.out.println("treeSet初始容量大小:"+treeSet.size());
treeSet.add(new Person("李一",18));
treeSet.add(new Person("李二",17));
treeSet.add(new Person("李三",19));
treeSet.add(new Person("李四",21));
treeSet.add(new Person("李五",20));
}
输出结果:
treeSet初始容量大小:0
treeSet容量大小:5
按照年龄从小到大,自定义排序结果:
李二:17,李一:18,李三:19,李五:20,李四:21,
需要注意的是,TreeSet不能添加为空的元素,否则会报空指针错误!
EnumSet 是一个与枚举类型一起使用的专用 Set 集合,继承自AbstractSet抽象类。与 HashSet、LinkedHashSet 、TreeSet 不同的是,EnumSet 元素必须是Enum的类型,并且所有元素都必须来自同一个枚举类型,EnumSet 定义源码如下:
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable {
......
}
EnumSet是一个虚类,不能直接通过实例化来获取对象,只能通过它提供的静态方法来返回EnumSet实现类的实例。
EnumSet的实现类有两个,分别是RegularEnumSet、JumboEnumSet两个类,两个实现类都继承自EnumSet。
EnumSet会根据枚举类型中元素的个数,来决定是返回哪一个实现类,当 EnumSet元素中的元素个数小于或者等于64,就会返回RegularEnumSet实例;当EnumSet元素个数大于64,就会返回JumboEnumSet实例。
这一点,我们可以从源码中看出,源码如下:
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum<?>[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
//当元素个数小于或者等于 64 的时候,返回 RegularEnumSet
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
//大于64,返回 JumboEnumSet
return new JumboEnumSet<>(elementType, universe);
}
noneOf是EnumSet中一个静态方法,用于判断是返回哪一个实现类。
我们来看看当元素个数小于等于64的时候,使用RegularEnumSet的类,源码如下:
class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
}
RegularEnumSet 通过二进制运算得到结果,直接使用long来存放元素。
我们再来看看当元素个数大于64的时候,使用JumboEnumSet的类,源码如下:
class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
}
JumboEnumSet 也是通过二进制运算得到结果,使用long来存放元素,但是它是使用数组来存放元素。
二者相比,RegularEnumSet 效率比 JumboEnumSet 高些,因为操作步骤少,大多数情况下返回的是 RegularEnumSet,只有当枚举元素个数超过 64 的时候,会使用 JumboEnumSet。
添加元素:
//新建一个EnumEntity的枚举类型,定义2个参数
public enum EnumEntity {
WOMAN,MAN;
}
创建一个空的 EnumSet:
//创建一个 EnumSet,内容为空
EnumSet<EnumEntity> noneSet = EnumSet.noneOf(EnumEntity.class);
System.out.println(noneSet);
输出结果:
[]
创建一个 EnumSet,并将枚举类型的元素全部添加进去:
//创建一个 EnumSet,将EnumEntity 元素内容添加到EnumSet中
EnumSet<EnumEntity> allSet = EnumSet.allOf(EnumEntity.class);
System.out.println(allSet);
输出结果:
[WOMAN, MAN]
创建一个 EnumSet,添加指定的枚举元素:
//创建一个 EnumSet,添加 WOMAN 到 EnumSet 中
EnumSet<EnumEntity> customSet = EnumSet.of(EnumEntity.WOMAN);
System.out.println(customSet);
查询元素
EnumSet与HashSet、LinkedHashSet、TreeSet一样,通过迭代器或者 for 循环来遍历元素,方法如下:
EnumSet<EnumEntity> allSet = EnumSet.allOf(EnumEntity.class);
for (EnumEntity enumEntity : allSet) {
System.out.print(enumEntity + ",");
}
输出结果:
WOMAN,MAN,
HashSet 是一个输入输出无序的 Set 集合,元素不重复,底层基于 HashMap 的 key 来实现,元素可以为空,如果添加的元素为对象,对象需要重写 equals() 和 hashCode() 方法来约束是否为相同的元素。
LinkedHashSet 是一个输入输出有序的 Set 集合,继承自 HashSet,元素不重复,底层基于 LinkedHashMap 的 key来实现,元素也可以为空,LinkedHashMap 使用循环链表结构来保证输入输出有序。
TreeSet 是一个排序的 Set 集合,元素不可重复,底层基于 TreeMap 的 key来实现,元素不可以为空,默认按照自然排序来存放元素,也可以使用 Comparable 和 Comparator 接口来比较大小,实现自定义排序。
EnumSet 是一个与枚举类型搭配使用的专用 Set 集合,在 jdk1.5 中加入。EnumSet 是一个虚类,有2个实现类 RegularEnumSet、JumboEnumSet,不能显式的实例化改类,EnumSet 会动态决定使用哪一个实现类,当元素个数小于等于64的时候,使用 RegularEnumSet;大于 64的时候,使用JumboEnumSet类,EnumSet 其内部使用位向量实现,拥有极高的时间和空间性能,如果元素是枚举类型,推荐使用 EnumSet。
1、JDK1.7&JDK1.8 源码
2、程序园 - java集合-EnumMap与EnumSet
3、 Java极客技术 - https://blog.csdn.net/javageektech/article/details/103077788
② nginx 源代码分析 (一)
ngx_pool_t提供内存分配接口。
ngx_pool_t对大块内存和小块内存的分配使用不同的策略。
对于大块内存(超过指定阈值,一般是内存的页大小4096),调用malloc()动态分配。
对于小块内存,则是先预分配大的内存块,然后根据需要从中动态分配小块的。
ngx_create_pool()创建这个单向链表的第一个元素。
ngx_palloc()从指定的ngx_pool_t实例中分配内存。如果要求大块内存,则调用ngx_palloc_large(),否则调用ngx_palloc_small()。
对于ngx_palloc_small(),
对于ngx_palloc_large(),
ngx_free()负责释放内存。它只是调用free()释放大块内存。实际上小块内存是不能一个一个释放的,nginx的策略是释放就释放全部。
ngx_reset_pool()释放所有大块内存,将ngx_pool_t链表置于未使用状态。
ngx_destroy_pool()释放所有大块内存,也释放ngx_pool_t链表自身占用的内存。
nginx_pool_t还用于保存“待清理的任务”,这个任务保存在ngx_pool_cleanup_t结构中,从预分配内存中分配。这也是一个单向链表,保存在ngx_pool_t的成员cleanup中。
ngx_pool_cleanup_t的成员handler是任务的处理函数,成员data是处理函数的参数。
ngx_array_t实现了数组。
ngx_array_create()创建数组。
ngx_array_push()从数组中得到一个可用的元素。
ngx_list_create()实现单向链表。与一般链表不同的是,它的链表中每一个元素中保存的是一个数组。下图是包含两个ngx_list_part_t元素的链表。
ngx_list_create()创建一个链表。
ngx_list_push()从链表中得到一个可用的位置。
ngx_queue_t是一个双向链表。
使用ngx_queue_t的方式是,在结构中包含ngx_queue_t,就可以把结构串联起来。如下面的示意图。
ngx_queue_data()可以根据ngx_queue_t成员在结构中的位置偏移,从成员地址计算结构地址。
ngx_rbtree_t实现红黑树,ngx_rbtree_node_t是树上的节点。
对于ngx_rbtree_node_t,
对于ngx_rbtree_t,
③ 哈希算法从原理到实战
引言
将任意长度的二进制字符串映射为定长二进制字符串的映射规则我们称为散列(hash)算法,又叫哈希(hash)算法,而通过原始数据映射之后得到的二进制值称为哈希值。哈希表(hash表)结构是哈希算法的一种应用,也叫散列表。用的是数组支持按照下标随机访问数据的特性扩展、演化而来。可以说没有数组就没有散列表。
哈希算法主要特点
从哈希值不能反向推导原始数据,也叫单向哈希。
对输入数据敏感,哪怕只改了一个Bit,最后得到的哈希值也大不相同。
散列冲突的概率要小。
哈希算法执行效率要高,散列结果要尽量均衡。
哈希算法的核心应用
安全加密 :对于敏感数据比如密码字段进行MD5或SHA加密传输。
唯一标识 :比如图片识别,可针对图像二进制流进行摘要后MD5,得到的哈希值作为图片唯一标识。
散列函数 :是构造散列表的关键。它直接决定了散列冲突的概率和散列表的性质。不过相对哈希算法的其他方面应用,散列函数对散列冲突要求较低,出现冲突时可以通过开放寻址法或链表法解决冲突。对散列值是否能够反向解密要求也不高。反而更加关注的是散列的均匀性,即是否散列值均匀落入槽中以及散列函数执行的快慢也会影响散列表性能。所以散列函数一般比较简单,追求均匀和高效。
*负载均衡 :常用的负载均衡算法有很多,比如轮询、随机、加权轮询。如何实现一个会话粘滞的负载均衡算法呢?可以通过哈希算法,对客户端IP地址或会话SessionID计算哈希值,将取得的哈希值与服务器列表大小进行取模运算,最终得到应该被路由到的服务器编号。这样就可以把同一IP的客户端请求发到同一个后端服务器上。
*数据分片 :比如统计1T的日志文件中“搜索关键词”出现次数该如何解决?我们可以先对日志进行分片,然后采用多机处理,来提高处理速度。从搜索的日志中依次读取搜索关键词,并通过哈希函数计算哈希值,然后再跟n(机器数)取模,最终得到的值就是应该被分到的机器编号。这样相同哈希值的关键词就被分到同一台机器进行处理。每台机器分别计算关键词出现的次数,再进行合并就是最终结果。这也是MapRece的基本思想。再比如图片识别应用中给每个图片的摘要信息取唯一标识然后构建散列表,如果图库中有大量图片,单机的hash表会过大,超过单机内存容量。这时也可以使用分片思想,准备n台机器,每台机器负责散列表的一部分数据。每次从图库取一个图片,计算唯一标识,然后与机器个数n求余取模,得到的值就是被分配到的机器编号,然后将这个唯一标识和图片路径发往对应机器构建散列表。当进行图片查找时,使用相同的哈希函数对图片摘要信息取唯一标识并对n求余取模操作后,得到的值k,就是当前图片所存储的机器编号,在该机器的散列表中查找该图片即可。实际上海量数据的处理问题,都可以借助这种数据分片思想,突破单机内存、CPU等资源限制。
*分布式存储 :一致性哈希算法解决缓存等分布式系统的扩容、缩容导致大量数据搬移难题。
JDK集合工具实现 :HashMap、 LinkedHashMap、ConcurrentHashMap、TreeMap等。Map实现类源码分析,详见 https://www.jianshu.com/p/602324fa59ac
总结
本文从哈希算法的原理及特点,总结了哈希算法的常见应用场景。
其中基于余数思想和同余定理实现的哈希算法(除留取余法),广泛应用在分布式场景中(散列函数、数据分片、负载均衡)。由于组合数学中的“鸽巢”原理,理论上不存在完全没有冲突的哈希算法。(PS:“鸽巢”原理是指有限的槽位,放多于槽位数的鸽子时,势必有不同的鸽子落在同一槽内,即冲突发生。同余定理:如果a和b对x取余数操作时a%x = b%x,则a和b同余)
构造哈希函数的常规方法有:数据分析法、直接寻址法、除留取余法、折叠法、随机法、平方取中法等 。
常规的解决哈希冲突方法有开放寻址法(线性探测、再哈希)和链表法。JDK中的HashMap和LinkedHashMap均是采用链表法解决哈希冲突的。链表法适合大数据量的哈希冲突解决,可以使用动态数据结构(比如:跳表、红黑树等)代替链表,防止链表时间复杂度过度退化导致性能下降;反之开放寻址法适合少量数据的哈希冲突解决。
④ 求java学习路线图
二进制和十进制的转化
注释、单行注释、多行注释、文本注释、注释内容和字节码的关系
标识符、关键字、驼峰原则
变量的本质、内存画图、变量声明和初始化
变量的分类和作用域(局部变量、成员变量、静态变量)
常量和Final
基本数据类型介绍
整型变量和整型常量
浮点类型、float、double
char字符型、转义字符
boolean布尔型、if语句使用要点、布尔类型占用空间问题
运算符介绍
算数运算符(二元、自增、自减)
赋值和赋值运算符
关系运算符详解
逻辑运算符、短路运算符详解
位运算符详解
字符串连接符
条件运算符(三元运算符)
运算符优先级问题
自动类型转换详解
强制类型装换详解
基本数据类型装换常见错误、溢出、L问题
使用Scanner获取键盘输入
控制语句和实现逻辑对应
if单选结构
if_elseif_else多选结构
switch语句_IDEA更换主题
循环_while
循环_for循环_dowhile
嵌套循环
break和continue语句_标签_控制语句底层原理
写一个年薪计算机_网络查问题的秘诀(重要)
个人所得税计算器软件
方法核心详解_天才思维模型教你高手学习思维模式
方法的重载
递归结构讲解_递归头_递归体
面向过程和面向对象的区别
类和对象的概述
类的属性和方法
创建对象内存分析
构造方法(Construtor)及重载
对象类型的参数传递
this关键字
static关键字详解
局部代码块、构造代码块和静态代码块
package和import详解
JavaDoc生成API文档
面向对象的三大特性
面向对象之【封装(Encapsulation)】
访问权限修饰符
面向对象之【继承(Inheritance)】
Object类
方法重写Override
super关键字详解
重写equals()和toString()
继承中对象创建的内存分析
面向对象之【多态(Polymorphism)】
向上转型
向下转型
instanceof运算符
编译时和运行时详解
final修饰符
抽象类和抽象方法(abstrct)
接口的定义和实现
JDK8的接口新特性
接口应用:内部类比较器Comparable
内部类详解
Java的内存管理与垃圾回收
异常的概述
异常的执行过程与分析
try-catch-finally捕捉异常
throw抛出异常
throws声明异常
异常继承体系
运行时异常和编译异常
自定义异常
自动装箱和自动拆箱
包装类的源码分析
String类的使用与内存原理
String类的源码分析
StringBuffer
StringBuilder
字符串处理类性能分析
Date类
System类
DateFormat类
Calendat类
Math类
BigInteger类和BigDecimal类
Random类
枚举类
File类
常见的面试题讲述与分析
数据结构算法
数据结构的概述
线性表
顺序表
链表
栈和队列
树
二叉树
二叉查找树
二叉平衡树
黑红树
图
冒泡排序
选择排序
递归
折半查找
集合(容器)
集合和数组的联系和区别
集合框架体系
ArrayList的使用和源码分析
集合中使用泛型
LinkedList的使用和源码分析
HashSet的使用和源码分析
哈希表及原理
TreeSet的使用和源码分析
比较器Comparable和Comparator
HashMap的使用和源码分析
TreeMap的使用和源码分析
Iterator于ListIterator
Collections工具类
旧集合类Vector、Hashtable
集合总结和选择依据
泛型接口
泛型类
泛型方法
IO流
IO流的概念
IO流的分类及其原理分析
文件流FlieInputStream、FileOutputStream
缓冲流BufferedInputStream、BufferedOutputStream
数据流ObjectInputStream、ObjectOutputStream
序列化和反序列化
转换流InputStreamReader、OutputStreamWriter
打印流PrintWrite和PrintStream
数组流ByteArrayOutputStream、ByteArrayInputStream
使用IO复制文件夹
多线程
进程和线程
线程的创建与启动
创建线程的三种方式对比
线程的生命周期
线程控制
多线程的安全问题与解决办法
线程的同步:同步代码块
线程的同步:同步方法
线程的同步:Lock锁
线程的死锁问题
线程通信
Condition
线程的完整生命周期
线程池ThreadPoolExecutor
ForkJoin框架
ThreadLocal类
网络编程
计算机网络基础知识
网络通信协议
OSI参考模型
TCP/IP参考模型
数据的封装与拆封原理解析
TCP协议
UDP协议
IP地址和端口号
URL和Socket
使用TCP编程实现登录功能
使用UDP编程实现客服系统
使用TCP编程实现文件上传
集合提升寻训练
手写ArrayList
手写单链表
手写Linkedlist
手写HashMap
手写HashSet
最新并发集合类
多线程提升训练
生产者消费者模式扩展
Lock锁和Condition
ReadWriteLock
BlockingQueue
volatile关键字
多线程题目练习
JDK新特征
面试题详解
设计模式
设计模式入门
面向对象设计七大原则
简单工厂模式
工厂方法模式
单例模式
原型模式
装饰模式
适配器模式
外观模式
MySQL基础
数据库基础知识
MySQL基础知识
MySQL8新特征
安装和卸载MySQL8
使用navicat访问数据库
SQL语言入门
创建数据库表
DML
修改删除数据库表
表的完整性约束
表的外键约束
DML扩展
MySQL 查询语句
基本select查询
where子句
函数
group by
having
SQL99-内连接查询
SQL99-外连接查询
SQL99-自连接查询
SQL92-连接查询
不相关子查询
相关子查询
分页查询
数据库对象
索引
事务及其特征
事务的并发问题
事务的隔离级别
存储过程
导入导出数据
JDBC
JDBC概述
使用JDBC完成添加/更新/删除操作
使用JDBC完成查询操作
JDBC常用接口
使用PreparedStatement
使用事务完成银行转账
提取DBUtil工具类
使用Properties读写属性文件
日志框架log4j
开发员工管理系统
第三阶段:JavaEE阶段
Servlet
web开发概述
B/S和C/S架构简介
HTTP协议
HTTP请求头和响应头
Tomcat安装使用
Tomcat目录结构
Servlet概述
Servlet快速入门
Servlet生命周期
读取配置文件信息
HttpServletRequest
HttpServletResponse
GET和POST区别
解决中文乱码
请求转发与重定向
绝对路径和相对路径
Cookie
Session
ServletContext
ServletConfig
JSP
JSP技术介绍
JSP的执行过程
scriptlet
表达式
声明
JSP指令元素
JSP动作元素
JSP隐式对象
JSP底层原理
九大内置对象
四个作用域
Servlet和JSP的关系和区别
MVC模式
合并Servlet
JavaScript
JavaScript概述与特点
JS基础语法
函数
数组
Math对象
String对象
Date对象
事件event
浏览器开发者工具
console
DOM和BOM
window
location
navigator
history
认识DOM
DOM获取元素
jQuery
jQuery简介及快速入门
jQuery入口函数
jQuery对象与DOM对象互相转换
基本选择器
属性选择器
位置选择器
表单选择器
内容选择器
jQuery事件
jQuery动画效果
DOM操作-操作文本
DOM操作-操作属性
DOM操作-操作元素
直接操作CSS样式
操作CSS类样式
购物车案例
表单验证
正则表达式
EL+JSTL+过滤器+监听器
EL介绍及使用
EL取值原理
EL隐含对象
EL逻辑运算
JSTL介绍-核心标签库
JSTL核心标签库
JSTL-格式标签库
Filter原理
Filter生命周期
Filter链
Filter登录验证
Filter权限控制
Listener概述及分类
Listener监听在线用户
Ajax和JSON
Ajax异步请求和局部刷新的原理
使用原生Ajax验证用户唯一性
jQuery Ajax
JSON的格式和使用
主要JSON解析器
Jackson的使用
Jackson的实现原理
使用jQuery Ajax实现三级联动
使用jQuery Ajax实现自动补全
分页和文件上传/下载
分页的意义
理解分页工具类
实现基本分页
实现带查询的分页
文件上传原理
文件上传API
实现文件上传
文件下载原理
文件下载响应头
实现文件下载
第四阶段:框架阶段
MyBatis
MyBatis概述
MyBatis入门配置
基本的CRUD操作
核心配置文件详解
Mapper.xml基础详解
模糊查询
分页的实现及插件PageHelper的使用
动态sql+sql片段的使用
一对多、多对一的关系处理
注解的使用
一级缓存和二级缓存说明及使用
generator逆向工程使用
Spring
Spring框架简介
Spring官方压缩包目录介绍
Spring环境搭建
IoC/DI容器详解
Spring创建Bean的三种方式
scope属性讲解
Spring中几种注入方式
静态代理设计模式
动态代理设计模式
AOP详解
AOP中几种通知类型
AOP两种实现方式
自动注入
声明式事务
事务传播行为
事务隔离级别
只读事务
事务回滚
基于注解式配置
常用注解
Spring 整合MyBatis
i18n
Spring整合Junit
SpringMVC
MVC架构模式
手写MVC框架
SpringMVC简介
SpringMVC运行原理
基于配置文件方式搭建环境
基于注解方式搭建环境
SpringMVC的跳转及视图解析器的配置
SpringMVC和Ajax的交互
Spring 参数注入
SpringMVC作用域传值
视图解析器
文件下载
文件上传
Spring拦截器/拦截器栈
登录状态验证
SpringMVC容器和Spring容器介绍
异常处理4种方式
SpringMVC5其他常用注解
Maven
Maven简介
Maven原理
Linux安装及注意事项
Maven项目结构
POM模型
Maven 中项目类型
创建WAR类型的Maven项目
scope属性可取值
SSM项目拆分演示
Maven的常见插件讲解
热部署
BootStrap
BootStrap概述
BootStrap栅格系统
BootStrap常用全局CSS样式
常用组件
常用JavaScript插件
RBAC
RBAC概述
RBAC发展历史
基于RBAC的数据库表设计
URL拦截实现
动态菜单实现
密码学
第五阶段:前后端分离阶段
Spring Boot
Spring Boot简介
Spring Boot实现Spring MVC
配置文件顺序及类型讲解
Spring Boot项目结构
Spring Boot 整合MyBatis
Spring Boot 整合Druid
Spring Boot 整合PageHelper
Spring Boot 整合logback
Spring Boot 整合JSP
Spring Boot 整合Thymeleaf
Spring Boot 开发者工具
Spring Boot 异常显示页面
Spring Boot 整合Junit4
Spring Boot 项目打包部署
Spring Boot 整合Quartz
Spring Boot 中Interceptor使用
Spring Boot Actuator
HikariCP
Logback
Logback简介
Logback依赖说明
Logback 配置文件讲解
Logback 控制台输出
Logback 文件输出
Logback 数据库输出
Spring Security
Spring Security简介
Spring Security架构原理
什么是认证和授权
基础环境搭建
自定义认证流程
UserDetailsService和UserDetails
PasswordEncoder
自定义认证结果
授权-访问路径匹配方式
授权-权限管理
基于注解实现权限管理
Thymeleaf整合Security权限管理
Rememberme 实现
退出实现
CSRF
Linux - CentOS 8
Linux简介
VMWare安装及使用
Linux安装及注意事项
Linux目录结构及路径
Linux常用命令
VMWare常用配置
XShell安装及使用
Xftp安装及使用
JDK解压版配置步骤
Tomcat配置步骤
安装MySQL
WAR包部署
Docker
Docker简介
Docker与VM对比
Docker特点
Docker架构
Docker安装与启动
镜像加速器配置
Docker镜像操作常用命令
Docker容器操作常用命令
DockerFile
搭建本地镜像仓库
推送镜像到阿里云及本地仓库
Docker容器生命周期
Docker数据管理
Redis
Redis简介
Redis 单机版安装
Redis 数据类型介绍
Redis 常用命令
Redis 持久化方案
Redis 的主从搭建
Redis的哨兵搭建
Redis 的集群搭建
Spring Boot整合Spring Data Redis
Redis的缓存穿透
Redis的缓存雪崩
Redis的缓存击穿
Vue
vsCode和插件安装
webpack介绍
Vue项目创建
Vue模板语法
Vue条件渲染
Vue列表渲染
Vue事件处理
Vue计算属性
Vue Class与Style
Vue表单处理
Vue组件
Vue组件生命周期
Vue 路由配置
Vue Axios网络请求
Vue跨域处理
Vue Element
Mock.js
Swagger
Swagger2简介
Springfox
Swagger2基本用法
Swagger-UI用法
Swagger2配置
Swagger2常用配置
Git/GitEE
Git的下载和安装
Git和SVN对比
Git创建版本库
Git版本控制
Git远程仓库
Git分支管理
Git标签管理
GitEE建库
GitEE 连接及使用
GitEE 组员及管理员配置
第六阶段:微服务架构
FastDFS
分布式文件系统概述
FastDFS简介
FastDFS架构
Tracker Server
Storage Server
FastDFS安装
安装带有FastDFS模块的Nginx
Fastdfs-java-client的使用
创建Fastdfs-java-client工具类
实现文件上传与下载
KindEditor介绍
通过KindEditor实现文件上传并回显
RabbitMQ
AMQP简介
RabbitMQ简介
安装Erlang
安装RabbitMQ
RabbitMQ原理
Spring Boot 集成RabbitMQ
RabbitMQ的交换器
Spring AMQP的使用
Spring Cloud Netflix Eureka
Eureka简介
Eureka和Zookeeper 对比
搭建Eureka注册中心
Eureka 服务管理平台介绍
搭建高可用集群
集群原理
Eureka优雅停服
Spring Cloud Netflix Ribbon
Ribbon简介
集中式与进程内负载均衡区别
Ribbon常见的负载均衡策略
Ribbon的点对点直连
Spring Cloud OpenFeign
Feign简介
Feign的请求参数处理
Feign的性能优化
配置Feign负载均衡请求超时时间
Spring Cloud Netflix Hystrix
Hystrix简介
服务降级
服务熔断
请求缓存
Feign的雪崩处理
可视化的数据监控Hystrix-dashboard
Spring Cloud Gateway
Spring Cloud Gateway简介
Gateway基于配置文件实现路由功能
Gateway基于配置类实现路由功能
Gateway中内置过滤器的使用
Gateway中自定义GatewayFilter过滤器的使用
Gateway中自定义GlobalFilter过滤器的使用
Gateway中使用过滤器实现鉴权
Gateway结合Hystrix实现熔断功能
Spring Cloud Config
什么是分布式配置中心
创建配置中心服务端
创建配置中心客户端
基于Gitee存储配置文件
基于分布式配置中心实现热刷新
Spring Cloud Bus
什么是消息总线
基于消息总线实现全局热刷新
ELK
ElasticSearch介绍
ElasticSearch单机版安装
ElasticSearch集群版安装
ElasticSearch索引管理
ElasticSearch文档管理
ElasticSearch文档搜索
SpringDataElasticSearch访问ElasticSearch
LogStash介绍
基于LogStash收集系统日志
TX-LCN
分布式事务简介
分布式事务两大理论依据
分布式事务常见解决方案
LCN简介
TX-LCN的3种模式
LCN原理
LCN环境搭建及Demo演示
Nginx
Nginx的简介
什么是正向代理、反向代理
Nginx的安装
Nginx配置虚拟主机
Nginx配置服务的反向代理
Nginx的负载均衡配置
Spring Session
Spring Session介绍
通过Spring Session共享session中的数据
通过Spring Session同步自定义对象
Spring Session的Redis存储结构
设置Session失效时间
Spring Session序列化器
MyBatis Plus
MyBatis Plus简介
Spring整合MyBatis Plus
MyBatis Plus的全局策略配置
MyBatis 的主键生成策略
MyBatis Plus的CRUD操作
条件构造器EntityWrapper讲解
MyBatis Plus的分页插件配置
MyBatis Plus的分页查询
MyBatis Plus的其他插件讲解
MyBatis Plus的代码生成器讲解
MyBatis Plus的公共字段自动填充
ShardingSphere
简介
数据库切分方式
基本概念
MySQL主从配置
切片规则
读写分离
实现分库分表
第七阶段:云服务阶段
Kafka
Kafka简介
Kafka架构
分区和日志
Kafka单机安装
Kafka集群配置
自定义分区
自动控制
Spring for Apache Kafka
Zookeeper
Zookeeper简介和安装
Zookeeper 数据模型
Zookeeper 单机版安装
Zookeeper常见命令
ZClient操作Zookeeper
Zookeeper 集群版安装
Zookeeper 客户端常用命令
Zookeeper分布式锁
RPC
什么是分布式架构
什么是RFC、RPC
HttpClient实现RPC
RestTemplate
RMI实现RPC
基于Zookeeper实现RPC 远程过程调用
Dubbo
SOA架构介绍
Dubbo简介
Dubbo结构图
Dubbo注册中心
Dubbo 支持的协议
Dubbo 注册中心搭建
Spring Boot 整合 Dubbo
Admin管理界面
Dubbo 搭建高可用集群
Dubbo 负载均衡
Spring Cloud Alibaba Dubbo
Spring Cloud Alibaba Dubbo简介
基于Zookeeper发布服务
基于Zookeeper订阅服务
实现远程服务调用处理
Spring Cloud Alibaba Nacos
Spring Cloud Alibaba Nacos简介
搭建Nacos服务器
基于Nacos发布|订阅服务
实现远程服务调用处理
Nacos Config配置中心
Spring Cloud Alibaba Sentinel
Spring Cloud Alibaba Sentinel简介
搭建Sentinel服务器
Sentinel-实时监控
Sentinel-簇点链路
Sentinel-授权规则
Sentinel-系统规则
@SentinelResource注解
持久化规则
Spring Cloud Alibaba Seata
Spring Cloud Alibaba Seata简介
搭建Seata服务器
Seata支持的事务模式-AT模式
Seata支持的事务模式-TCC模式
Seata支持的事务模式-Saga模式
Seata支持的事务模式-XA模式
SeataAT事务模式应用方式
SeataTCC事务模式应用方式