Java函数式编程之【Stream终止操作】【下】【三】【收集操作collect()与分组分区】【下游收集器】

分组收集器groupingBy():groupingBy()收集器用于按条件对元素象进行分组,并将结果存储在Map实例中。其作用与数据库的SQL语句的group by的用法有异曲同工之妙。
分区收集器partitioningBy():partitioningBy()可以看作是分组groupingBy()的特殊情形,实质是在做二分组。它根据断言(Predicate,谓词)将Stream中的元素收集到一个Map实例中;该Map将断言测试结果作为key(键),其key(键)是true/false,可以将流中的元素分为true和false两部分,而value(值)是由满足/不满足断言的元素构成的列表。
Java 核心库中Collectors类中给出的分组和分区操作的样例代码:

/***     // Group employees by department*     Map<Department, List<Employee>> byDept*         = employees.stream()*                    .collect(Collectors.groupingBy(Employee::getDepartment));**     // Compute sum of salaries by department*     Map<Department, Integer> totalByDept*         = employees.stream()*                    .collect(Collectors.groupingBy(Employee::getDepartment,*                                                   Collectors.summingInt(Employee::getSalary)));**     // Partition students into passing and failing*     Map<Boolean, List<Student>> passingFailing =*         students.stream()*                 .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));** }</pre>** @since 1.8*/

首先,我们定义一个后文要用到的Employee类:

package test;
import java.util.function.Predicate;
public class Employee {private String name;private String gender; //性别private int age; private int salary; //月工资private String department;private String subCompany;public Employee(String name,String gender,int age) {this.name = name;this.gender = gender;this.age = age;}public Employee(String name,String gender, int age, int salary,String subComp,String dept) {this(name, gender, age);this.salary = salary;subCompany = subComp;department = dept;}public Integer getAge() {return age;}public String getGender() {return (gender.equalsIgnoreCase("M")) ? "男":"女";}public String getName() {return name;}public int getSalary() {return salary;}public String getDepartment() {return department;}public String getSubCompany() {return subCompany;}@Overridepublic String toString() {String sex = getGender();return "{姓名:"+name+" "+sex+" "+age+" }";}//对于常用的谓词逻辑(断言),可在主语实体中定义。如本例Employee中定义以下断言:public static Predicate<Employee> 老年人 = x -> x.getAge() > 60;public static Predicate<Employee> 男性 = p -> "男".equals(p.getGender());public static Predicate<Employee> 成年人 = e -> e.getAge() > 18; 
}	//Employee定义结束。

收集操作collect中分组收集器(groupingBy)的用法
一、分组收集器groupingBy()
用于按条件对元素对象进行分组,并将结果存储在Map实例中。其作用与数据库的SQL语句的group by的用法有异曲同工之妙。
分组收集器groupingBy()会返回一个Map,它有两个关键要素,即分组器函数和值收集器:

  • 分组器函数:classifier函数,对流中的元素进行处理,返回一个用于分组键值key,根据key将元素分配到组里。
  • 值收集器:是对于分组后的数据元素的进一步处理转换逻辑容器,此容器是一种Collector收集器,和collect()方法中传入的收集器参数完全等同,实际上就是一个下游收集器,像俄罗斯套娃一样可循环嵌套。

对于分组收集器groupingBy而言,分组器函数与值收集器二者缺一不可。
分组收集器groupingBy共有三种重载形式:

  • public static <T, K> Collector<T, ?, Map<K, List>> groupingBy(Function<? super T, ? extends K> classifier)
    这是单参数的重载形式,有一个参数classifier 是分类器函数。将分类器函数应用于Stream中的数据元素产生键key,根据键key把元素或映射后作为值value放入对应的值收集器。此重载相当于groupingBy(classifier,toList())。请看示例,把公司雇员按部门进行分组,其中Employee::getDepartment是分类器函数:
	 Map<Department, List<Employee>> byDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));
  • public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
    这是两个参数的重载形式,除了参数classifier分类器函数,还有一个下游收集器downstream参数。此分组收集器会返回一个映射表map,将分类器函数应用于Stream中的数据元素产生键key,而值value是由下游收集器收集。请看示例,按部门分组计算工资汇总:
    	Map<Department,Integer> totalByDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment,Collectors.summingInt(Employee::getSalary)));

下面这个示例:按城市个子最高的人:

	Comparator<Person> byHeight = Comparator.comparing(Person::getHeight);Map<City, Person> tallestByCity = people.stream().collect(groupingBy(Person::getCity, reducing(BinaryOperator.maxBy(byHeight))));
  • public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,Supplier mapFactory,Collector<? super T, A, D> downstream)
    这是叁个参数的重载形式,除了classifier分类器和downstream下游收集器参数外,又新增了一个map供给器参数mapFactory。此分组收集器会返回一个映射表map,将分类器函数应用于Stream中的数据元素产生键key,而与key对应的值value则由下游收集器收集。请看示例,按部门把公司员工分组,收集器定制使用LinkedHashMap,下游收集器使用ArrayList。
   	Map<Department, List<Employee>> byDept = employees.stream().collect(groupingBy(Employee::getDepartment),LinkedHashMap::new,Arraylist::new);

参数说明:
参数classifier,是Function接口的实例,是分类器。
参数mapFactory,是Supplier接口的实例,是供给器(生产map容器)
参数downstream,是Collector接口的实例,是下游收集器

收集器groupingBy()都是在数据收集前分组,然后再将分好组的数据传递给下游的收集器。两个参数和叁个参数的版本都可适用于Collector嵌套。

收集器groupingBy()有个兄弟groupingByConcurrent(),用法相似。两者区别也仅仅是单线程和多线程的使用场景,groupingByConcurrent()是并发的分组收集器,是线程安全的,可用于多线程场景。

【例程10-31】相同字母异序词测试程序AnagramTest的collect分组收集版本
下面我们先来研究一个相同字母异序词测试程序。
程序说明:程序中用到下面的方法alphabetize()

	private static String alphabetize( String str ) { //按字母顺序重组字符串char[] array = str.toCharArray();  //把输入字符串分解为字母数组Arrays.sort(array);  //按字母顺序排序return new String(array); //返回按字母顺序重组的字符串}

这个方法先把输入的字符串分解为字母数组,然后再返回按字母顺序重组的字符串。它的作用相当于给“相同字母异序词”生成一个key。例如,异序词"ear"、“are”、“era"经此方法处理后生成的key为"aer”。
例程中定义了一个映射:

	Map<String, TreeSet<String>> wsMap = new HashMap<>();

映射wsMap的键key类型是String,值value是树集(TreeSet)。
例程使用了Map方法computeIfAbsent(K key, Function remappingFunction),该方法有两个参数:第一个参数是HashMap的key;第二个参数又称之为重映射函数,用于重新计算value值,本例中这个value是一个TreeSet。
方法computeIfAbsent的作用是:如果HashMap中不存在指定的key键,由重新映射函数计算一个新的value值(创建一个新的TreeSet),然后插入键值对(key,value)至HashMap中,同时返回value值。如果HashMap中已存在key值,该方法只需返回查询到的value值。当HashMap中已存在key值时,该方法的作用相当于get(Object key),实际上是返回一个TreeSet的句柄。
【例程】AnagramTest.java开始:

import java.util.*;
import static java.util.stream.Collectors.*; /**增加程序可读性**/
/**** @author QiuGen* @description  异序词例程AnagramTest的collect分组收集版本* @date 2024/8/26* ***/
public class AnagramTest { /**相同字母异序词测试**/static final List<String>  WdList = Arrays.asList("ear","are","triangle","integral","three","htree","staple","petals","there","era");private static String alphabetize( String str ) { //按字母顺序重组字符串char[] array = str.toCharArray();  //把输入字符串分解为字母数组Arrays.sort(array);  //按字母顺序排序return new String(array); //返回按字母顺序重组的字符串}public static void TestByMap() {  /**面向对象编程,使用Map集合的外部迭代**/Map<String, TreeSet<String>> wsMap = new HashMap<>();/**使用集合forEach的写法* k->new TreeSet<>()*不能用*TreeSet::new*奇怪*/WdList .forEach(w->{  wsMap.computeIfAbsent(alphabetize(w), k->new TreeSet<>()).add(w);});wsMap.forEach( (k,set)-> System.out.println(set) );//Map的forEach有二个参数}public static void TestByStream() { /**函数式编程,演示三种集合收集器**/WdList .stream().collect( groupingBy(word->alphabetize(word)) ) //默认使用List.values().stream().forEach(System.out::println);System.out.println("**************");//使用默认的Set下游收集器WdList.stream().collect( groupingBy(word->alphabetize(word), toSet()) ).values().stream().forEach(System.out::println);System.out.println("**************");//使用定制的TreeSet收集器WdList.stream().collect( groupingBy(word->alphabetize(word), toCollection(TreeSet::new)) ).values().stream().forEach(System.out::println);}public static void main(String[] args) { //用二种写法的比较测试TestByStream();  /**函数式编程,演示三种集合收集器**/System.out.println("---------------");TestByMap();}
}

二、分区收集器partitioningBy()

分区收集器partitioningBy()可以看作是分组groupingBy()的特殊情形,其实质根据断言(Predicate)结果分成二组。当分组收集器groupingBy()的分类器classifier返回值为布尔值时,则效果等同于一个分区收集器。

在这里插入图片描述

收集器partitioningBy()根据断言将Stream中的元素收集到一个Map中;该Map把断言测试结果作为key(键),根据key是true/false,可以将流中的元素分为true和false两部分,而value(值)通常是由满足/不满足断言的元素构成的列表。partitioningBy()收集器有两种重载形式:

  • public static Collector<T,?,Map<Boolean,List<>>> partitioningBy(Predicate<? super T>predicate)
    单个参数重载形式,其参数是一个断言predicate。实质上它是双参数重载形式的特殊形式,相当于双参数的:
	partitioningBy(predicate, toMap())

下面的示例:按考试成绩是否及格分区(类),收集到列表中

	Map<Boolean, List<Student>> passingFailing = students.stream().collect(Collectors.partitioningBy (s -> s.getGrade() >= PASSED));
  • public static <T, D, A>
    Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
    Collector<? super T, A, D> downstream)
    双参数的重载形式,新增的第二个参数downstream是下游收集器。下面的示例:按考试成绩是否及格分区,收集到集Set里,指定的收集器是Set。
	Map<Boolean, Set<Student>> map = students.stream().collect(partitioningBy(s -> s.getGrade() >= PASSED, toSet()));

三、映射属性收集器mapping()的用法

Collectors.mapping()也是与下游收集器相关的映射收集器,它允许你在收集过程中对流中的每个元素应用一个函数,并将其结果收集起来。使用mapping()可自定义要收集的元素类型,这是一个很有用的功能。

  • public static <T, U, A, R>
    Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
    Collector<? super U, A, R> downstream)

下面的示例:把Person映射转换为字符串类型name后再收集到列表中:

	List<String> nameList = personList.stream().collect(mapping(Person::getName, toList()));

亦可先map()映射再收集,实现相同的功能:

	List<String> nameList = personList.stream().map(Person::getName).collect(toList());

四、下游收集器(Collector)
下游收集器(Downstream Collector)是Java Stream API中的一个重要概念,它允许在分组(grouping)或分区(partitioning)操作后进行更复杂的收集操作。

下游收集器通常出现在以下收集器方法中:

  • Collectors.groupingBy() 的第二个参数
  • Collectors.partitioningBy() 的第二个参数
  • Collectors.mapping() 的第二个参数

下游收集器用于在主要收集操作完成后,对每个组或分区中的元素执行的进一步收集操作。
下游收集器除了上文介绍的分组收集器groupingBy()、分区收集器partitioningBy()和映射属性收集器Collectors.mapping()外,其他一些常见的下游收集器如下所示:

  1. 归约收集器Collectors.reducing()
  2. 计数Collectors.counting()
  3. 求和Collectors.summingInt()
  4. 求平均值Collectors.averagingInt()
  5. 最大值/最小值Collectors.maxBy()
  6. 连接字符串Collectors.joining()

请看示例:

//reducing() 特别适合作为分组后的下游收集器
// 按城市分组,计算每个城市的最高工资
Map<String, Integer> maxSalaryByCity = employees.stream().collect(Collectors.groupingBy(Employee::getCity,Collectors.reducing(0,Employee::getSalary,Integer::max)));Map<String, Long> countByGroup = list.stream().collect(Collectors.groupingBy(Item::getCategory, Collectors.counting()));Map<String, Integer> sumByGroup = list.stream().collect(Collectors.groupingBy(Item::getCategory, Collectors.summingInt(Item::getPrice)));Map<String, Double> avgByGroup = list.stream().collect(Collectors.groupingBy(Item::getCategory, Collectors.averagingInt(Item::getPrice)));Map<String, Optional<Item>> maxByGroup = list.stream().collect(Collectors.groupingBy(Item::getCategory, Collectors.maxBy(Comparator.comparing(Item::getPrice))));Map<String, Set<String>> namesByGroup = list.stream().collect(Collectors.groupingBy(Item::getCategory, Collectors.mapping(Item::getName, Collectors.toSet())));Map<String, String> joinedNames = list.stream().collect(Collectors.groupingBy(Item::getCategory, Collectors.mapping(Item::getName, Collectors.joining(", "))));

下游收集器大大增强了Java Stream API的数据处理能力,使得复杂的数据聚合操作变得简洁而高效。

五、收集器(Collector)的嵌套

有的时候,我们需要先根据某个维度进行分组,然后再根据第二维度进一步的分组,然后再对分组后的结果进一步的进行处理。这种应用场景,我们可以通过分组分区和映射收集器,再组合其他下游收集器(Collector)的叠加嵌套来实现。这种嵌套像可以俄罗斯套娃一样层层嵌套。Employee类定义请参见本节开头。
【例程10-32】Collector使用综合例程“Collect分组分区Demo”

package test;
import static java.util.stream.Collectors.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;
public class Collect分组分区Demo {public static List<Employee> empList() {List<Employee> list = new ArrayList<>();list.add(new Employee("刘敏","f", 28, 2000,"宁波分公司","销售部"));list.add(new Employee("李伟","M", 44, 4060,"上海分公司","营销部"));list.add(new Employee("丁丽","F", 55, 5050,"上海分公司","工程部"));list.add(new Employee("赵云","m" , 66, 6080,"宁波分公司","营销部"));list.add(new Employee("张三","M", 33, 3300,"上海分公司","工程部"));list.add(new Employee("钱玄同","m", 23, 2080,"上海分公司","销售部"));return list;}public static void collectGroupingBy() { // 对“上海分公司”的员工,按部门分组Map<String, List<Employee>> resultMap = empList().stream().filter(e -> "上海分公司".equals(e.getSubCompany())).collect(groupingBy(Employee::getDepartment));}public static void collectPartitioningBy() { // 分区示例Map<Boolean,List<Employee>> map = empList().stream().filter(e -> "宁波分公司".equals(e.getSubCompany())).collect(partitioningBy(e->e.getAge() > 40));System.out.println("----按工资>=4000 分区:打印分区结果----");Map<Boolean, Set<Employee>> setMap = empList().stream().collect(partitioningBy(e -> e.getSalary() >= 4000, toSet()));setMap.forEach((k,v) -> System.out.println("键:" + k + ", 值:" + v));}public static void collectReducing() {	/***reducing示例***//***统计每个分公司年龄最大的员工***/Comparator<Employee> cAge = Comparator.comparing(Employee::getAge);Optional<Employee> employeeOpt = empList().stream().filter(e -> "上海分公司".equals(e.getSubCompany())).collect(reducing(BinaryOperator.maxBy(cAge)));//寻找上海分公司中年龄最大的员工:对收集器结果进行转换整理Employee employee = empList().stream().filter(e -> "上海分公司".equals(e.getSubCompany())).collect(collectingAndThen(reducing(BinaryOperator.maxBy(cAge)),Optional::get));System.out.println(employee);}public static void collectMaping() {  /***mapping示例***///例如,获取Employee姓名列表:System.out.println("---获取Employee姓名列表:---");List<String> namelist = empList().stream().collect(mapping(Employee::getName, toList()));namelist.forEach(System.out::println);   System.out.println("--------------------");/***下面使用map()映射后再collect(),实现相同功能,更简明。***/namelist = empList().stream().map(Employee::getName).collect(toList());namelist.forEach(System.out::println); }public static void collectingAndThenTest() {		/***collectingAndThen示例***/// 先按工资再按年龄升序排序List<String> nameList = empList().stream().sorted(Comparator.comparing(Employee::getSalary).thenComparing(Employee::getAge)).map(Employee::getName).collect(Collectors.toList());Employee employee = empList().stream().filter(emp -> "上海分公司".equals(emp.getSubCompany())).collect(collectingAndThen(maxBy(Comparator.comparing(Employee::getSalary)), Optional::get));// 将员工先按分公司分组,再对分公司员工排序后放入列表Map<String, List<Employee>> map = empList().stream().collect(groupingBy(Employee::getSubCompany, collectingAndThen(toCollection(() -> new TreeSet<>(Comparator.comparing(Employee::getSalary))),ArrayList::new)));System.out.println("打印显示map的内容");for (String dept : map.keySet()) {map.get(dept).stream().forEach(e->System.out.println(e.getName()));}// 将员工先排序,再按分公司分组,效率低Map<String, List<Employee>> map3 = empList().stream().sorted(Comparator.comparing(Employee::getSalary))	.collect(groupingBy(Employee::getSubCompany));System.out.println("打印显示map3的内容");for (String dept : map3.keySet()) {map3.get(dept).stream().forEach(e->System.out.println(e.getName()));}}public static void collect嵌套() {/***将员工先按分公司分组,再把Employee列表映射为姓名列表。***/Map<String, List<String>> mapN = empList().stream().collect(groupingBy(Employee::getSubCompany, mapping(Employee::getName,toList())));/***将员工先按分公司分组,再按部门分组。***/Map<String, Map<String, List<Employee>>> map = empList().stream().collect(groupingBy(Employee::getSubCompany, groupingBy(Employee::getDepartment)));/***按分公司汇总工资***/Map<String, Integer> sumSalary = empList().stream().collect(groupingBy(Employee::getSubCompany,summingInt(Employee::getSalary)));/***按照分公司+部门两个维度,统计各个部门人数。叁层嵌套***/Map<String, Map<String, Long>> rtnMap = empList().stream().collect(groupingBy(Employee::getSubCompany,groupingBy(Employee::getDepartment,counting())));System.out.println(rtnMap);/***将员工先按分公司分组,再按性别分组。***/Map<String, Map<Boolean, List<Employee>>> map2 = empList().stream().collect(groupingBy(Employee::getSubCompany, partitioningBy(Employee.男性)));/***将员工按性别分组统计***/Map<Boolean, Long> rstMap = empList().stream().collect(partitioningBy(Employee.男性, counting())); rstMap.forEach((k,v) -> System.out.println("键:" + k + ", 值:" + v));/***将员工先按分公司分组,再求各分公司工资最高的员工***/Map<String, Employee> map3 = empList().stream().collect(groupingBy(Employee::getSubCompany, collectingAndThen(maxBy(Comparator.comparing(Employee::getSalary)), Optional::get)));/***统计每个分公司年龄最大的员工***/Comparator<Employee> cAge = Comparator.comparing(Employee::getAge);Map<String, Optional<Employee>> oldestSubCompany = empList().stream().collect(groupingBy(Employee::getSubCompany, reducing(BinaryOperator.maxBy(cAge))));/***先按分公司分组,再寻找年龄最大的员工***/Map<String, Employee > mapOldest = empList().stream().collect(groupingBy(Employee::getSubCompany,collectingAndThen(reducing(BinaryOperator.maxBy(cAge)),Optional::get)));}public static void main(String[] args) {collectingAndThenTest();//collectPartitioningBy();collect嵌套();	}
}	

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/91485.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/91485.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python设计模式-工厂模式

工厂模式的核心思想&#xff1a;封装对象创建过程、解耦对象使用与创建 。示例代码&#xff1a;from enum import Enum# 基类&#xff1a;人类 class Person:species Homo sapiensdef __init__(self, name):self.name namedef __str__(self):return f"{self.__class__._…

Rust:anyhow::Result 与其他 Result 类型转换

当函数返回的不是 anyhow::Result 而是其他 Result 类型时&#xff08;如 std::io::Result、serde_json::Result 或自定义 Result&#xff09;&#xff0c;可通过以下方法统一处理错误类型&#xff0c;确保与 anyhow 兼容或实现错误传播&#xff1a;&#x1f6e0;️ 一、错误类…

PLC-梯形图编程

1.位运算,比较 如&#xff1a;>,<,, 2.定时器 生成脉冲TP&#xff0c;常开触点闭合触发&#xff0c;赋值10秒时长&#xff0c;PT配置参数&#xff0c;ET运行时已PT计时 接通延时TON&#xff0c;常开触点闭合触发&#xff0c;延时10秒后赋值 关断延时TOF&#xff0c;常开触…

LLM学习笔记5——InstructGPT

系列文章目录 参考文献 参考文献 参考文献 参考视频 文章目录系列文章目录前言目前大模型不同的技术流派与框架路线&#xff1a;1. ​​BERT&#xff1a;Encoder-only架构​​​​1&#xff09; 架构特点​​​​2&#xff09; 训练目标​​3&#xff09; ​​​​应用场景2. …

热能小车cad【12张】三维图+设计说明书

摘要 无碳小车来自全国大学生工程能力训练大赛题目&#xff0c;根据“节能减排&#xff0c;绿色出行”的环保理念&#xff0c;提出了一种基于热力驱动的具有方向自动控制的无碳小车。 本文设计的无碳小车主要是将热能转化成机械能&#xff0c;用来驱动小车前进的装置&#xff0…

云原生 DevOps 实战之Jenkins+Gitee+Harbor+Kubernetes 构建自动化部署体系

技术背景​ 在云原生生态中&#xff0c;工具链的选择直接决定 CI/CD 流水线的效率与稳定性。本次方案的工具组合并非偶然&#xff0c;而是基于各组件的核心优势与生态适配性&#xff1a;​ 代码管理层&#xff1a;Gitee 作为国内主流的代码托管平台&#xff0c;支持 Git 分布…

二建机电工程专业都考哪些知识点?

二建机电工程专业需要考《建设工程施工管理》《建设工程法规及相关知识》和《机电工程管理与实务》三个科目。其中《机电工程管理与实务》是专业科目&#xff0c;也是考试重点&#xff0c;主要考查机电工程技术、机电工程相关法规与标准、机电工程项目管理实务等内容。具体如下…

React + ts + react-webcam + CamSplitter 实现虚拟摄像头解决win摄像头独占的问题

一、安装 CamSplitter 这块网上有很多教程了&#xff0c;这里不再赘述&#xff0c;就一点&#xff0c;需要分几个虚拟摄像头&#xff0c;就要在CamSplitter 的安装目录下 driver_install.cmd 执行几次。二、React ts react-webcam 调用虚拟摄像头import { useState, useEffec…

【深度学习①】 | Numpy数组篇

0 序言 本文为NumPy数组库的系统学习笔记&#xff0c;将自己先前的笔记做一个总结归纳。内容涵盖数组基础、创建、索引、变形、运算、函数、布尔型数组及与张量的衔接等内容。通过具体示例解析核心概念与操作&#xff0c;帮助读者掌握NumPy的使用逻辑与方法&#xff0c;为后续深…

5.实现 call

call 是 JavaScript 中非常核心的函数方法之一。它能改变函数的执行上下文&#xff08;也就是 this 的指向&#xff09;&#xff0c;在日常开发和面试中都极其常见。本文将带你一步步实现一个 Function.prototype.call 的自定义版本&#xff0c;真正理解它的底层原理。✨ 一、c…

Go语言中的盲点:竞态检测和互斥锁的错觉

&#x1f9e0; Go语言中的盲点&#xff1a;竞态检测和互斥锁的错觉 使用 -race 就能发现所有并发问题&#xff1f;加了 mutex 就万无一失&#xff1f; 这篇文章揭示了 Go 并发编程中的一个“危险盲区” —— 互斥锁并不能总能保护你免受数据竞争的影响&#xff0c;尤其是在 -ra…

从文件到文件描述符:理解程序与文件的交互本质

一、理解文件 抛一个概念&#xff1a; 文件 内容 属性。 1. 那么&#xff0c;空文件有大小吗&#xff1f;答案是有的。因为空文件指的是文件内容为空&#xff0c;文件属性也要占据大小啊。 将来对文件操作&#xff0c;无非分为两类&#xff1a; 1.对文件内容做修改。 2.对文件…

优化算法专栏——阅读导引

前言 提醒&#xff1a; 文章内容为方便作者自己后日复习与查阅而进行的书写与发布&#xff0c;其中引用内容都会使用链接表明出处&#xff08;如有侵权问题&#xff0c;请及时联系&#xff09;。 其中内容多为一次书写&#xff0c;缺少检查与订正&#xff0c;如有问题或其他拓展…

[ The Missing Semester of Your CS Education ] 学习笔记 Vim篇

“Writing English words and writing code are very different activities. When programming, you spend more time switching files, reading, navigating, and editing code compared to writing a long stream.” —— < The Missing Semester of Your CS Education &g…

Linux 系统中定时执行指定命令 crontab 定时任务配置

crontab 定时任务配置是 Linux/Unix 系统中用于自动、周期性执行指定命令或脚本的工具&#xff0c;相当于系统的 “定时闹钟”。它可以让系统在预设的时间&#xff08;如每天凌晨、每周一、每月 1 号等&#xff09;自动完成重复性工作&#xff0c;无需人工干预。自动化运维定期…

[ Leetcode ]---快乐数

题目链接 Leetcode快乐数 题目描述 如下图&#xff1a; 题目解析&#xff1a; 1.双指针法 算法核心思路 判断快乐数的关键挑战是如何检测是否进入无限循环。这里使用了快慢指针法&#xff08;Floyd 循环检测算法&#xff09;&#xff0c;这是一种高效检测循环的技巧&#…

智慧社区构建——2

1.实现Token校验## Token校验URLjson GET /checkToken 参数json HttpServletRequest request 返回json {"msg": "操作成功","code": 200,"status": "ok" }{"msg": "操作成功","code": 200,&q…

K-Means聚类:当数据没有标签时,如何让计算机自动“物以类聚”?

K-Means聚类&#xff1a;当数据没有标签时&#xff0c;如何让计算机自动“物以类聚”&#xff1f;&#x1f44b; 大家好&#xff0c;我是小瑞瑞&#xff01;欢迎回到我的专栏&#xff01; 在我们之前的旅程中&#xff0c;解决的问题大多都有一个明确的“目标”&#xff0c;比如…

万事皆可用 GeeLark AI

在今年4月&#xff0c;GeeLark AI 全面接入 DeepSeek AI 大模型&#xff0c;你可以在独立窗口中便捷地使用 GeeLark AI。除了帮助你编写文案等基础内容&#xff0c;在使用 GeeLark 过程中&#xff0c;如果遇到问题&#xff0c;也可以通过询问 GeeLark AI&#xff0c;及时获取帮…

3D 高保真处理:声网让游戏声音随角色动作变化

传统游戏的声音体验像老式收音机&#xff0c;不管声源位置、距离和障碍物&#xff0c;仅靠左右声道机械调音量&#xff0c;毫无方向感和空间感&#xff0c;如同蒙眼听声辨位。射击游戏中敌人从左边来&#xff0c;耳机却两边同响且音量相近&#xff0c;让人晕头转向&#xff1b;…