最新要闻
- 【新要闻】128核心Arm处理器能跑Windows!还能配NVIDIA显卡
- 时讯:新疆和硕:田间地头 话两会
- 【天天速看料】马龙惊魂3-2拒绝一轮游,与小林再迎老少对决!国乒首日狂走钢丝
- 在米其林餐厅找罪受?人均8千元餐厅疑发生食物中毒
- 世界热点!张艺谋宣布将拍网剧《英雄联盟》!第一次 想拍个不一样的
- FSR、DLSS谁更好?26款游戏PK:AMD居然0胜!
- 旅游专列来啦具体详细内容是什么
- 中国新能源汽车在欧洲火了:每10辆就有1辆来自中国
- 程序员午休时健身猝死 被认定工伤 法院:属于在岗状态
- 比亚迪车主用木头手工还原汉EV:前后双电机 真能跑
- 简讯:招标活动法律保障工作实务指南 2016年版
- 锐龙7 7800X3D配A620主板 性能损失多少?实在没想到
- 天天微头条丨成都升温 千足虫满街爬:专家科普小心毒臭液
- 全球最大盗版电子书网站Z-Library被封后复活:路子更野了!要共享纸质书
- 速读:Steam排名第一!GTX 1650亮机卡火爆断货:RTX 2070紧急变身救场
- 天天速递!只剩Windows版了!B站正式宣布UWP版停止维护
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
Java8 Stream流式编程
原创/朱季谦
(资料图片)
本文主要基于实际项目常用的Stream Api流式处理总结。
因笔者主要从事风控反欺诈相关工作,故而此文使用比较熟悉的三要素之一的【手机号】黑名单作代码案例说明。
我在项目当中,很早就开始使用Java 8的流特性进行开发了,但是一直都没有针对这块进行开发总结。这次就对这一块代码知识做一次全面总结,在总结的过程中去发现自己的不足,同时方便日后开发查询。
在实际项目当中,若能熟练使用Java8 的Stream流特性进行开发,就比较容易写出简洁优雅的代码。目前市面上很多开源框架,如Mybatis- Plus、kafka Streams以及Flink流处理等,都有一个相似的地方,即用到Stream流特性,其写出的代码简洁而易懂,当然,若是在不熟悉流特性的基础上而贸然去使用Stream开发的话,难免会写出一手bug。
此文主要适合新手。
一、Stream中间操作
Stream的中间操作是指在流链当中,可以对数据进行处理操作,包括filter过滤、map映射转换、flatMap合并、distinct去重、sorted排序等操作。这些操作都会返回一个新的Stream流对象,可以通过链式调用多个中间操作进行复杂的数据处理。需要注意的是,中间操作需要具有终止操作才会触发。
下面按类别讲解Stream常见的中间操作。
1.1、filter:过滤出符合条件的元素。
filter()方法常用于实现数据过滤,即可以对集合、数组等数据源筛选出符合指定条件的元素,并返回一个新的流。
假设有一个黑名单手机号列表,需要筛选出其中所有开头为“133”的元素,那么可以通过filter()实现——
//将数组转换为一个字符串列表List numbers = Arrays.asList("13378520000","13278520000","13178520000","13358520000");//通过stream()方法创建一个流,接着使用filter()方法过滤出前缀为“133”的元素,最终通过collect() 方法将结果收集到一个新列表中List filterdNumbers = numbers.stream().filter(s -> s.startsWith("133")).collect(Collectors.toList());System.out.println(filterdNumbers);打印结果:[13378520000, 13358520000]
1.2、map:映射转换元素。
map()方法用于对流中的每个元素进行映射操作,将其转换为另一个元素或者提取其中的信息,并返回一个新的流。
根据以下两个案例分别学习map()将元素转换为另一个元素以及提取元素其中的信息——
1.2.1、转换元素
假设有一个手机号字符列表,需要根据前7位来确定手机号归属地,那么就需要获取所有手机号前7位子字符串,可以使用map()方法实现:
List numbers = Arrays.asList("13378520000","13278520000","13178520000","13558520000");//通过stream()方法创建一个流,使用map()方法将每个字符串转换为截取前7位的字符,最后使用collect()方法将结果收集到一个新列表中List filterdNumbers = numbers.stream().map(s -> s.substring(0,7)).collect(Collectors.toList());System.out.println(filterdNumbers);打印结果:[1337852, 1327852, 1317852, 1355852]
1.2.2、提取元素信息
假设有一个用户对象列表,我们需要提取其中每个对象的手机号,可以使用map()方法实现:
List peopleList = Arrays.asList( new People("王二","13378520000"), new People("李二","13278520000"), new People("张四","13178520000"));//通过stream()方法创建一个流,使用map()方法提取每个用户的手机号,最后使用collect()方法将结果收集到一个新列表中List tel = peopleList.stream().map(People::getTel).collect(Collectors.toList());System.out.println(tel);打印结果:[13378520000, 13278520000, 13178520000]
1.3、flatMap:将多个流合并为一个流。
flatMap()方法可以实现多对多的映射,或者将多个列表合并成一个列表操作。
1.3.1、实现多对多的映射
假设有两组余额列表A和B,需要将A组每个元素都与B组所有元素依次进行相加,可以使用flatMap实现该多对多的映射——
List listA = Arrays.asList(1, 2, 3);List listB = Arrays.asList(4, 5, 6);List list = listA.stream().flatMap(a -> listB.stream().map(b -> a +b)).collect(Collectors.toList());System.out.println(list);打印结果: [5, 6, 7, 6, 7, 8, 7, 8, 9]
1.3.2、将多个列表合并成一个列表
假设有一个包含多个手机号字符串列表的列表,现在需要合并所有手机号字符串成为一个列表,可以使用flatMap()方法实现:
List> listOfLists = Arrays.asList( Arrays.asList("13378520000", "13278520000"), Arrays.asList("13178520000", "13558520000"), Arrays.asList("15138510000", "15228310000"));List flatMapList = listOfLists.stream().flatMap(Collection::stream).collect(Collectors.toList());System.out.println(flatMapList);打印结果:[13378520000, 13278520000, 13178520000, 13558520000, 15138510000, 15228310000]
1.4、distinct:去除重复的元素。
distinct()方法可以用来去除流中的重复元素,生成无重复的列表。
假设有一个包含重复手机号字符串的列表,可以使用distinct()去重操作——
List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15138510000");List disNumbers = numbers.stream().distinct().collect(Collectors.toList());System.out.println(disNumbers);打印结果:[13378520000, 15138510000, 13178520000]
注意一点的是,distinct用于针对流作去重操作时,需要确定流中元素实现了equals()和hashCode()方法,因为这两个方法是判断两个对象是否相等的标准。
1.5、sorted:排序元素。
sorted()方法用于对流中的元素进行排序。
假设需要对一组People对象按照年龄排序,下面分别按照升序排序和降序排序——
1.5.1、升序排序
默认情况下,是升序排序——
List peopleList = Arrays.asList( new People("王二",20), new People("李二",30), new People("张四",31));List newpeopleList=peopleList.stream().sorted(Comparator.comparing(People::getAge)).collect(Collectors.toList());//打印结果newpeopleList.stream().forEach(System.out::println);打印结果:People{name="王二", age=20}People{name="李二", age=30}People{name="张四", age=31}
1.5.2、降序排序
通过reversed()方法进行逆序排序,也就是将升序排序进行倒序排序——
List peopleList = Arrays.asList( new People("王二",20), new People("李二",30), new People("张四",31));List newpeopleList = peopleList.stream().sorted(Comparator.comparing(People::getAge).reversed()).collect(Collectors.toList());//打印结果newpeopleList.stream().forEach(System.out::println);打印结果:People{name="张四", age=31}People{name="李二", age=30}People{name="王二", age=20}
1.6、peek:查看每个元素的信息,但不修改流中元素的状态。
peek()方法用于查看流中的元素而不会修改流中元素的状态,可以在流中的任何阶段使用,不会影响到流的操作,也不会终止流的操作。
List telList = Arrays.asList("13378520000","13278520000","13178520000","13558520000");telList.stream().peek(t -> System.out.println(t)) .map(t -> t.substring(0,3)) .peek(t -> System.out.println(t)) .collect(Collectors.toList());打印结果:1337852000013313278520000132
peek()方法和forEach很类似,都是可以用于遍历流中的元素,但是,两者之间存在较大的区别。主要一点是,forEach在流中是一个终止操作,一旦调用它,就意味着Stream流已经被处理完成,不能再进行任何操作,例如,无法在forEach之后针对流进行map、filter等操作,但peek方法可以,以上的案例可以看出,在第一次调用peek打印一个元素后,该元素还可以接着进行map操作,进行字符串的前三位截取。
这是peek()方法和forEach最大的区别。
1.7、limit 和 skip:截取流中的部分元素。
limit()和skip()都是用于截取Stream流中部分元素的方法,两者区别在于,limit()返回一个包含前n个元素的新流,skip()则返回一个丢弃前n个元素后剩余元素组成的新流。
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};System.out.print("取数组前5个元素:");Arrays.stream(arr).limit(5).forEach(n -> System.out.print(n + " ")); // 输出结果为:1 2 3 4 5System.out.print("跳过前3个元素,取剩余数组元素:");Arrays.stream(arr).skip(3).forEach(n -> System.out.print(n + " ")); // 输出结果为:4 5 6 7 8 9 10
二、Stream终止操作
Stream的终止操作是指执行Stream流链中最后一个步骤,到这一步就会结束整个流处理。在Java8中,Stream终止操作包括forEach、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst和findAny等。这些终止操作都有返回值。需要注意一点是,如果没有执行终止操作的话,Stream流是不会触发执行的,例如,一个没有终止操作的peek()方法代码是不会执行进而打印——
list.stream().peek(t -> System.out.println("ddd"))
当加上终止操作话,例如加上collect,就会打印出“ddd”——
list.stream().peek(t -> System.out.println("ddd")).collect(Collectors.toList());
下面按类别分别讲解各个终止操作的使用。
2.1、forEach:遍历流中的每个元素。
该forEach前面已经提到,这里不做过多介绍。
2.2、count:统计流中元素的数量。
count可以统计流中元素的数量并返回结果。
假设有一个包含多个手机号字符串的列表,需要统计去重后的手机号数量,就可以使用count方法——
List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15138510000");long count = numbers.stream() .distinct()//去重 .count();//统计去重后的手机号System.out.println(count);打印结果:3
2.3、reduce:将流中的所有元素归约成一个结果。
reduce()可以将流中的所有元素根据指定规则归约成一个结果,并将该结果返回。
常用语法格式如下:
Optional result = stream.reduce(BinaryOperator accumulator);
可见,reduce方法会返回一个Optional类型的值,表示归约后的结果,需要通过get()方法获取Optional里的值。
假设有一个包含多个手机号字符串的List列表,需要在去重之后,再将列表所有字符串拼按照逗号间隔接成一个字符串返回,那么就可以通过reduce来实现——
List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15138510000");Optional result = numbers.stream() .distinct() //去重 .reduce((a ,b) -> a+","+b);//指定规则为,相临两个字符通过逗号“,”间隔System.out.println(result.get());打印结果:13378520000,15138510000,13178520000
2.4、collect:将流中的元素收集到一个容器中,并返回该容器。
collect的作用是将流中的元素收集到一个新的容器中,返回该容器。打个比喻,它就像一个采摘水果的工人,负责将水果一个个采摘下来,然后放进一个篮子里,最后将篮子交给你。我在前面的案例当中,基本都有用到collect,例如前面2.1的filter过滤用法中的List
在Java8的collect方法中,除里toList()之外,还提供了例如toSet,toMap等方法满足不同的场景,根据名字就可以知道,toSet()返回的是一个Set集合,toMap()返回的是一个Map集合。
2.5、min 和 max:找出流中的最小值和最大值。
min和max用来查找流中的最小值和最大值。
假设需要在查找出用户列表中年龄最小的用户,可以按照以下代码实现——
List peopleList = Arrays.asList( new People("王二",20), new People("李二",30), new People("张四",31));//查找年龄最小的用户,若没有则返回一个nullPeople people = peopleList.stream().min(Comparator.comparing(People::getAge)).orElse(null);System.out.println(people);打印结果:People{name="王二", age=20}
max的用法类似,这里不做额外说明。
2.6、anyMatch、allMatch 和 noneMatch:判断流中是否存在满足指定条件的元素。
2.6.1、anyMatch
anyMatch用于判断,如果流中至少有一个元素满足给定条件,那么返回true,反之返回false,即 true||false为true这类的判断。
假设在一个手机号字符串的List列表当中,判断是否包含前缀为“153”的手机号,就可以使用anyMatch——
List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15338510000");boolean hasNum = numbers.stream().anyMatch(n -> n.startsWith("153"));System.out.println(hasNum);打印结果:true
2.6.2、allMatch
allMatch用于判断,流中的所有元素是否都满足给定条件,满足返回true,反之false,即true&&false为false这类判断。
假设在一个手机号字符串的List列表当中,判断手机号是否都满足前缀为“153”的手机号,就可以用allMatch——
List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15338510000");boolean hasNum = numbers.stream().allMatch(n -> n.startsWith("153"));System.out.println(hasNum);打印结果:false
2.6.2、noneMatch
noneMatch用于判断,如果流中没有任何元素满足给定的条件,返回true,如果流中有任意一个条件满足给定条件,返回false,类似!true为false的判断。
假设在一个手机号字符串的List列表当中,判断手机号是否都不满足前缀为“153”的手机号,就可以用noneMatch——
List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "1238510000");//numbers里没有前缀为“153”的手机号boolean hasNum = numbers.stream().noneMatch(n -> n.startsWith("153"));System.out.println(hasNum);打印结果:true
这三个方法其实存在一定互相替代性,例如在3.6.1中,满足!anyMatch表示所有手机号都不为“153”前缀,才得到true,这不就是noneMatch,主要看在项目当中如何灵活应用。
2.7、findFirst 和 findAny:返回流中第一个或任意一个元素。
2.7.1、findFirst
findFirst用于返回流中第一个元素,如果流为空话,则返回一个空的Optional对象——
假设需要对一批同手机号的黑名单用户按照时间戳降序排序,然后取出第一个即时间戳为最早的用户,就可以使用findFirst——
List peopleList = Arrays.asList( new People("王二","13178520000","20210409"), new People("李二","13178520000","20230401"), new People("张四","13178520000","20220509"), new People("赵六","13178520000","20220109"));/** * 先按照时间升序排序,排序后的结果如下: * People{name="王二", tel="13178520000", time="20210409"} * People{name="赵六", tel="13178520000", time="20220109"} * People{name="张四", tel="13178520000", time="20220509"} * People{name="李二", tel="13178520000", time="20230401"} * *排序后,People{name="王二", tel="13178520000", time="20210409"}成了流中的第一个元素 */People people = peopleList.stream().sorted(Comparator.comparing(People::getTime)).findFirst().orElse(null);System.out.println(people);打印结果:People{name="王二", tel="13178520000", time="20210409"}
2.7.2、findAny
findAny返回流中的任意一个元素,如果流为空,则通过Optional对象返回一个null。
假设有一个已经存在的黑名单手机号列表blackList,现在有一批新的手机号列表phoneNumber,需要基于blackList列表过滤出phoneNumber存在的黑名单手机号,最后从过滤出来的黑名单手机号当中挑选出来出来任意一个,即可以通过findAny实现——
//blackList是已经存在的黑名单列表List blackList = Arrays.asList("13378520000", "15138510000");//新来的手机号列表List phoneNumber = Arrays.asList("13378520000", "13178520000", "1238510000","15138510000","13299920000");String blackPhone = phoneNumber.stream() //过滤出phoneNumber有包含在blackList的手机号,这类手机号即为黑名单手机号。 .filter(phone -> blackList.contains(phone)) //获取过滤确定为黑名单手机号的任意一个 .findAny() //如果没有则返回一个null .orElse(null);System.out.println(blackPhone);打印结果:13378520000
三、并行流
前面的案例主要都是以顺序流来讲解,接下来,就是讲解Stream的并行流。在大数据量处理场景下,使用并行流可以提高某些操作效率,但同样存在一些需要考虑的问题,并非所有情况下都可以使用。
3.1、什么是并行流:并行流的概念和原理。
并行流是指通过将数据按照一定的方式划分成多个片段分别在多个处理器上并行执行,这就意味着,可能处理完成的数据顺序与原先排序好的数据情况是不一致的。主要是用在比较大的数据量处理情况,若数据量太少,效率并不比顺序流要高,因为底层其实就使用到了多线程的技术。
并行流的流程原理如下:
1、输入数据:并行流的初始数据一般是集合或者数组,例如Arrays.asList("13378520000", "13178520000", "1238510000","15138510000","13299920000");
2、划分数据:将初始数据平均分成若干个子集,每个子集可以在不同的线程中独立进行处理,这个过程通常叫“分支”(Forking),默认情况下,Java8并行流使用到了ForkJoinPool框架,会将Arrays.asList("13378520000", "13178520000", "1238510000","15138510000","13299920000")划分成更小的颗粒进行处理,可能会将该数组划分成以下三个子集:
[13378520000, 13178520000] [1238510000, 13338510000] [13299920000]
3、处理数据:针对划分好的子集并行进行相同的操作,例如包括过滤(filter)、映射(map)、去重(distinct)等,这个过程通常叫“计算”(Computing),例如需要过滤为前缀包括“133”的字符集合,那么,各个子集,就会处理得到以下结果:
[13378520000] [13338510000] []
4、合并结果:将所有子集处理完成的结果进行汇总,得到最终结果。这个过程通常叫“合并”(Merging),结果就会合并如下:
[13378520000,13338510000]
5、返回结果:返回最终结果。
通俗而言,就是顺序流中,只有一个工人在摘水果,并行流中,是多个工人同时在摘水果。
3.2、创建并行流:通过 parallel() 方法将串行流转换为并行流。
可以通过parallel()方法将顺序流转换为并行流,操作很简单,只需要在顺序流上调用parallel()即可。
List numbers = Arrays.asList("13378360000","13278240000","13178590000","13558120000");//通过stream().parallel()方法创建一个并行流,使用map()方法将每个字符串转换为截取前7位的字符,最后使用collect()方法将结果收集到一个新列表中List filNums = numbers.stream().parallel().map(s -> s.substring(0,7)).collect(Collectors.toList());System.out.println(filNums);打印结果:[1337836, 1327824, 1317859, 1355812]
3.3、并行流的注意事项:并行流可能引发的线程安全,以及如何避免这些问题。
在使用并发流的过程中,可能会引发以下线程安全问题:并行流中的每个子集都在不同线程运行,可能会导致对共享状态的竞争和冲突。
避免线程问题的方法如下:避免修改共享状态,即在处理集合过程当中,避免被其他线程修改集合数据,可以使用锁来保证线程安全。
使用无状态操作:在并行流处理过程尽量使用无状态操作,例如filter、map之类的,可以尽量避免线程安全和同步问题。
四、Optional
4.1、什么是 Optional:Optional 类型的作用和使用场景。
在实际开发当中,Optional类型通常用于返回可能为空的方法、避免null值的传递和简化复杂的判断逻辑等场景。调用Optional对象的方法,需要通过isPresent()方法判断值是否存在,如果存在则可以通过get()方法获取其值,如果不存在则可以通过orElse()方法提供默认值,或者抛出自定义异常处理。
4.2、如何使用 Optional:如何使用 Optional 类型。
使用Optional类型主要目的是在数据可能为空的情况下,提供一种更安全、更优雅的处理方式。
以下是使用Optional类型的常用方法:
4.2.1、ofNullable()和isPresent()方法
将一个可能为null的对象包装成Optional类型的对象,然后根据isPresent方法判断对象是否包含空值——
String str = null;Optional optStr = Optional.ofNullable(str);if (optStr.isPresent()){ System.out.println("Optional对象不为空");}else { System.out.println("Optional对象为空");}打印结果:Optional对象为空
4.2.2、get()方法
获取Optional对象中的值,如果对象为空则抛出NoSuchElementException异常——
String str = null;Optional optStr = Optional.ofNullable(str);if (optStr.isPresent()){ System.out.println("Optional对象不为空");}else { System.out.println("Optional对象为空"); optStr.get();}控制台打印结果:Exception in thread "main" java.util.NoSuchElementException: No value presentat java.util.Optional.get(Optional.java:135)at com.zhu.fte.biz.test.StreamTest.main(StreamTest.java:144)Optional对象为空
4.2.4、orElse()方法
获取Optional对象中的值,如果对象为空则返回指定的默认值——
String str = null;Optional optStr = Optional.ofNullable(str);if (optStr.isPresent()){ System.out.println("Optional对象不为空");}else { System.out.println("Optional对象为空,返回默认值:" + optStr.orElse("null"));}打印结果:Optional对象为空,返回默认值:null
当然,如果不为空的话,则能正常获取对象中的值——
String str = "测试";Optional optStr = Optional.ofNullable(str);if (optStr.isPresent()){ System.out.println("Optional对象不为空,返回值:" + optStr.orElse("null"));}else { System.out.println("Optional对象为空,返回默认值:" + optStr.orElse("null"));}打印结果:Optional对象不为空,返回值:测试
那么,问题来了,它是否能判断“ ”这类空格的字符串呢,我实验了一下,
String str = " ";Optional optStr = Optional.ofNullable(str);if (optStr.isPresent()){ System.out.println("Optional对象不为空,返回值:" + optStr.orElse("null"));}else { System.out.println("Optional对象为空,返回默认值:" + optStr.orElse("null"));}打印结果:Optional对象不为空,返回值:
可见,这类空字符串,在orElse判断当中,跟StringUtils.isEmpty()类似,都是把它当成非空字符串,但是StringUtils.isBlank()则判断为空字符串。
4.2.5、orElseGet()方法
orElseGet()和orElse()类似,都可以提供一个默认值。两者区别在于,orElse方法在每次调用时都会创建默认值,而orElseGet只在需要时才会创建默认值。
4.3、Optional 和 null 的区别: Optional 类型与 null 值的异同。
两者都可以表示缺失值的情况,两者主要区别为:Optional类型是一种包装器对象,可以将一个可能为空的对象包装成一个Optional对象。这个对象可以通过调用ofNullable()
、of()
或其他方法来创建。而null值则只是一个空引用,没有任何实际的值。
Optional类型还可以避免出现NullPointerException异常,具体代码案例如下:
String str = null;//错误示范:直接调用str.length()方法会触发NullPointerException//int length = str.length()//通过Optional类型避免NullPointerExceptionOptional optionalStr = Optional.ofNullable(str);if (optionalStr.isPresent()){//判断Optional对象是否都包含非空值 int length = optionalStr.get().length(); System.out.println("字符串长度为:" + length);}else { System.out.println("字符串为空!");}//使用map()方法对Optional对象进行转换时,确保返回对结果不为nullOptional optionalLength = optionalStr.map(s -> s.length());System.out.println("字符串长度为:" + optionalLength.orElse(-1)); // 使用orElse()方法提供默认值
五、扩展流处理
除里以上常用的流处理之外,Java8还新增了一些专门用来处理基本类型的流,例如IntStream、LongStream、DoubleStream等,其对应的Api接口基本与前面案例相似,读者可以自行研究。
最后,需要注意一点是,在流处理过程当中,尽量使用原始类型数据,避免装箱操作,因为装箱过程会有性能开销、内存占用等问题,例如,当原始数据int类型被装箱成Integer包装类型时,这个过程会涉及到对象的创建、初始化、垃圾回收等过程,需要额外的性能开销。
以上,就是关于Java8流处理相关知识的总结,笔者水平有限,若存在有误的地方,还需帮忙指正。
最后,码字不易,欢迎关注、点赞、收藏,谢谢!
关键词:
今日热文:Spring5课堂笔记
Java8 Stream流式编程
【新要闻】128核心Arm处理器能跑Windows!还能配NVIDIA显卡
时讯:新疆和硕:田间地头 话两会
maptalks点线面图形样式设置经验总结
【天天速看料】马龙惊魂3-2拒绝一轮游,与小林再迎老少对决!国乒首日狂走钢丝
在米其林餐厅找罪受?人均8千元餐厅疑发生食物中毒
世界热点!张艺谋宣布将拍网剧《英雄联盟》!第一次 想拍个不一样的
环球新动态:Vulnhub Mercy靶场 Walkthrough
FSR、DLSS谁更好?26款游戏PK:AMD居然0胜!
环球速看:AI 史话
每日讯息!Weex原理及架构剖析
旅游专列来啦具体详细内容是什么
中国新能源汽车在欧洲火了:每10辆就有1辆来自中国
【时快讯】WebKit三件套(3):WebKit之Port篇
每日速看!HBase在进行模型设计时重点在什么地方?一张表中定义多少个Column Family最合适?为什么?
环球资讯:第136篇:Three.js基础入门动画API:setInterval 与 requestAnimationFrame的区别
程序员午休时健身猝死 被认定工伤 法院:属于在岗状态
比亚迪车主用木头手工还原汉EV:前后双电机 真能跑
每日速讯:WebKit三件套(1):WebKit之WebCore篇
焦点关注:C盘爆满的解决方法,不用删除文件,使用分区助手无损增加内存
简讯:招标活动法律保障工作实务指南 2016年版
锐龙7 7800X3D配A620主板 性能损失多少?实在没想到
天天微头条丨成都升温 千足虫满街爬:专家科普小心毒臭液
全球最大盗版电子书网站Z-Library被封后复活:路子更野了!要共享纸质书
速读:Steam排名第一!GTX 1650亮机卡火爆断货:RTX 2070紧急变身救场
【天天速看料】Vue——patch.ts【十四】
浏览器史话中chrome霸主地位的奠定与国产浏览器的割据混战
Vue开发规范
天天速递!只剩Windows版了!B站正式宣布UWP版停止维护
环球时讯:洛阳上演汉服秀 网友偶遇唐僧师徒四人坐地铁去“取经”
男子体内惊现小树苗!都长到5厘米高了
环球今亮点!或为AR眼镜新品!小米神秘“新礼物”明早10点正式亮相
今热点:解放军环台岛战备警巡和演习,专家:或成惩戒“台独”新模式
天天通讯!Linux常用操作命令总结
iOS Modern Collection View
线段树好题! P2824 [HEOI2016/TJOI2016]排序 题解
每日动态!小米寄修服务“史诗级”提升:新增进度可视化功能
天天资讯:第7章_InnoDB数据存储结构
天天快报!css过去及未来展望—分析css演进及排版布局的考量
每日关注!微信小程序 开发,uni-app 开发简介
今日报丨朗新科技:拟提前赎回“朗新转债”
今日观点!男子网购耐克鞋却挂着李宁的吊牌 网友:联名款?
数组的算法
超同年漫威大片近3倍!新海诚《铃芽之旅》国内票房达7亿
VUE-Antd开发,validate规则校验第一次为空,后面无论怎么输入都显示不可为空
环球新消息丨上海国创科技产业创新发展中心理事长黄岩:人工智能会像手机一样进入我们的日常生活
每日聚焦:青春回来了!电影《灌篮高手》流川枫角色预告出炉:4月20日国内上映
世界今亮点!暴雪战网遭遇DOSS攻击!多款游戏无法正常登录
世界聚焦:2023年3月随笔暨第1季度总结
世界热资讯!ASP.NET Core MVC 从入门到精通之接化发(一)
【全球聚看点】快速幂算法
私有化部署chatGPT,告别网络困扰
新车一超过60km/h就耳朵疼 车主:找了20多个人开都如此
Win11新版最快5月推送:微软确认Moment 3更新
【全球热闻】富二代开保时捷碰瓷:专挑开豪车酒驾的敲诈 “赚了”30多万
聚焦:五粮液董事长曾从钦:在“时与势”中勇担使命,在“稳与进”中拓展空间,在“个与众”中升级维度
今年1号台风“珊瑚”下周或生成 预计2023年登陆我国的台风至少6个
夏日必备!楠木之舟EVA拖鞋19.9元:轻盈高回弹
男士洗脸专属大牌 妮维雅控油、祛痘洗面奶大促:不到20
世界最新:ChatGPT火爆 元宇宙房产崩盘!林俊杰买虚拟地产浮亏91%
岚图汽车1V4 “开火”保时捷、蔚来、奥迪、极氪!结果尴尬:无一车企回应
环球实时:4月09日10时江苏南通疫情最新消息 4月09日10时江苏南通今日确诊人数
让民生福祉更有“数”——中国电子政务论坛聚焦数字政府建设
【天天播资讯】为啥国产电动车在欧洲不好卖?海外汽车大V给出5点原因
环球热讯:今天国际护胃日 年轻人为啥会被胃癌盯上?胃病转向胃癌有5个信号
全球今头条!通用自动驾驶汽车撞上公交车 300辆无人出租车被召回
沙尘暴蓝色预警来了:北方将迎沙尘天气!官方分享防御指南
“码农”劳动争议频发 厘清责任依法“护体”
焦点快播:存碰撞风险!特斯拉美国召回422辆Model 3:前悬架连杆有隐患
新资讯:朱晓彤 总算升职了!将在特斯拉墨西哥工厂获得一张新床
环球资讯:有人用ChatGPT月入十万了!70+款免费AI工具大搜罗
每日热门:sip消息拆包原理及组包流程
每日聚焦:超2250万台(套)各型装备投入春耕—— 农机化支撑粮食稳产增产
微头条丨小米14曝光:屏幕边框窄到没朋友 华星供货
贝克汉姆回复王濛大胆表白 感谢你的赞美:后者曾公开表示因为帅喜欢他
【天天时快讯】选台式机还是游戏本?13代酷睿旗舰处理器性能PK
环球报道:传音Tecno Camon 20 Premier曝光:天玑1200、五边形镜组
天天看点:广东白云技师学院短训班专业设置_广东白云技师学院
今日热讯:Vulnhub Bravery靶机 Walkthrough
焦点观察:69岁成龙说我还能打还能跳:是个奇迹
1999元神机诞生!Redmi Note 12 Turbo成了:用户好评率接近100%
天天快消息!Golang常用库之UUID
如何成为一名优秀的工程师?顶级程序员的5点建议
全球报道:曾称失控都是踩错单踏板!网友晒特斯拉新OTA 想要的制动恢复两档可选
全球播报:女子乘高铁把脚放前排乘客头上:狡辩称”鞋底不脏“
天天快资讯丨碱性食物有哪些_十大碱性食物排名
.Net Core Console&Generic HostBuilder
用阳光代替WiFi信号连网 沙特科学家这成果亮了
世界热点!【算法数据结构专题】「延时队列算法」史上手把手教你针对层级时间轮(TimingWheel)实现延时队列的开发实战落地(下)
世界速讯:电影《龙马精神》上映!刘德华清唱生日快乐歌祝福成龙
全球微动态丨连休5天!五一劳动节期间高速公路免费通行
口感自然 富含矿物质:依能天然苏打水2.3元(京东4元)
每日热议!博主带秤在淄博连逛10家店无缺斤少两:他很佩服
天天速递!投资者为何对苹果AR/VR设备没有信心?原因揭开
常州市天宁区人民检察院对一起拟不起诉案件公开听证
java -- 异常处理、Collection、Iterator迭代器、泛型
CentOS7-实现全网备份脚本
【天天报资讯】装机成本降了! 微星A620M-E主板到手799元:锐龙7 7800X3D绝配
关注:国宝熊猫回家记:丫丫要回来了