摘要:在云服务业务开发中,善于使用代码新特性,往往能让开发效率大大提升,这里简单介绍下lambad表达式及函数式接口特性。

1.Lambda 表达式

Lambda表达式也被称为箭头函数、匿名函数、闭包。他允许把函数作为一个方法的参数(函数作为参数传递到方法中),体现出轻量级函数式编程思想。

为什么引入lambda?

Model Code as Data,编码及数据,尽可能轻量级的将代码封装为数据。

解决方案:接口&实现类(匿名内部类)

存在问题:语法冗余,this关键字、变量捕获、数据控制等

public static void main (String[] args){
// 1. 传统模式下,新线程的创建
new Thread (new Runnable() {
@Override
public void run() {
System.out.println("threading..." + Thread.currentThread().getId())
}
}).start();
// 2. lambda表达式优化线程模式
new Thread(()->{
System.out.println("lambda threading..." + Thread.currentThread().getId());
}) }
  1. 不是解决未知问题的新技术
  2. 对现有问题的语义化优化
  3. 需要根据实际需求考虑性能问题

2.函数式接口(Functional Interface)

函数式接口就是Java类型系统中的接口,是只包含一个抽象方法的特殊接口(可以有很多非抽象方法)。

语言化检测注解:@FunctionalInterface 检测合法性

java1.8支持接口内包含:抽象方法、默认接口方法、静态接口方法、来自Object继承的方法

/**
* 用户身份认证标记接口
*/
@FunctionalInterface
public interface IUserCredential { /**
* 通过用户账号,验证用户身份信息的接口
* @param username 要验证的用户账号
* @return 返回身份信息[系统管理员、用户管理员、普通用户]
*/
String verifyUser(String username); default String getCredential(String username) {
if ("admin".equals(username)) {
return "admin + 系统管理员用户";
} else if("manager".equals(username)){
return "manager + 用户管理员用户";
} else {
return "commons + 普通会员用户";
}
}
String toString(); /**
* 消息合法性验证方法
* @param msg 要验证的消息
* @return 返回验证结果
*/
static boolean verifyMessage(String msg) {
if (msg != null) {
return true;
}
return false;
}
}
 // 匿名内部类,实现接口的抽象方法
IUserCredential ic = new IUserCredential() {
@Override
public String verifyUser(String username) {
return "admin".equals(username)?"管理员":"会员";
}
};
// lambda表达式是函数式接口的一种简单实现
IUserCredential ic2 = (username) -> {
return "admin".equals(username)?"lbd管理员": "lbd会员";
};

JDK 1.8 之前已有的函数式接口:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • more

JDK 1.8 新增加的函数接口:

  • java.util.function
  /*
java.util.function提供了大量的函数式接口
Predicate 接收参数T对象,返回一个boolean类型结果
Consumer 接收参数T对象,没有返回值
Function 接收参数T对象,返回R对象
Supplier 不接受任何参数,直接通过get()获取指定类型的对象
UnaryOperator 接口参数T对象,执行业务处理后,返回更新后的T对象
BinaryOperator 接口接收两个T对象,执行业务处理后,返回一个T对象
*/
Predicate<String> pre = (String username) -> {
return "admin".equals(username);
};
System.out.println(pre.test("manager")); Consumer<String> con = (String message) -> {
System.out.println("要发送的消息:" + message);
};
con.accept("lambda expression."); Function<String, Integer> fun = (String gender) -> {
return "male".equals(gender)?1:0;
};
System.out.println(fun.apply("male")); Supplier<String> sup = () -> {
return UUID.randomUUID().toString();
};
System.out.println(sup.get()); UnaryOperator<String> uo = (String img)-> {
img += "[100x200]";
return img;
};
System.out.println(uo.apply("原图--")); BinaryOperator<Integer> bo = (Integer i1, Integer i2) -> {
return i1 > i2? i1: i2;
};
System.out.println(bo.apply(12, 13));

3.lambda表达式的基本语法

基本语法

  • 声明:就是和lambda表达式绑定的接口类型
  • 参数:包含在一对圆括号中,和绑定的接口中的抽象方法中的参数个数及顺序一致。
  • 操作符:->
  • 执行代码块:包含在一对大括号中,出现在操作符号的右侧

[接口声明] = (参数) -> {执行代码块};

// 没有参数,没有返回值的lambda表达式绑定的接口
interface ILambda1{
void test();
} // 带有参数,没有返回值的lambda表达式
interface ILambda2{
void test(String name, int age);
} // 带有参数,带有返回值的lambda表达式
interface ILambda3 {
int test(int x, int y);
}
ILambda1 i1 = () -> System.out.println("hello boys!");
i1.test(); ILambda2 i21 = ( n, a) -> {
System.out.println(n + "say: my year's old is " + a);
};
i21.test("jerry", 18); ILambda2 i22 = (n, a) ->
System.out.println(n + " 说:我今年" + a + "岁了."); i22.test("tom", 22); ILambda3 i3 = (x, y) -> {
int z = x + y;
return z;
};
System.out.println(i3.test(11, 22)); ILambda3 i31 = (x, y) -> x + y;
System.out.println(i31.test(100, 200));

总结:

  • lambda表达式,必须和接口进行绑定。
  • lambda表达式的参数,可以附带0个到n个参数,括号中的参数类型可以不用指定,jvm在运行时,会自动根据绑定的抽象方法中的参数进行推导。
  • lambda表达式的返回值,如果代码块只有一行,并且没有大括号,不用写return关键字,单行代码的执行结果,会自动返回。 如果添加了大括号,或者有多行代码,必须通过return关键字返回执行结果。

变量捕获

  • 匿名内部类型变量捕获
  • lambda表达式变量捕获
// 1. 匿名内部类型中对于变量的访问
String s1 = "全局变量";
public void testInnerClass() {
String s2 = "局部变量"; new Thread(new Runnable() {
String s3 = "内部变量";
@Override
public void run() {
// 访问全局变量
// System.out.println(this.s1);// this关键字~表示是当前内部类型的对象(报错)
System.out.println(s1); System.out.println(s2);// 局部变量的访问,不能对局部变量进行数据的修改final
// s2 = "hello"; System.out.println(s3);
System.out.println(this.s3);
}
}).start();
} // 2. lambda表达式变量捕获
public void testLambda() {
String s2 = "局部变量lambda"; new Thread(() -> {
String s3 = "内部变量lambda"; // 访问全局变量
// 不再建立对象域
System.out.println(this.s1);// this关键字,表示的就是所属方法所在类型的对象
// 访问局部变量
System.out.println(s2);
// s2 = "hello";// 不能进行数据修改,默认推导变量的修饰符:final
System.out.println(s3);
s3 = "labmda 内部变量直接修改";
System.out.println(s3);
}).start();
}

总结:Lambda表达式优化了匿名内部类类型中的this关键字,不再单独建立对象作用域,表达式本身就是所属类型对象的一部分,在语法语义上使用更加简洁。

类型检查

对于语法相同的表达式,Jvm在运行的过程中,在底层通过解释及重构,进行类型的自动推导。

  • 表达式类型检查
  • 参数类型检查
@FunctionalInterface
interface MyInterface<T, R> {
R strategy (T t, R r); }
public static void test(MyInterface<String, List> inter) {
List<String> list = inter.strategy("hello", new ArrayList());
System.out.println(list);
} public static void main(String[] args) {
test(new MyInterface<String, List>() {
@Override
public List strategy(String s, List list) {
list.add(s);
return list;
}
}); test((x, y) -> {
y.add(x);
return y;
// x.add(y);
// return x;
}); /*
(x,y)->{..} --> test(param) --> param==MyInterface --> lambda表达式-> MyInterface类型
这个就是对于lambda表达式的类型检查,MyInterface接口就是lambda表达式的目标类型(target typing) (x,y)->{..} --> MyInterface.strategy(T r, R r)--> MyInterface<String, List> inter
--> T==String R==List --> lambda--> (x, y) == strategy(T t , R r)--> x==T==String y==R==List
*/

方法重载

interface Param1 {
void outInfo(String info);
} interface Param2 {
void outInfo(String info);
}
// 定义重载的方法
public void lambdaMethod(Param1 param) {
param.outInfo("hello param1 imooc!");
}
public void lambdaMethod(Param2 param) {
param.outInfo("hello param2 imooc");
}
test.lambdaMethod(new Param1() {
@Override
public void outInfo(String info) {
System.out.println(info);
}
}); test.lambdaMethod(new Param2() {
@Override
public void outInfo(String info) {
System.out.println("------");
System.out.println(info);
}
}); /*
lambda表达式存在类型检查-> 自动推导lambda表达式的目标类型
lambdaMethod() -> 方法 -> 重载方法
-> Param1 函数式接口
-> Param2 函数式接口
调用方法-> 传递Lambda表达式-> 自动推导->
-> Param1 | Param2
*/
// 报错 Ambigus Method call
// test.lambdaMethod( (String info) -> {
// System.out.println(info);
// });

总结:出现方法重载的类型中参数都是函数式接口的情况,需使用匿名内部类实现替代lambda表达式。

底层构建原理

public class Test{
public static void main(String args[]){
ITest it = (message) -> System.out.println(message);
it.markUp("lambda!");
// new Test$$Lambda$1().markUp("lambda");
}
}
interface ITest{
void markUp(String msg);
}

javac Test.java

  • javap -p Test.class (javap反解析工具 -p显示所有类与成员)
java -Djdk.internal.lambda.dumpProxyClasses Test

Compiled from "Test.java"
public class Test {
public Test();
public static void main(java.lang.String[]);
private static void lambda$main$0(java.lang.String){
System.out.println(message);
}; }
finnal class Test$$Lambda$1 implements ITest{
private Test$$Lambda$1(){ }
public void markUp(java.lang.String msg){
Test.lambda$main$0(msg);
}
}
      1. 声明一个私有静态方法,对Lambda表达式做一个具体的方法实现
      2. 声明一个final内部类型并实现接口
      3. 在实现接口后的重写方法中利用外部类调用该私有静态方法

4.方法引用

方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

      1. 静态方法引用
      2. 实例方法引用
      3. 构造方法引用
class Person {
private String name;
private String gender;
private int age; // 静态方法引用
public static int compareByAge(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
}
class PersonUtil {
// 增加一个实例方法
public int comprareByName(Person p1, Person p2) {
return p1.getName().hashCode() - p2.getName().hashCode();
} interface IPerson {
// 抽象方法:通过指定类型的构造方法初始化对象数据
Person initPerson(String name, String gender, int age); }
public static void main(String[] args) {
List<Person> list = new ArrayList<Person>();
list.add(new Person("shuke", "男", 29));
list.add(new Person("tom", "男", 16));
list.add(new Person("jerry", "男", 20));
list.add(new Person("beita", "女", 30)); // 1.匿名内部类实现
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
// 2.lambda表达式实现
Collections.sort(list, (p1, p2) -> p1.getAge() - p2.getAge());
// 3.静态方法引用实现
Collections.sort(list, Person::compareByAge); // 4.实例方法引用
PersonUtil pu = new PersonUtil();
Collections.sort(list, pu::comprareByName);
list.forEach(System.out::println); // 5.构造方法引用:绑定函数式接口
IPerson ip = Person::new;
Person person = p1.initPerson("tom", "男", 18);
System.out.println(person);
}

5.Stream

    • 新添加的Stream流—是一个来自数据源的元素队列并支持聚合操作。把真正的函数式编程风格引入到Java中。
    • 不存储数据,也不修改原始源。
    • Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
    • Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
    • 这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
    • 元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
// 1. for循环实现 List<String> list = new ArrayList<String>(); for (String s : list) { if (s.length() > 3) { lista.add(s); } } System.out.println(lista);
// 2. 迭代器实现
List<String> listb = new ArrayList<>();
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String s = it.next();
if(s.length() > 3) {
listb.add(s);
}
}
System.out.println(listb); // 3. stream实现
List listc = list.stream().filter(s->s.length()>3)
.collect(Collectors.toList());
System.out.println(listc);

几者关系

    • lambda表达式是传统方法的语法糖,简化并且改造传统内部类实现设计方案的另一种实现模式。
    • 方法引用又是lambda基础上的语法糖,和Stream没有关系,简化方法调用的。
    • Stream是针对数据和集合的强化优化操作,可以和lambda结合起来简化编码过程。

常见API介绍

1.聚合操作

2.Stream的处理流程

    • 数据源
    • 数据转换[可一到多次转换]
    • 获取结果

3.获取Stream对象

    • 从集合或者数组中获取

Collection.stream(), 如list.stream()

Collection.parallelstream(), 获得支持并发处理的流

Arrays.stream(T t)

    • BufferReader

BufferReader.lines()-> stream()

    • 静态工厂

java.util.stream.IntStream.range()..

java.nio.file.Files.walk()..

    • 自定构建

java.util.Spliterator

    • 更多的方式

Random.ints()

Pattern.spiltAsStream()..

4.中间操作API{intermediate}:

    • 操作结果是一个Stream对象,所以中间操作可有一个或多个连续的中间操作,需要注意的是中间操作只记录操作方式,不做具体执行,直到结束操作发生时,才做数据的最终执行。
    • 中间操作就是业务逻辑处理
    • 操作过程分为有状态和无状态

无状态:即处理数据时,不受前置中间操作的影响

    • map/filter/peek/parallel/sequential/unordered
    • 有状态:即处理数据时,受前置中间操作的影响
    • distant/sorted/limit/skip

5.终结操作|结束操作{Terminal}

一个steam对象只能有一个Terminal操作。这个操作不可逆,一旦发生,就会真实处理数据生成对应结果

    • 非短路操作:当前的Stream对象必须处理完集合中所有的数据,才能得到处理结果

forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator

短路操作:当前的Stream对象在处理过程中,一旦满足某个条件,就可以得到结果

anyMatch/AllMatch/noneMatch/findfirst/findAny等

short-circuiting : 在无限大的stream 中返回有限大的stream 需要包含短路操作是有必要的

Stream转换

 // 1. 批量数据 -> Stream对象
// 多个数据
Stream stream = Stream.of("admin", "tom", "jerry"); // 数组
String [] strArrays = new String[] {"xueqi", "biyao"};
Stream stream2 = Arrays.stream(strArrays); // 列表
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
Stream stream3 = list.stream(); // 集合
Set<String> set = new HashSet<>();
set.add("aaa");
set.add("bbb");
set.add("ccc");
Stream stream4 = set.stream(); // Map
Map<String, Integer> map = new HashMap<>();
map.put("tom", 1000);
map.put("jerry", 1200);
map.put("shuke", 1000);
Stream stream5 = map.entrySet().stream(); //2. Stream对象对于基本数据类型的功能封装
//int / long / double
IntStream.of(new int[] {10, 20, 30}).forEach(System.out::println); //只做一次拆箱装箱
IntStream.range(1, 5).forEach(System.out::println);
IntStream.rangeClosed(1, 5).forEach(System.out::println); // 3. Stream对象 --> 转换得到指定的数据类型
// 数组
Object [] objx = stream.toArray(String[]::new); // 字符串
String str = stream.collect(Collectors.joining()).toString();
System.out.println(str); // 列表
//List<String> listx = (List<String>) stream.collect(Collectors.toList());
System.out.println(listx); // 集合
//Set<String> setx = (Set<String>) stream.collect(Collectors.toSet());
System.out.println(setx); // Map
//Map<String, String> mapx = (Map<String, String>) stream.collect(Collectors.toMap(x->x, y->"value:"+y)); System.out.println(mapx);

Stream常见操作

// Stream中常见的API操作
List<String> accountList = new ArrayList<>();
accountList.add("tom");
accountList.add("jerry");
accountList.add("apha");
accountList.add("beta");
accountList.add("shuke"); // map() 中间操作,map()方法接收一个Functional接口
accountList = accountList.stream().map(x->"name:" + x).collect(Collectors.toList()); // filter() 添加过滤条件,过滤符合条件的用户
accountList = accountList.stream().filter(x-> x.length() > 3).collect(Collectors.toList()); // forEach 增强型循环
accountList.forEach(x-> System.out.println("forEach->" + x)); // peek() 中间操作,迭代数据完成数据的依次处理过程
accountList.stream()
.peek(x -> System.out.println("peek 1: " + x))
.peek(x -> System.out.println("peek 2:" + x))
.forEach(System.out::println);// 合并多个过程 迭代只发生一次 accountList.forEach(System.out::println); // Stream中对于数字运算的支持
List<Integer> intList = new ArrayList<>();
intList.add(20);
intList.add(19);
intList.add(7);
intList.add(8);
intList.add(86);
intList.add(11);
intList.add(3);
intList.add(20); // skip() 中间操作,有状态,跳过部分数据
intList.stream().skip(3).forEach(System.out::println); // limit() 中间操作,有状态,限制输出数据量
intList.stream().skip(3).limit(2).forEach(System.out::println); // distinct() 中间操作,有状态,剔除重复的数据
intList.stream().distinct().forEach(System.out::println); // sorted() 中间操作,有状态,排序
// max() 获取最大值
Optional optional = intList.stream().max((x, y)-> x-y);
System.out.println(optional.get());
// min() 获取最小值 // reduce() 合并处理数据
Optional optional2 = intList.stream().reduce((sum, x)-> sum + x);
System.out.println(optional2.get());

6.案例

问题一:将实例List转化为Map

对于List<Table>来说,我需要将其形变为Map<Table.id,Table>,用如下流处理代码

//Table类
public class DmTable {
private Integer id; private String tableName; private String tableComment; private Integer datasourceId; private Integer directoryId; private Boolean partitionFlag; private Integer columnNum;
// ......
}
tableMap=TableList.stream().collect(Collectors.toMap(Table::getId, b -> b);
// 等效于
tableMap=TableList.stream().collect(Collectors.toMap(Table::getId, Function.identity()));// 静态方法 实现 return t -> t;

问题二:将集合分成若干类别

使用问题一中的Table类,对于List<Table>,我需要将其按照partitionFlag分类,Collector提供两种方法partitioningBy()、groupingBy()。前者分成满足条件与不满足条件两类,后者可按条件分成若干类别的Map。

Map<Boolean, List<Table>> tablePartition = tableList
.stream().collect(Collectors.partitioningBy(item -> item.getPartitionFlag() == true));

有的时候,我们关注的不光是元素还有元素的个数,流处理可以再进行后期处理。

Map<Boolean, List<Table>> tablePartition = tableList

        .stream().collect(Collectors.partitioningBy(item -> item.getPartitionFlag() == true,Collectors.counting()));

可输出符合要求的个数。

groupingBy()可对字符串长度分组。

List<String> strings=Arrays.asList(“this”,”is”,”a”,”test”);
Map<Integer, List<String>> stringsMap = strings
.stream().collect(Collectors.groupingBy(String::length);

结果输出多分类的map,key值为字符串长度。

注意:如果是从数据库获取数据,务必将分组操作放在数据库中执行,java8新增方法只适合处理内存中的数据。

问题三:从list中得到某个特定的对象

获得List<Table>中columnNum最多的table对象

tableList.stream().sorted(comparingInt(Table::getColumnNum)).collect(Collectors.toList()).get(tableList.size() - 1);

添加中间操作reversed() 可获取最小columnNum的对象

问题四: 得到Map<Table,Table.columnNum>中最大columnNum的table

 List<Map.Entry<Table, Integer>> list = new ArrayList(tableMap.entrySet());
Collections.sort(list, (o1, o2) -> (o2.getValue() - o1.getValue()));
list.get(0).getKey();

7.性能与安全

  • 串行Stream的性能小于传统的for循环、 迭代器
  • 并行Stream的性能与传统的for循环、 迭代器差不多,在处理对象(复杂数据类型)的情况下,并行性能最佳
// 整数列表
List<Integer> lists = new ArrayList<Integer>();
// 增加数据
for (int i = 0; i < 1000; i++){
lists.add(i);
} // 串行Stream
List<Integer> list2 = new ArrayList<>();
lists.stream().forEach(x->list2.add(x));
System.out.println(lists.size());
System.out.println(list2.size());
// 并行Stream 线程不安全 丢失
List<Integer> list3 = new ArrayList<>();
lists.parallelStream().forEach(x-> list3.add(x));
System.out.println(list3.size());
// collect 当并行执行时可以实例化、填充和合并多个中间结果,以保持可变数据结构的隔离
List<Integer> list4 = lists.parallelStream().collect(Collectors.toList());
System.out.println(list4.size());

本文分享自华为云社区《如何善用函数式接口简化云服务业务代码开发》,原文作者:luanzhen 。

点击关注,第一时间了解华为云新鲜技术~

interface Param1 {
void outInfo(String info);
} interface Param2 {
void outInfo(String info);
}
// 定义重载的方法
public void lambdaMethod(Param1 param) {
param.outInfo("hello param1 imooc!");
}
public void lambdaMethod(Param2 param) {
param.outInfo("hello param2 imooc");
}

简化业务代码开发:看Lambda表达式如何将代码封装为数据的更多相关文章

  1. 每日代码 - 6/26 lambda表达式

    public class CartViewRender { public static CartView renderMyCartView(Cart cart){ ErrorCodeEnum erro ...

  2. 【Python开发】Lambda表达式使用

    lambda只是一个表达式,函数体比def简单很多. lambda的主体是一个表达式,而不是一个代码块.仅仅能在lambda表达式中封装有限的逻辑进去. lambda表达式是起到一个函数速写的作用.允 ...

  3. 使用 functional interface 和 lambda 表达式来优化代码

    ========================================原始代码========================================RoleService 类有删除 ...

  4. Kotlin的Lambda表达式以及它们怎样简化Android开发(KAD 07)

    作者:Antonio Leiva 时间:Jan 5, 2017 原文链接:https://antonioleiva.com/lambdas-kotlin/ 由于Lambda表达式允许更简单的方式建模式 ...

  5. Java开发笔记(六十一)Lambda表达式

    前面介绍了匿名内部类的简单用法,通过在sort方法中运用匿名内部类,不但能够简化代码数量,还能保持业务代码的连续性.只是匿名内部类的结构仍显啰嗦,虽然它省去了内部类的名称,但是花括号里面的方法定义代码 ...

  6. C#复习笔记(4)--C#3:革新写代码的方式(Lambda表达式和表达式树)

    Lambda表达式和表达式树 先放一张委托转换的进化图 看一看到lambda简化了委托的使用. lambda可以隐式的转换成委托或者表达式树.转换成委托的话如下面的代码: Func<string ...

  7. 开发基础之牛逼哄哄的 Lambda 表达式,简洁优雅就是生产力

    什么是Lambda? 我们知道,对于一个Java变量,我们可以赋给其一个“值”. 如果你想把“一块代码”赋给一个Java变量,应该怎么做呢? 比如,我想把右边那块代码,赋给一个叫做aBlockOfCo ...

  8. C# 从CIL代码了解委托,匿名方法,Lambda 表达式和闭包本质

    前言 C# 3.0 引入了 Lambda 表达式,程序员们很快就开始习惯并爱上这种简洁并极具表达力的函数式编程特性. 本着知其然,还要知其所以然的学习态度,笔者不禁想到了几个问题. (1)匿名函数(匿 ...

  9. day10-Python运维开发基础(函数嵌套、nonlocal声明局部变量、闭包、locals/globals、lambda表达式)

    1. 函数的嵌套与nonlocal 声明局部变量 # ### 函数的嵌套 """ 函数和函数之间可以互相嵌套: 嵌套在内层的叫做内函数 乔涛在外层的叫做外函数 " ...

  10. lambda表达式之进化

    前言 在C#我们可以自定义委托,但是C#为什么还要内置泛型委托呢?因为我们常常要使用委托,如果系统内置了一些你可能会用到的委托,那么就省去了定义委托,然后实例化委托的步骤,这样一来既使代码看起来简洁而 ...

随机推荐

  1. 2023-10-18:用go语言,给定一个数组arr,长度为n,表示有0~n-1号设备, arr[i]表示i号设备的型号,型号的种类从0~k-1,一共k种型号, 给定一个k*k的矩阵map,来表示型号

    2023-10-18:用go语言,给定一个数组arr,长度为n,表示有0~n-1号设备, arr[i]表示i号设备的型号,型号的种类从0~k-1,一共k种型号, 给定一个k*k的矩阵map,来表示型号 ...

  2. 虹科喜报 | 虹科技术工程师【国内首批】拿下Redis认证开发者证书!

    要说虹科数据库技术工程师有多强悍,认证考试2022年12月上线,次年2月就以全国首批速度强势通过考试,并于两周后正式收到[Redis认证开发人员]证书! 虹科小云忍不住浅浅炫耀一下: 或许大家对Red ...

  3. MAC安装pwntools记录

    1.使用python3安装pwntools pip3 install pwntools 2.安装成功后测试 测试不成功提示安装 binutils pwnlib.exception.PwnlibExce ...

  4. 最新 2023.2 版本 IDEA 永久破解教程,IDEA 破解补丁永久激活(亲测有效)

    最近 jetbrains 官方发布了 2023.2 版本的 IDEA,之前的激活方法并不支持这个新的版本. 下面是最新的激活教程,激活步骤和之前是类似的,只是换用了不同的补丁文件. 本教程支持 Jet ...

  5. 研读Java代码必须掌握的Eclipse快捷键

    1. Ctrl+左键 和F3 这个是大多数人经常用到的,用来查看变量.方法.类的定义 跳到光标所在标识符的定义代码.当按执行流程阅读时,F3实现了大部分导航动作. 2 Ctrl+Shift+G 在工作 ...

  6. 算法训练 递归 s01串

    问题描述 s01串初始为"0" 按以下方式变换 0变1,1变01 输入格式 1个整数(0~19) 输出格式 n次变换后s01串 样例输入 3 样例输出 101 数据规模和约定 0~ ...

  7. .NET周刊【11月第2期 2023-11-12】

    国内文章 一个基于百度飞桨封装的.NET版本OCR工具类库 - PaddleOCRSharp https://www.cnblogs.com/Can-daydayup/p/17818557.html ...

  8. Windows文件句柄无效

    今天我用FreeFileSync从移动硬盘复制一个名为Con的文件夹到本地硬盘,复制失败. 通过文件夹资源管理器Explorer直接访问文件夹则提示"禁止访问",右键属性切换到安全 ...

  9. 深入了解Rabbit加密技术:原理、实现与应用

    一.引言 在信息时代,数据安全愈发受到重视,加密技术作为保障信息安全的核心手段,得到了广泛的研究与应用.Rabbit加密技术作为一种新型加密方法,具有较高的安全性和便捷性.本文将对Rabbit加密技术 ...

  10. MySQL-mysqldump 报错:[ERROR] unknown variable 'local_infile=1'.

    版权声明:原创作品,谢绝转载!否则将追究法律责任. ----- 作者:kirin mysqldump: [ERROR] unknown variable 'local_infile=1'. 解决方法: ...