当前位置: 首页 > 编程日记 > 正文

10个Java 8 Lambda表达式经典示例

Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动。特性之一便是随同发布的lambda表 达式,它将允许我们将行为传到函数里。在Java 8之前,如果想将行为传入函数,仅有的选择就是匿名类,需要6行代码。而定义行为最重要的那行代码,却混在中间不够突出。Lambda表达式取代了匿名 类,取消了模板,允许用函数式风格编写代码。这样有时可读性更好,表达更清晰。在Java生态系统中,函数式表达与对面向对象的全面支持是个激动人心的进 步。

将进一步促进并行第三方库的发展,充分利用多核CPU。尽管业界需要时间来消化Java 8,但我认为任何严谨的Java开发者都不应忽视此次Java发布的核心特性,即lambda表达式、函数式接口、流API、默认方法和新的Date以及 Time API。作为开发人员,我发现学习和掌握lambda表达式的最佳方法就是勇于尝试,尽可能多练习lambda表达式例子。鉴于受Java 8发布的影响最大的是Java集合框架(Java Collections framework),所以最好练习流API和lambda表达式,用于对列表(Lists)和集合(Collections)数据进行提取、过滤和排 序。我一直在进行关于Java 8的写作,过去也曾分享过一些资源来帮助大家掌握Java 8。本文分享在代码中最有用的10个lambda表达式的使用方法,这些例子都短小精悍,将帮助你快速学会lambda表达式。

Java 8 lambda表达式示例

我个人对Java 8发布非常激动,尤其是lambda表达式和流API。越来越多的了解它们,我能写出更干净的代码。虽然一开始并不是这样。第一次看到用lambda表达 式写出来的Java代码时,我对这种神秘的语法感到非常失望,认为它们把Java搞得不可读,但我错了。花了一天时间做了一些lambda表达式和流 API示例的练习后,我开心的看到了更清晰的Java代码。这有点像学习泛型,第一次见的时候我很讨厌它。我甚至继续使用老版Java 1.4来处理集合,直到有一天,朋友跟我介绍了使用泛型的好处(才意识到它的好处)。所以基本立场就是,不要畏惧lambda表达式以及方法引用的神秘语 法,做几次练习,从集合类中提取、过滤数据之后,你就会喜欢上它。下面让我们开启学习Java 8 lambda表达式的学习之旅吧,首先从简单例子开始。

例1、用lambda表达式实现Runnable

我开始使用Java 8时,首先做的就是使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例。看一下Java 8之前的runnable实现方法,需要4行代码,而使用lambda表达式只需要一行代码。我们在这里做了什么呢?那就是用() -> {}代码块替代了整个匿名类。

// Java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Before Java8, too much code for too little to do");
}
}).start();

//Java 8方式:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();

输出:

too much code, for too little to do
Lambda expression rocks !!

这个例子向我们展示了Java 8 lambda表达式的语法。你可以使用lambda写出如下代码:

(params) -> expression
(params) -> statement
(params) -> { statements }

例如,如果你的方法不对参数进行修改、重写,只是在控制台打印点东西的话,那么可以这样写:

() -> System.out.println("Hello Lambda Expressions");

如果你的方法接收两个参数,那么可以写成如下这样:

(int even, int odd) -> even + odd

顺便提一句,通常都会把lambda表达式内部变量的名字起得短一些。这样能使代码更简短,放在同一行。所以,在上述代码中,变量名选用a、b或者x、y会比even、odd要好。

例2、使用Java 8 lambda表达式进行事件处理

如果你用过Swing API编程,你就会记得怎样写事件监听代码。这又是一个旧版本简单匿名类的经典用例,但现在可以不这样了。你可以用lambda表达式写出更好的事件监听代码,如下所示:

// Java 8之前:
JButton show =  new JButton("Show");
show.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Event handling without lambda expression is boring");
}
});

// Java 8方式:
show.addActionListener((e) -> {
System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
});

Java开发者经常使用匿名类的另一个地方是为 Collections.sort() 定制 Comparator。在Java 8中,你可以用更可读的lambda表达式换掉丑陋的匿名类。我把这个留做练习,应该不难,可以按照我在使用lambda表达式实 现 Runnable 和 ActionListener 的过程中的套路来做。

例3、使用lambda表达式对列表进行迭代

如果你使过几年Java,你就知道针对集合类,最常见的操作就是进行迭代,并将业务逻辑应用于各个元素,例如处理订单、交易和事件的列表。由于 Java是命令式语言,Java 8之前的所有循环代码都是顺序的,即可以对其元素进行并行化处理。如果你想做并行过滤,就需要自己写代码,这并不是那么容易。通过引入lambda表达式 和默认方法,将做什么和怎么做的问题分开了,这意味着Java集合现在知道怎样做迭代,并可以在API层面对集合元素进行并行处理。下面的例子里,我将介 绍如何在使用lambda或不使用lambda表达式的情况下迭代列表。你可以看到列表现在有了一个 forEach()  方法,它可以迭代所有对象,并将你的lambda代码应用在其中。

// Java 8之前:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
System.out.println(feature);
}

// Java 8之后:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));

// 使用Java 8的方法引用更方便,方法引用由::双冒号操作符标示,
// 看起来像C++的作用域解析运算符
features.forEach(System.out::println);

输出:

Lambdas
Default Method
Stream API
Date and Time API

列表循环的最后一个例子展示了如何在Java 8中使用方法引用(method reference)。你可以看到C++里面的双冒号、范围解析操作符现在在Java 8中用来表示方法引用。

例4、使用lambda表达式和函数式接口Predicate

除了在语言层面支持函数式编程风格,Java 8也添加了一个包,叫做 java.util.function。它包含了很多类,用来支持Java的函数式编程。其中一个便是Predicate,使用 java.util.function.Predicate 函数式接口以及lambda表达式,可以向API方法添加逻辑,用更少的代码支持更多的动态行为。下面是Java 8 Predicate 的例子,展示了过滤集合数据的多种常用方法。Predicate接口非常适用于做过滤。

public static void main(args[]){
List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");

System.out.println("Languages which starts with J :");
filter(languages, (str)->str.startsWith("J"));

System.out.println("Languages which ends with a ");
filter(languages, (str)->str.endsWith("a"));

System.out.println("Print all languages :");
filter(languages, (str)->true);

System.out.println("Print no language : ");
filter(languages, (str)->false);

System.out.println("Print language whose length greater than 4:");
filter(languages, (str)->str.length() > 4);
}

public static void filter(List names, Predicate condition) {
for(String name: names)  {
  if(condition.test(name)) {
   System.out.println(name + " ");
  }
}
}

输出:

Languages which starts with J :
Java
Languages which ends with a
Java
Scala
Print all languages :
Java
Scala
C++
Haskell
Lisp
Print no language :
Print language whose length greater than 4:
Scala
Haskell

// 更好的办法
public static void filter(List names, Predicate condition) {
names.stream().filter((name) -> (condition.test(name))).forEach((name) -> {
  System.out.println(name + " ");
});
}

可以看到,Stream API的过滤方法也接受一个Predicate,这意味着可以将我们定制的 filter() 方法替换成写在里面的内联代码,这就是lambda表达式的魔力。另外,Predicate接口也允许进行多重条件的测试,下个例子将要讲到。

例5、如何在lambda表达式中加入Predicate

上个例子说到,java.util.function.Predicate 允许将两个或更多的 Predicate 合成一个。它提供类似于逻辑操作符AND和OR的方法,名字叫做and()、or()和xor(),用于将传入 filter() 方法的条件合并起来。例如,要得到所有以J开始,长度为四个字母的语言,可以定义两个独立的 Predicate 示例分别表示每一个条件,然后用 Predicate.and() 方法将它们合并起来,如下所示:

// 甚至可以用and()、or()和xor()逻辑函数来合并Predicate,
// 例如要找到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传入
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
.filter(startsWithJ.and(fourLetterLong))
.forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));

类似地,也可以使用 or() 和 xor() 方法。本例着重介绍了如下要点:可按需要将 Predicate 作为单独条件然后将其合并起来使用。简而言之,你可以以传统Java命令方式使用 Predicate 接口,也可以充分利用lambda表达式达到事半功倍的效果。

例6、Java 8中使用lambda表达式的Map和Reduce示例

本例介绍最广为人知的函数式编程概念map。它允许你将对象进行转换。例如在本例中,我们将 costBeforeTax 列表的每个元素转换成为税后的值。我们将 x -> x*x lambda表达式传到 map() 方法,后者将其应用到流中的每一个元素。然后用 forEach() 将列表元素打印出来。使用流API的收集器类,可以得到所有含税的开销。有 toList() 这样的方法将 map 或任何其他操作的结果合并起来。由于收集器在流上做终端操作,因此之后便不能重用流了。你甚至可以用流API的 reduce() 方法将所有数字合成一个,下一个例子将会讲到。

// 不使用lambda表达式为每个订单加上12%的税
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
System.out.println(price);
}

// 使用lambda表达式
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);

输出:

112.0
224.0
336.0
448.0
560.0
112.0
224.0
336.0
448.0
560.0

例6.2、Java 8中使用lambda表达式的Map和Reduce示例

在上个例子中,可以看到map将集合类(例如列表)元素进行转换的。还有一个 reduce() 函数可以将所有值合并成一个。Map和Reduce操作是函数式编程的核心操作,因为其功能,reduce 又被称为折叠操作。另外,reduce 并不是一个新的操作,你有可能已经在使用它。SQL中类似 sum()、avg() 或者 count() 的聚集函数,实际上就是 reduce 操作,因为它们接收多个值并返回一个值。流API定义的 reduceh() 函数可以接受lambda表达式,并对所有值进行合并。IntStream这样的类有类似 average()、count()、sum() 的内建方法来做 reduce 操作,也有mapToLong()、mapToDouble() 方法来做转换。这并不会限制你,你可以用内建方法,也可以自己定义。在这个Java 8的Map Reduce示例里,我们首先对所有价格应用 12% 的VAT,然后用 reduce() 方法计算总和。

// 为每个订单加上12%的税
// 老方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
total = total + price;
}
System.out.println("Total : " + total);

// 新方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);

输出:

Total : 1680.0
Total : 1680.0

例7、通过过滤创建一个String列表

过滤是Java开发者在大规模集合上的一个常用操作,而现在使用lambda表达式和流API过滤大规模数据集合是惊人的简单。流提供了一个 filter() 方法,接受一个 Predicate 对象,即可以传入一个lambda表达式作为过滤逻辑。下面的例子是用lambda表达式过滤Java集合,将帮助理解。

// 创建一个字符串列表,每个字符串长度大于2
List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());
System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);

输出:

Original List : [abc, , bcd, , defg, jk], filtered list : [abc, bcd, defg]

另外,关于 filter() 方法有个常见误解。在现实生活中,做过滤的时候,通常会丢弃部分,但使用filter()方法则是获得一个新的列表,且其每个元素符合过滤原则。

例8、对列表的每个元素应用函数

我们通常需要对列表的每个元素使用某个函数,例如逐一乘以某个数、除以某个数或者做其它操作。这些操作都很适合用 map() 方法,可以将转换逻辑以lambda表达式的形式放在 map() 方法里,就可以对集合的各个元素进行转换了,如下所示。

// 将字符串换成大写并用逗号链接起来
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);

输出:

USA, JAPAN, FRANCE, GERMANY, ITALY, U.K., CANADA

例9、复制不同的值,创建一个子列表

本例展示了如何利用流的 distinct() 方法来对集合进行去重。

// 用所有不同的数字创建一个正方形列表
List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.printf("Original List : %s,  Square Without duplicates : %s %n", numbers, distinct);

输出:

Original List : [9, 10, 3, 4, 7, 3, 4],  Square Without duplicates : [81, 100, 9, 16, 49]

例10、计算集合元素的最大值、最小值、总和以及平均值

IntStream、LongStream 和 DoubleStream 等流的类中,有个非常有用的方法叫做 summaryStatistics() 。可以返回 IntSummaryStatistics、LongSummaryStatistics 或者 DoubleSummaryStatistic s,描述流中元素的各种摘要数据。在本例中,我们用这个方法来计算列表的最大值和最小值。它也有 getSum() 和 getAverage() 方法来获得列表的所有元素的总和及平均值。

//获取数字的个数、最小值、最大值、总和以及平均值
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());

输出:

Highest prime number in List : 29
Lowest prime number in List : 2
Sum of all prime numbers : 129
Average of all prime numbers : 12.9

Lambda表达式 vs 匿名类

既然lambda表达式即将正式取代Java代码中的匿名内部类,那么有必要对二者做一个比较分析。一个关键的不同点就是关键字 this。匿名类的 this 关键字指向匿名类,而lambda表达式的 this 关键字指向包围lambda表达式的类。另一个不同点是二者的编译方式。Java编译器将lambda表达式编译成类的私有方法。使用了Java 7的 invokedynamic 字节码指令来动态绑定这个方法。

Java 8 Lambda表达式要点

10个Java lambda表达式、流API示例

到目前为止我们看到了Java 8的10个lambda表达式,这对于新手来说是个合适的任务量,你可能需要亲自运行示例程序以便掌握。试着修改要求创建自己的例子,达到快速学习的目 的。我还想建议大家使用Netbeans IDE来练习lambda表达式,它对Java 8支持良好。当把代码转换成函数式的时候,Netbeans会及时给你提示。只需跟着Netbeans的提示,就能很容易地把匿名类转换成lambda表 达式。此外,如果你喜欢阅读,那么记得看一下Java 8的lambdas,实用函数式编程这本书(Java 8 Lambdas, pragmatic functional programming),作者是Richard Warburton,或者也可以看看Manning的Java 8实战(Java 8 in Action),这本书虽然还没出版,但我猜线上有第一章的免费pdf。不过,在你开始忙其它事情之前,先回顾一下Java 8的lambda表达式、默认方法和函数式接口的重点知识。

1)lambda表达式仅能放入如下代码:预定义使用了 @Functional 注释的函数式接口,自带一个抽象函数的方法,或者SAM(Single Abstract Method 单个抽象方法)类型。这些称为lambda表达式的目标类型,可以用作返回类型,或lambda目标代码的参数。例如,若一个方法接收Runnable、 Comparable或者 Callable 接口,都有单个抽象方法,可以传入lambda表达式。类似的,如果一个方法接受声明于 java.util.function 包内的接口,例如 Predicate、Function、Consumer 或 Supplier,那么可以向其传lambda表达式。

2)lambda表达式内可以使用方法引用,仅当该方法不修改lambda表达式提供的参数。本例中的lambda表达式可以换为方法引用,因为这仅是一个参数相同的简单方法调用。

list.forEach(n -> System.out.println(n));
list.forEach(System.out::println);  // 使用方法引用

然而,若对参数有任何修改,则不能使用方法引用,而需键入完整地lambda表达式,如下所示:

list.forEach((String s) -> System.out.println("*" + s + "*"));

事实上,可以省略这里的lambda参数的类型声明,编译器可以从列表的类属性推测出来。

3)lambda内部可以使用静态、非静态和局部变量,这称为lambda内的变量捕获。

4)Lambda表达式在Java中又称为闭包或匿名函数,所以如果有同事把它叫闭包的时候,不用惊讶。

5)Lambda方法在编译器内部被翻译成私有方法,并派发 invokedynamic 字节码指令来进行调用。可以使用JDK中的 javap 工具来反编译class文件。使用 javap -p 或 javap -c -v 命令来看一看lambda表达式生成的字节码。大致应该长这样:

private static java.lang.Object lambda$0(java.lang.String);

6)lambda表达式有个限制,那就是只能引用 final 或 final 局部变量,这就是说不能在lambda内部修改定义在域外的变量。

List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { factor++; });

Compile time error : "local variables referenced from a lambda expression must be final or effectively final"

另外,只是访问它而不作修改是可以的,如下所示:

List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { System.out.println(factor*element); });

输出:

4
6
10
14

因此,它看起来更像不可变闭包,类似于Python。

以上就是Java 8的lambda表达式的全部10个例子。此次修改将成为Java史上最大的一次,将深远影响未来Java开发者使用集合框架的方式。我想规模最相似的一 次修改就是Java 5的发布了,它带来了很多优点,提升了代码质量,例如:泛型、枚举、自动装箱(Autoboxing)、静态导入、并发API和变量参数。上述特性使得 Java代码更加清晰,我想lambda表达式也将进一步改进它。我在期待着开发并行第三方库,这可以使高性能应用变得更容易写。


来源:51CTO

相关文章:

Sql server 2005带来的分页便利

select threadid from (select threadid, ROW_NUMBER() OVER (order by stickydate) as Pos from cs_threads) as T where T.Pos > 100000 and T.Pos < 100030 如果里面的这个表cs_threads数据量超大&#xff0c;比如&#xff0c;几亿条记录&#xff0c;那这个方法应该是…

想学Python?快看看这个教程!收藏!

Python入门从哪开始&#xff0c;90%以上的书上都是这样讲的&#xff1a;先介绍 Python 的基本语法规则、list、dict、tuple 等数据结构&#xff0c;然后再介绍字符串处理和正则表达式&#xff0c;介绍文件等IO操作.... 就这样一点一点往下说。然而这种按部就班的学习方法&#…

Struts1.x系列教程(4):标签库概述与安装

Struts的整个视图层&#xff08;就是MVC模式中的View层&#xff09;是由Struts的定制标签&#xff08;或者称为定制动作&#xff09;和客户端代码(Javascript、HTML等)实现的。这些Struts标签被写在JSP页面中&#xff0c;用于生成客户端代码、进行逻辑判断等工作&#xff0c;使…

绿盟科技与CCF成立“鲲鹏”科研基金 计划发力5大领域资助16个项目

【51CTO.com原创稿件】2017年5月10日&#xff0c;由中国计算机学会(CCF)和北京神州绿盟信息安全科技股份有限公司(以下简称&#xff1a;绿盟科技)主办的2017 CCF-绿盟科技“鲲鹏”科研基金新闻发布会于北京隆重举行。双方共同宣布“鲲鹏”科研基金正式成立&#xff0c;该基金将…

修改SQL SERVER内置存储过程

SQLSERVER估计是为了安装或者其它方面&#xff0c;它内置了一批危险的存储过程。能读到注册表信息&#xff0c;能写入注册表信息&#xff0c;能读磁盘共享信息等等……各位看到这儿&#xff0c;心里可能会在想&#xff0c;我的网站中有其它的代码&#xff0c;又不像查询分析器那…

〖Linux〗使用Qt5.2.0开发Android的NDK应用程序

2013年12月11日&#xff0c;Qt发布了其新的Qt版本&#xff1a;Qt5.2.0&#xff1b; 利用这个新的版本&#xff0c;我们可以很轻松地制作出Android手机的NDK应用程序。 开发环境&#xff1a;Ubuntu13.10 x86_64 下载链接&#xff1a;http://download.qt-project.org/official_re…

基于 OpenCV 的面部关键点检测实战

【 编者按】这篇文章概述了用于构建面部关键点检测模型的技术&#xff0c;这些技术是Udacity的AI Nanodegree程序的一部分。作者 | 小白责编 | 欧阳姝黎概述在Udacity的AIND的最终项目中&#xff0c;目标是创建一个面部关键点检测模型。然后将此模型集成到完整的流水线中&#…

气温上升影响数据中心节能

也许一些公司都是你熟悉的名字。但你可能不知道的是&#xff0c;这样的公司拥有遍布美国的大型数据中心&#xff0c;包括北弗吉尼亚州。数据中心用于存储、备份和通信业务&#xff0c;使用了大量的电力&#xff0c;为IT设备供电&#xff0c;并保持其冷却其数据中心基础设施。 也…

如何将SQL Server表驻留内存和检测

将SQL Server数据表驻留内存是SQL Server提供的一项功能&#xff0c;在一般小型系统的开发过程中估计很少会涉及到。这里整理了相关文档资料&#xff0c;演示如何把SQL Server中一个表的所有数据都放入内存中&#xff0c;实现内存数据库&#xff0c;提高实时性。 1, DBCC PINTA…

C 一样快,Ruby 般丝滑的 Crystal 发布 1.0 版本,你看好吗?

整理 | 寇雪芹 头图 | 下载于视觉中国 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 近日&#xff0c;编程语言 Crystal 发布了 1.0 版本。 Crystal 是一种通用的、面向对象的编程语言&#xff0c;其语法受到 Ruby 语言的启发&#xff0c;具有静态类型检查功能&am…

php in yii framework

为什么80%的码农都做不了架构师&#xff1f;>>> command line php yiic defined(STDIN) or define(STDIN, fopen(php://stdin, r));$_SERVER[argv](2013-12-30 22:44:10) 最近读这本书, 讲使用方法, 每章节各自独立, 配合些源码看(但不要细看, 会干扰进度, 浪费时…

selenium grid2 使用远程机器的浏览器

下载 selenium-server-standalone-3.4.0.jar包在selenium-server-standalone-3.4.0.jar包目录下面执行cmd 命令 java -jar selenium-server-standalone-3.4.0.jar -role hub 启用selenium grid hub&#xff0c; 默认端口 4444注册浏览器客户端&#xff0c;命令行执行&#xff…

Create a restful app with AngularJS/Grails(4)

为什么80%的码农都做不了架构师&#xff1f;>>> #Standalone AngularJS application In the real world applications, it is usually required to integrate the third party APIs into the project. Most of case, the third party service is provided as flexi…

无代码的时代真的来了吗?

所谓“无代码”&#xff0c;并不是不存在代码&#xff0c;无代码平台的开发&#xff0c;给开发者更大的挑战、更多 机会。所以&#xff0c;“无代码”不是解放程序员&#xff0c;而是给程序员提出了更高的要求、带来更大的挑战。作者 | 函子科技陆继恒&#xff0c;Jessica Tang…

实现自适应高度

天修改一个用Excel的报表,有一个数据格是跨两列,一般单格的数据格用自动换行就可以实现自适应高度,但是跨列是不行的.查找google良久,也没发现适合的办法,一阵头痛之后,突然有了灵感,于是马上做试验,还真的实现了.先写出来,如果有困于这个问题的朋友偶然路过,或许还能有一点用处…

分表分库之一:分布式数据库的常见用法

尽量减少事务边界 采用分库分表的方式将业务数据拆分后&#xff0c;如果每条SQL语句中都能带有分库分表键&#xff0c;分布式服务层对于SQL解析后都能精准地将这条SQL语句推送到该数据所在的数据库上执行&#xff0c;数据库将执行的结果再返回给分布式服务层&#xff0c;分布式…

打印自定义纸张大小

长江支流说的办法保留太多了,结果不行,很多类都是他在程序集里自定义的,源码又没公开 不过还是要感谢他的提示 今天和小陈搞了一天,他在国外的论坛上看到了一篇文章得到了启示,最后我们在凌晨3点终于把自定义纸张的代码给写出来了,看来必须用API,微软的.NET对打印的支持太菜了…

看完 50000 张专辑封面,AI 设计师开始疯狂输出

西班牙艺术家利用 StyleGAN2 打造了一个 AI 设计师&#xff0c;借助 50000 张图像自学成才&#xff0c;没想到培养一个印象派设计师这么简单。作者 | 三羊来源 | HyperAI超神经头图 | 网友整理抄袭事件的对比图也许是有些设计太经典出挑&#xff0c;总是让人情不自禁地模仿。日…

XenApp_XenDesktop_7.6实战篇之八:申请及导入许可证

1. 申请许可证Citrix XenApp_XenDesktop7.6和XenServer 6.5申请许可证的步骤是一致的&#xff0c;由于之前我已经申请过XenApp_XenDesktop的许可证&#xff0c;本次以XenServer6.5的许可证申请为例。1.1 在申请试用或购买Citrix产品时&#xff0c;收到相应的邮件&#xff0c;其…

使用Windows操作系统的13个窍门

Windows操作系统的13个使用窍门&#xff0c;很适用。 1.删除Windows下不让删除的文件 有时想删除某个文件&#xff0c;系统会告诉无法删除&#xff0c;换到DOS下或是安全模式虽然可以删除&#xff0c;但是有点麻烦。这时可以用鼠标右键点击回收站&#xff0c;选择“属性”将“回…

如何让机器像人一样多角度思考?协同训练来帮你

作者 | 宁欣头图 | 下载于视觉中国出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;本文目录0. 摘要1. 引言2. 协同训练介绍3. 协同训练改进3.1 基于视图划分的协同训练3.2 基于学习器差异化的协同训练3.3 基于标签置信度的协同训练4. 协同训练应用5. 总结与展望摘要协…

PHP设计模式(4)命令链模式

命令链 模式以松散耦合主题为基础&#xff0c;发送消息、命令和请求&#xff0c;或通过一组处理程序发送任意内容。每个处理程序都会自行判断自己能否处理请求。如果可以&#xff0c;该请求被处理&#xff0c;进程停止。您可以为系统添加或移除处理程序&#xff0c;而不影响其他…

MFC界面库BCGControlBar v25.3新版亮点:Gauge Controls

2019独角兽企业重金招聘Python工程师标准>>> 亲爱的BCGSoft用户&#xff0c;我们非常高兴地宣布BCGControlBar Professional for MFC和BCGSuite for MFC v25.3正式发布&#xff01;新版本添加了对Visual Studio 2017的支持、增强对Windows 10的支持等。接下来几篇文…

如何使用 ASP.NET 实用工具加密凭据和会话状态连接字符串

文章编号:329290最后修改:2006年4月10日修订:8.0 重要说明&#xff1a;本文包含有关如何修改注册表的信息。修改注册表之前&#xff0c;一定要先进行备份&#xff0c;并且一定要知道在发生问题时如何还原注册表。有关如何备份、还原和修改注册表的更多信息&#xff0c;请单击下…

16款小米新品,刚刚雷军只发了5款

会前&#xff0c;雷军在微博上提前疯狂剧透小米即将发布的新品信息。要发布的产品实在太多了&#xff0c;整合提前发布的信息来看&#xff0c;此次发布会可能会是小米有史以来时间跨度最长、新品数量最多的新品发布会&#xff0c;包括小米11 Pro/Ultra、小米MIX新机、小米11青春…

windows下使用aspell开启emacs的单词拼写检查功能

第一步,你需要下载aspell安装文件和至少一个字典,下载地址为http://aspell.net/win32/. 下载之后,分别安装aspell和字典. 需要注意的是,在64位的WIN7下,“C:\Program Files (x86)”是32位安装程序的默认安装目录,而“C:\Program Files"是64位安装程序的默认安装目录,因此a…

老板来了:人脸识别 + 手机推送,老板来了你立刻知道!

背景介绍 学生时代&#xff0c;老师站在窗外的阴影挥之不去。大家在玩手机&#xff0c;看漫画&#xff0c;看小说的时候&#xff0c;总是会找同桌帮忙看着班主任有没有来。 一转眼&#xff0c;曾经的翩翩少年毕业了&#xff0c;新的烦恼来了&#xff0c;在你刷知乎&#xff0c;…

在C#中使用COM+实现事务控制

.NET技术是微软大力推广的下一代平台技术&#xff0c;自从.NET技术架构的正式发布&#xff0c;此项技术也逐渐走向成熟和稳定。按照微软的平台系统占有率&#xff0c;我们不难想象得到&#xff0c;在未来的一两年内.NET技术必定会势如破竹一般的登上主流的技术平台&#xff0c;…

深入理解 JavaScript 中的 replace 方法

2019独角兽企业重金招聘Python工程师标准>>> 字符串替换字符串 1 I am loser! .replace( loser , hero ) //I am hero! 直接使用字符串能让自己从loser变成hero&#xff0c;但是如果有2个loser就不能一起变成hero了。 1 I am loser,You are loser .replace( loser ,…

透过计算机视觉,看看苏伊士运河堵船

作者 | Edison_G来源 | 计算机视觉研究院头图 | 下载于视觉中国3月29日&#xff0c;长赐号终于重新上浮。船运代理公司Inchcape和苏伊士运河管理局皆证实&#xff0c;长赐号已经重新漂浮在水面上&#xff0c;但目前还不清楚需要多少时间重启运河。这张图片&#xff0c;相信大家…