先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。






既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注Java)

正文
如果你的初始值是1,则在并发情况下每个线程的初始化都是1,那么你的最终和就会比你预想的结果要大。
2.2 max:利用归约求最大
max方法也是一个归约方法,它是直接调用了reduce方法。
先来看一个示例:
Optional max = List.of(1, 2, 3).stream()
.max((a, b) -> {
if (a > b) {
return 1;
} else {
return -1;
}
});
复制代码
没错,这就是max方法用法,这让我觉得我不是在使用函数式接口,当然你也可以使用Integer的方法进行简化:
Optional max = List.of(1, 2, 3).stream()
.max(Integer::compare);
复制代码
哪怕如此,这个方法依旧让我感觉到很繁琐,我虽然可以理解在max方法里面传参数是为了让我们自己自定义排序规则,但我不理解为什么没有一个默认按照自然排序进行排序的方法,而是非要让我传参数。
直到后来我想到了基础类型Stream,果然,它们里面是可以无需传参直接拿到最大值:
OptionalLong max = LongStream.of(1, 2, 3).max();
复制代码
果然,我能想到的,类库设计者都想到了~
注 :OptionalLong是Optional对基础类型long的封装。
2.3 min:利用归约求最小
min还是直接看例子吧:
Optional max = List.of(1, 2, 3).stream()
.min(Integer::compare);
复制代码
它和max区别就是底层把 > 换成了 <,过于简单,不再赘述。
3. 收集器
第三节我们来看看收集器,它的作用是对Stream中的元素进行收集而形成一个新的集合。
虽然我在本篇开头的时候已经给过一张思维导图了,但是由于收集器的API比较多所以我又画了一张,算是对开头那张的补充:

收集器的方法名是collect,它的方法定义如下:
<R, A> R collect(Collector<? super T, A, R> collector);
复制代码
顾名思义,收集器是用来收集Stream的元素的,最后收集成什么我们可以自定义,但是我们一般不需要自己写,因为JDK内置了一个Collector的实现类——Collectors。
3.1 收集方法
通过Collectors我们可以利用它的内置方法很方便的进行数据收集:
比如你想把元素收集成集合,那么你可以使用toCollection或者toList方法,不过我们一般不使用toCollection,因为它需要传参数,没人喜欢传参数。
你也可以使用toUnmodifiableList,它和toList区别就是它返回的集合不可以改变元素,比如删除或者新增。
再比如你要把元素去重之后收集起来,那么你可以使用toSet或者toUnmodifiableSet。
接下来放一个比较简单的例子:
// toList
List.of(1, 2, 3).stream().collect(Collectors.toList());
// toUnmodifiableList
List.of(1, 2, 3).stream().collect(Collectors.toUnmodifiableList());
// toSet
List.of(1, 2, 3).stream().collect(Collectors.toSet());
// toUnmodifiableSet
List.of(1, 2, 3).stream().collect(Collectors.toUnmodifiableSet());
复制代码
以上这些方法都没有参数,拿来即用,toList底层也是经典的ArrayList,toSet 底层则是经典的HashSet。
也许有时候你也许想要一个收集成一个Map,比如通过将订单数据转成一个订单号对应一个订单,那么你可以使用toMap():
List orders = List.of(new Order(), new Order());
Map<String, Order> map = orders.stream()
.collect(Collectors.toMap(Order::getOrderNo, order -> order));
复制代码
toMap() 具有两个参数:
-
第一个参数代表key,它表示你要设置一个Map的key,我这里指定的是元素中的orderNo。
-
第二个参数代表value,它表示你要设置一个Map的value,我这里直接把元素本身当作值,所以结果是一个Map<String, Order>。
你也可以将元素的属性当作值:
List orders = List.of(new Order(), new Order());
Map<String, List> map = orders.stream()
.collect(Collectors.toMap(Order::getOrderNo, Order::getItemList));
复制代码
这样返回的就是一个订单号+商品列表的Map了。
toMap() 还有两个伴生方法:
-
toUnmodifiableMap():返回一个不可修改的Map。
-
toConcurrentMap():返回一个线程安全的Map。
这两个方法和toMap() 的参数一模一样,唯一不同的就是底层生成的Map特性不太一样,我们一般使用简简单单的toMap() 就够了,它的底层是我们最常用的HashMap() 实现。
toMap() 功能虽然强大也很常用,但是它却有一个致命缺点。
我们知道HahsMap遇到相同的key会进行覆盖操作,但是toMap() 方法生成Map时如果你指定的key出现了重复,那么它会直接抛出异常。
比如上面的订单例子中,我们假设两个订单的订单号一样,但是你又将订单号指定了为key,那么该方法会直接抛出一个IllegalStateException,因为它不允许元素中的key是相同的。
3.2 分组方法
如果你想对数据进行分类,但是你指定的key是可以重复的,那么你应该使用groupingBy 而不是toMap。
举个简单的例子,我想对一个订单集合以订单类型进行分组,那么可以这样:
List orders = List.of(new Order(), new Order());
Map<Integer, List> collect = orders.stream()
.collect(Collectors.groupingBy(Order::getOrderType));
复制代码
直接指定用于分组的元素属性,它就会自动按照此属性进行分组,并将分组的结果收集为一个List。
List orders = List.of(new Order(), new Order());
Map<Integer, Set> collect = orders.stream()
.collect(Collectors.groupingBy(Order::getOrderType, toSet()));
复制代码
groupingBy还提供了一个重载,让你可以自定义收集器类型,所以它的第二个参数是一个Collector收集器对象。
对于Collector类型,我们一般还是使用Collectors类,这里由于我们前面已经使用了Collectors,所以这里不必声明直接传入一个toSet()方法,代表我们将分组后的元素收集为Set。
groupingBy还有一个相似的方法叫做groupingByConcurrent(),这个方法可以在并行时提高分组效率,但是它是不保证顺序的,这里就不展开讲了。
3.3 分区方法
接下来我将介绍分组的另一种情况——分区,名字有点绕,但意思很简单:
将数据按照TRUE或者FALSE进行分组就叫做分区。
举个例子,我们将一个订单集合按照是否支付进行分组,这就是分区:
List orders = List.of(new Order(), new Order());
Map<Boolean, List> collect = orders.stream()
.collect(Collectors.partitioningBy(Order::getIsPaid));
复制代码
因为订单是否支付只具有两种状态:已支付和未支付,这种分组方式我们就叫做分区。
和groupingBy一样,它还具有一个重载方法,用来自定义收集器类型:
List orders = List.of(new Order(), new Order());
Map<Boolean, Set> collect = orders.stream()
.collect(Collectors.partitioningBy(Order::getIsPaid, toSet()));
复制代码
3.4 经典复刻方法
终于来到最后一节了,请原谅我给这部分的方法起了一个这么土的名字,但是这些方法确实如我所说:经典复刻。
换言之,就是Collectors把Stream原先的方法又实现了一遍,包括:
-
map →
mapping -
filter →
filtering -
flatMap →
flatMapping -
count →
counting -
reduce →
reducing -
max →
maxBy -
**min ** →
minBy
这些方法的功能我就不一一列举了,之前的文章已经讲的很详尽了,唯一的不同是某些方法多了一个参数,这个参数就是我们在分组和分区里面讲过的收集参数,你可以指定收集到什么容器内。
我把它们抽出来主要想说的为什么要复刻这么多方法处理,这里我说说个人见解,不代表官方意见。
我觉得主要是为了功能的组合。
什么意思呢?比方说我又有一个需求:使用订单类型对订单进行分组,并找出每组有多少个订单。
订单分组我们已经讲过了,找到其每组有多少订单只要拿到对应list的size就行了,但是我们可以不这么麻烦,而是一步到位,在输出结果的时候键值对就是订单类型和订单数量:
Map<Integer, Long> collect = orders.stream()
.collect(Collectors.groupingBy(Order::getOrderType, counting()));
复制代码
就这样,就这么简单,就好了,这里等于说我们对分组后的数据又进行了一次计数操作。
上面的这个例子可能不对明显,当我们需要对最后收集之后的数据在进行操作时,一般我们需要重新将其转换成Stream然后操作,但是使用Collectors的这些方法就可以让你很方便的在Collectors中进行数据的处理。
总结
无论是哪家公司,都很重视高并发高可用的技术,重视基础,重视JVM。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。
最后我整理了一些面试真题资料,技术知识点剖析教程,还有和广大同仁一起交流学习共同进步,还有一些职业经验的分享。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
总结
无论是哪家公司,都很重视高并发高可用的技术,重视基础,重视JVM。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。
最后我整理了一些面试真题资料,技术知识点剖析教程,还有和广大同仁一起交流学习共同进步,还有一些职业经验的分享。
[外链图片转存中…(img-i4kNIx9p-1713704431395)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-E8ntFojv-1713704431395)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
976

被折叠的 条评论
为什么被折叠?



