Chapter 1. Java 8: why should you care?

1.1 Java 怎么还在变

某些语言只是更适合某些方面。比如,C和C++仍然是构建操作系统和各种嵌入式系统的流行工具,因为它们编出的程序尽管安全性不佳,但运行时占用资源少。Java和C#等安全型语言在诸多运行资源不太紧张的应用中已经取代了前者。

1.1.1 Java 在编程语言生态系统中的位置

Java虚拟机(JVM)及其字节码 可能会变得比Java语言本身更重要,而且对于某些应用来说, Java可能会被同样运行在JVM上的 竞争对手语言(如Scala或Groovy)取代。

Java是怎么进入通用编程市场的?

封装原则使得其软件工程问题比C少,“一次编写,随处运行”

程序员越来越多地要处理所谓的大数据,并希望利用多核计算机或计算集群来有效地处理。这意味着需要使用并行处理——Java以前对此并不支持。

1.1.2 流处理

cat file1 file2 | tr "[A-Z]" "[a-z]" | sort | tail -3

sort把一个行流作为输入,产生了另一个行流(进行排序)作为输出。请注意在Unix中,命令( cat、 tr、sort和tail)是同时执行的,这样sort就可以在cat或tr完成前先处理头几行。

好处:

思路变成了把这样的流变成那样的流(就像写数据库查询语句时的那种思路),而不是一次只处理一个项目。

把输入的不相关部分拿到几个CPU内核上去分别执行你的Stream操作流水线

1.1.3 用行为参数化把代码传递给方法

让sort方法利用自定义的顺序进行比较。你可以写一个compareUsingCustomerId来比较两张发票的代码,而非写一个新Comparator对象

1.1.4 并行与共享的可变数据

并行:同时对不同的输入安全地执行。一般情况下这就意味着,你写代码时不能访问共享的可变数据。但如果要写入的是一个共享变量或对象,这就行不通了

这两个要点(1.1.4没有共享的可变数据,1.1.3将方法和函数即代码传递给其他方法的能力)是函数式编程范式的基石

1.1.5 Java 需要演变

1.2 Java 中的函数

值是Java中的一等公民,但其他很多Java概念(如方法和类等)则是二等公民。用方法来定义类很不错,类还可以实例化来产生值,但方法和类本身都不是值。在运行时传递方法能将方法变成一等公民,这在编程中非常有用。函数式编程中的函数的主要意思是“把函数作为一等值”,不过它也常常隐含着第二层意思,即“执行时在元素之间无互动”

1.2.1 方法和 Lambda 作为一等公民

旧:把isHidden包在一个FileFilter对象里,然后传递给File.listFiles方法

File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
public boolean accept(File file) {
return file.isHidden();
}
});



File[] hiddenFiles = new File(".").listFiles(File::isHidden);

(int x) -> x + 1

1.2.2 传递代码:一个例子

public static boolean isGreenApple(Apple apple) {
return "green".equals(apple.getColor());
}
public static boolean isHeavyApple(Apple apple) {
return apple.getWeight() > 150;
}
public interface Predicate<T>{
boolean test(T t);
} //平常只要从java.util.function导入就可以了
static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (p.test(apple)) {
result.add(apple);
}
}
return result;
}

调用时filterApples(inventory, Apple::isGreenApple);

1.2.3 从传递方法到 Lambda

甚至都不需要为只用一次的方法写定义

filterApples(inventory, (Apple a) -> "green".equals(a.getColor()) );

filterApples(inventory, (Apple a) -> a.getWeight() < 80 || "brown".equals(a.getColor()) );

1.3 流

import static java.util.stream.Collectors.toList;
Map<Currency, List<Transaction>> transactionsByCurrencies =
transactions.stream()
.filter((Transaction t) -> t.getPrice() > 1000)
.collect(groupingBy(Transaction::getCurrency));

多线程并非易事: 线程可能会同时访问并更新共享变量。因此,如果没有协调好,数据可能会被意外改变。Java 8也用Stream API(java.util.stream)解决了这两个问题:集合处理时的套路和晦涩,以及难以利用多核。

Collection主要是为了存储和访问数据,而Stream则主要用于描述对数据的计算。

顺序处理:

import static java.util.stream.Collectors.toList;
List<Apple> heavyApples =
inventory.stream().filter((Apple a) -> a.getWeight() > 150)
.collect(toList());

并行处理:

import static java.util.stream.Collectors.toList;
List<Apple> heavyApples =
inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150)
.collect(toList());

1.4 默认方法

主要是为了支持库设计师,让他们能够写出更容易改进的接口。

List调用sort方法。它是用Java 8 List接口中如下所示的默认方法实现的,它会调用Collections.sort静态方法:

default void sort(Comparator<? super E> c) {
Collections.sort(this, c); //this指Collections的具体变量
}

1.5 来自函数式编程的其他好思想

Java中从函数式编程中引入的两个核心思想:将方法和Lambda作为一等值,以及在没有可变共享状态时,函数或方法可以有效、安全地并行执行。

其他:

Optional<T>类,如果你能一致地使用它的话,就可以帮助你避免出现NullPointer异常。

(结构)模式匹配,Java 8对模式匹配的支持并不完全

Chapter 2. Passing code with behavior parameterization

行为参数化,就是把方法或者方法内部的行为作为参数传入另一方法中。

2.1 应对不断变化的需求

背景:有一“列”苹果,需要根据不同标准(颜色,重量等)来筛选。当标准不断变化,如何处理?

下面是错误示范

public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<Apple>();
for(Apple apple: inventory){
if( "green".equals(apple.getColor() ) {//然后根据需求改这一行,或许有必要在上面增加引入的变量,如要判断color是否为red
result.add(apple);
}
}
return result;
} //使用(符合更多标准时)
List<Apple> greenApples = filterApples(inventory, "green", 0, true);

这种方法实现不同需求,要写大量重复代码(只有评论和参数引入不同)

2.2 行为参数化

进一步,写接口,每个标准写一个并实现该接口的方法,最后再写实现需求的方法。之后就可以使用了....

这种方法灵活,但依旧啰嗦

//必写(当然,有的可以引用),可抽象为Predicate<T>
public interface ApplePredicate{
boolean test (Apple apple);
}
//可省
public class AppleHeavyWeightPredicate implements ApplePredicate{
public boolean test(Apple apple){
return apple.getWeight() > 150;
}
}
//必写,写法可能不一样(看2.4),另外可抽象为public static <T> List<T> filter(List<T> list, Predicate<T> p)
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p){
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if(p.test(apple)){
result.add(apple);
}
}
return result;
}
//可简,使用(记得要new)
List<Apple> redAndHeavyApples = filterApples(inventory, new AppleRedAndHeavyPredicate());

2.3 简化

1.匿名类(简化一小步)

和上面一样,先写好接口和实现需求的方法,然后就可以用了。在用时再写上判断标准...

List<Apple> redApples = filterApples(inventory, new ApplePredicate() {//接口名同样记得new,下面实现接口。如果实现中有this,则指该实现ApplePredicate
public boolean test(Apple apple){
return "red".equals(apple.getColor());
}
})

2.Lambda 表达式(一大步,Java 8特征)

在接口处直接写所需参数,通过->后写行为

List<Apple> result = filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));

2.4 真实的例子(暂时不需深究)

Java API包含很多可以用不同行为进行参数化的方法,如下面三种

1.用 Comparator 来排序

public interface Comparator<T> {
public int compare(T o1, T o2);
} inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}); inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));

2.用 Runnable 执行代码块

public interface Runnable{
public void run();
} Thread t = new Thread(new Runnable() {
public void run(){
System.out.println("Hello world");
}
}); Thread t = new Thread(() -> System.out.println("Hello world"));

3.GUI 事件处理

Button button = new Button("Send");
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
}); button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));

Chapter 3. Lambda expressions

3.1 Lambda in a nutshell

根据chapter2中匿名方式和Lambda的比较,我们可以的出Lambda表达式实际是表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型。(可能还有一个可以抛出的异常列表)

理论上来说,你在Java 8之前做不了的事情, Lambda也做不了。

基本语法

(parameters) -> expression一个string也是表达式

(parameters) -> { statements; }加上花括号,里面就要写语句,返回要return

一些例子

//1
(String s) -> s.length()
//2
(Apple a) -> a.getWeight() > 150
//3
(int x, int y) -> {
System.out.println("Result:");
System.out.println(x+y);
}
//4
() -> 42
//5
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
//6
() -> {}
//7
() -> new Apple(10)

3.2 在哪里以及如何使用 Lambda

1.函数式接口

在函数式接口上使用Lambda表达式。函数式接口就是只定义一个抽象方法的接口,如:

public interface Predicate<T>{
boolean test (T t);
}

当某个方法用Predicate<T>作为参数时,就可以用Lambda了

Lambda表达式可以被赋给一个变量,或传递给一个接受函数式接口作为参数的方法,如

Runnable r1 = () -> System.out.println("Hello World 1");

process(() -> System.out.println("Hello World 3")); process接受Runnable参数

tip:如果你用@FunctionalInterface定义了一个接口,而它却不是函数式接口的话,编译器将返回一个提示原因的错误。

2.函数描述符

函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种函数式接口的抽象方法(如下面的() -> void)的签名叫作函数描述符。

//Lambda签名为() -> void,和Runnable的相同
execute(() -> {});
public void execute(Runnable r){
r.run();
}
//可以在定义实践方法(指实现要求的主体方法)时直接用Lambda,签名为() -> String,和fetch相同
public Callable<String> fetch() {
return () -> "Tricky example ;-)";
}
//签名为(Apple) -> Intege,Predicate不同;在实际当中其实不需要=及左部分
Predicate<Apple> p = (Apple a) -> a.getWeight();

3.3 实战

环绕执行( execute around) 模式: 资源处理(例如处理文件或数据库)时一个常见的模式就是打开一个资源,做一些处理,然后关闭资源。这个设置和清理阶段总是很类似,并且会围绕着执行处理的那些重要代码。

下面代码只能读文件的第一行。如果你想要返回头两行,甚至是返回使用最频繁的词,该怎么办呢

第一步,适合一种行为的方法:

public static String processFile() throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
return br.readLine();
}
}//Java 7中的带资源的try语句,它已经简化了代码,因为你不需要显式地关闭资源了

第二步,思考一个接口,返回什么,这个返回又是通过什么得到的。下面返回头两行,需要有br,即BufferedReader

String result = processFile((BufferedReader br) -> br.readLine() + br.readLine());

写接口

@FunctionalInterface
public interface BufferedReaderProcessor {
String process(BufferedReader b) throws IOException;//和上面签名一致,接受BufferedReader -> String,还可以抛出IOException异常
}

第三步,修改实践方法

public static String processFile(BufferedReaderProcessor p) throws IOException {
try (BufferedReader br =
new BufferedReader(new FileReader("data.txt"))) {
return p.process(br);
}
}

第四步,运用

String twoLines = processFile((BufferedReader br) -> br.readLine() + br.readLine());

3.4 预设的函数式接口

//1.Predicate接受泛型T对象,并返回一个boolean,方法为test
//需求:表示一个涉及类型T的布尔表达式
//生成的Predicate<T>还能调用.and(), .or, .negate方法
@FunctionalInterface
public interface Predicate<T>{
boolean test(T t);
}
//实际当中,引用上面就可以直接用了
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();//与Predicate.isEmpty(s)相同?即使s为空也能工作。 //2.Consumer接受泛型T对象,没有返回(void),方法为accept。
//需求:访问类型T的对象,并对其执行某些操作
//其他方法andThen
@FunctionalInterface
public interface Consumer<T>{
void accept(T t);
} //3.Function接受一个泛型T的对象,并返回一个泛型R的对象,方法为apply
//需求:将输入对象的信息映射到输出
//其他方法andThen, compose, identity
@FunctionalInterface
public interface Function<T, R>{
R apply(T t);
}
public static <T, R> List<R> map(List<T> list, Function<T, R> f) {
List<R> result = new ArrayList<>();
for(T s: list){
result.add(f.apply(s));
}
return result;
}
// 将["lambdas","in","action"]转化为[7, 2, 6]
List<Integer> l = map(
Arrays.asList("lambdas","in","action"),
(String s) -> s.length()
);
//其他接口(详情看java.util.function,或core_java_for_the_impatient 3.6)
Runable ()-> void .run
Comparator<T,T,Integer> (T, T) -> int
Supplier<T> ()->T .get
UnaryOperator<T> T->T
BinaryOperator<T> (T,T)->T .apply
BiPredicate<L,R> (L,R)->boolean
BiConsumer<T,U> (T,U)->void
BiFunction<T,U,R> (T,U)->R

2.原始类型特化

boxing: Java里将原始类型转换为对应的引用类型的机制。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。

Predicate<Integer>就会把参数1000装箱到一个Integer对象中,用IntPredicate可避免。一般来说,针对专门的输入参数类型的函数式接口的名称都要加上对应的原始类型前缀,比如DoublePredicate、 IntConsumer、 LongBinaryOperator、 IntFunction等。当然,输出也有。

不同的接口可是实现相同的Lambda,但效率可能不一样

3.关于异常

当没有办法自己创建一个接口时,比如Function<T, R>,可用下面的显式catch

Function<BufferedReader, String> f = (BufferedReader b) -> {
try {
return b.readLine();
}
catch(IOException e) {
throw new RuntimeException(e);
}
};

3.5 类型检查、类型推断以及限制

1.类型检查

实践方法所需参数,参数中的接口(目标类型Predicate<Apple>),接口中的抽象方法,接受什么和返回什么,与Lambda比较是否匹配

如果Lambda表达式抛出一个异常,那么抽象方法所声明的throws语句也必须与之匹配

2.同样的 Lambda,不同的函数式接口

如果一个Lambda的主体是一个语句表达式, 它就和一个返回void的函数描述符兼容(当然需要参数列表也兼容)。

// 合法,尽管Consumer返回了一个void
Consumer<String> b = s -> list.add(s);

3.类型推断

有时候显式写出类型更易读,有时候去掉它们更易读。

4.使用局部变量(类的方法中的变量)

int portNumber = 1337;//必须显式声明为final,或事实上是final
Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;//再加段就无法捕捉。这一限制不鼓励你使用改变外部变量的典型命令式编程模式(会阻碍很容易做到的并行处理

3.6 方法引用

inventory.sort((Apple a1, Apple a2)
-> a1.getWeight().compareTo(a2.getWeight()));
//使用方法引用和java.util.Comparator.comparing)
inventory.sort(comparing(Apple::getWeight));

1.In a nutshell

如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。如Apple::getWeight就是引用了Apple类中定义的方法getWeight。

实际是针对仅仅涉及单一方法的Lambda的语法糖

方法引用主要有三类

//1.指向静态方法(Integer::parseInt)
(String s) -> Integer.parseInt(s)//Integer::parseInt //2.指向实例方法
//第一个参数为方法接收者
(x, y) ->
x.compareToIgnoreCase(y) //String::compareToIgnoreCase //3.指向现有对象的实例方法
//调用一个已经存在的外部对象中的方法
(args) -> expr.instanceMethod(args)//expr::instanceMethod // 例子
() -> Thread.currentThread().dumpStack() //Thread.currentThread()::dumpStack
(String s) -> System.out.println(s) //System.out::println

2.构造函数引用

//默认构造
Supplier<Apple> c1 = Apple::new; //() -> new Apple()
Apple a1 = c1.get(); //其他构造
Function<Integer, Apple> c2 = Apple::new;//(weight) -> new Apple(weight);
Apple a2 = c2.apply(110);
//对于三个及以上参数来构造的,要自己写一个interface
public interface TriFunction<T, U, V, R>{
R apply(T t, U u, V v);
}

注意不同接口,相同的引用。只有在调用接口方法时才不同

//上文的map可以如下引用,让map根据一个list来构造不同的object
public static List<Apple> map(List<Integer> list,
Function<Integer, Apple> f){
List<Apple> result = new ArrayList<>();
for(Integer e: list){
result.add(f.apply(e));
}
return result;
} List<Integer> weights = Arrays.asList(7, 3, 4, 10);
List<Apple> apples = map(weights, Apple::new);

一个有趣的实现:使用Map来将构造函数(Function<Integer, Fruit>)映射到字符串值。这样就可以通过string和构造所需参数获得相应的构造出来的对象

static Map<String, Function<Integer, Fruit>> map = new HashMap<>();
static {
map.put("apple", Apple::new);
map.put("orange", Orange::new);
// etc...
}
public static Fruit giveMeFruit(String fruit, Integer weight){
return map.get(fruit.toLowerCase())
.apply(weight);

3.7 Lambda 和方法引用实战

用不同的排序策略给一个Apple列表排序

1.传递代码

void sort(Comparator<? super E> c)

它需要一个Comparator对象来比较两个Apple!这就是在Java中传递策略的方式:它们必须包裹在一个对象里。

//普通方法
public class AppleComparator implements Comparator<Apple> {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator()); //匿名方法
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}); //Lambda(根据Comparator的函数描述符(T, T) -> int)
inventory.sort((Apple a1, Apple a2)//不写Apple也可
-> a1.getWeight().compareTo(a2.getWeight())
); //更进一步
//Comparator具有一个叫作comparing的静态辅助方法,它可以接受一个能提取Comparable键值的Function,并生成一个Comparator对象
Comparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());//注意你现在传递的Lambda只有一个参数: Lambda说明了如何从苹果中提取需要比较的键值
//或
import static java.util.Comparator.comparing;
inventory.sort(comparing((a) -> a.getWeight())); //最终
inventory.sort(comparing(Apple::getWeight));

逆向思路:对List<Apple>排序,由于Apple不是Comparator,所以需要用Comparator包裹,而产生Comparator的方式可以是1.comparing(提取Comparable键值的function),恰好Apple本身就有.getWeight,而且只需一个function,所以为comparing(Apple::getWeight)

3.8 复合 Lambda 表达式的有用方法

1.比较器复合

Comparator接口的一些默认方法:

逆序reversed

comparing(Apple::getWeight).reversed()

比较器链thenComparing

inventory.sort(comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple::getCountry));

2.谓词(一个返回boolean的函数)复合

Predicate接口的一些默认方法:

非negate

Predicate<Apple> notRedApple = redApple.negate();

链(从左向右确定优先级的)

Predicate<Apple> redAndHeavyAppleOrGreen =
redApple.and(a -> a.getWeight() > 150)
.or(a -> "green".equals(a.getColor()));

3.函数复合

Function接口的一些默认方法:

andThen:从左往右f —> g

compose:从右往左f <— g

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2; Function<Integer, Integer> h = f.andThen(g);//g(f(x))
int result = h.apply(1);//返回4 Function<Integer, Integer> h = f.compose(g);//f(g(x))
int result = h.apply(1);//返回3 // 另一个例子
Function<String, String> addHeader = Letter::addHeader;
Function<String, String> transformationPipeline=
addHeader.andThen(Letter::checkSpelling)
.andThen(Letter::addFooter);

Java 8 实战 P1 Fundamentals的更多相关文章

  1. selenium2 Webdriver + Java 自动化测试实战和完全教程

    selenium2 Webdriver + Java 自动化测试实战和完全教程一.快速开始 博客分类: Selenium-webdriverselenium webdriver 学习selenium ...

  2. Java日志实战及解析

    Java日志实战及解析 日志是程序员必须掌握的基础技能之一,如果您写的软件没有日志,可以说你没有成为一个真正意义上的程序员. 为什么要记日志? •       监控代码 •       变量变化情况, ...

  3. Java编程实战宝典PDF (中文版带书签)

    Java编程实战宝典PDF 目录 第1篇 Java基础知识入门第1章 Java的开发运行环境( 教学视频:57分钟)1.1 Java运行原理与Java虚拟机1.1.1 Java运行原理简述1.1.2 ...

  4. 《Java 8实战》读书笔记系列——第三部分:高效Java 8编程(四):使用新的日期时间API

    https://www.lilu.org.cn/https://www.lilu.org.cn/ 第十二章:新的日期时间API 在Java 8之前,我们常用的日期时间API是java.util.Dat ...

  5. Java反射实战

    一.背景 最近的项目中需要使用到Java 反射的知识,以前不怎么了解,也基本没怎么用过,抽出一片时间,来具体学习和实战下Java的反射!拿来和大家分享以及记录方便以后学习! 二.反射相关概念解析 1. ...

  6. Java 8 实战 P3 Effective Java 8 programming

    目录 Chapter 8. Refactoring, testing, and debugging Chapter 9. Default methods Chapter 10. Using Optio ...

  7. 《selenium2 Java 自动化测试实战(第二版)》 更新2016.5.3

    java 版来了!! 本文档在<selenium2 Python 自动化测试实战>的基础上,将代码与实例替换为java ,当然,部分章节有变更.这主要更语言本身的特点有关.集合和java下 ...

  8. 【Java】实战Java虚拟机之五“开启JIT编译”

    今天开始实战Java虚拟机之五“开启JIT编译” 总计有5个系列 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟机之三“G1的新生代GC” 实战Ja ...

  9. Java BTrace实战(1)--BTrace的入门和使用

    前言: 对线上的java服务, 往往采用日志进行问题处理和分析. 倘若日志缺乏相关的信息时, 那又该如何处理? 远程调试会影响服务的正常工作, 修改代码重新部署的方案其实时性和灵活性难以保证(线上服务 ...

随机推荐

  1. 集合Set、List、Map的遍历方法

    package com.shellway.javase; import java.util.ArrayList; import java.util.Collection; import java.ut ...

  2. async await 同步方法调用异步方法死锁

    同步方法调用异步方法.GetAwaiter().GetResult()计算函数超时,异步方法所有的回调操作都会期望返回到主线程. 所以会导致各种线程死锁.异步方法中使用ConfigureAwait(f ...

  3. transactoin

    hibernate对数据的操作是封装在事务当中,并且默认是非自动提交方式.所以用session保存对象时,如果不开启事务,并且手工提交事务,对象并不会真正保存在数据库中.

  4. QQ空间里写的开发心得

    不回头看一眼还真没发现我已经写过这么多开发心得日志. 理一理设备数据走向  https://user.qzone.qq.com/1156740846/blog/1522292793 action的生命 ...

  5. LINUX-查看进程内环境变量

    ps -ef find PID cat /proc/$PID/environ | grep ENV

  6. mysql跟java时间类型转换

    参照这个就行了,这个对应注入类型.===========java注入数据库==========java类型 mysql类型 成功与否date date yesdate time nodate time ...

  7. Jmeter读取excel表中用例数据实现接口压测

    传统的接口测试,都是在接口中手动输入不同用例准备的多种场景参数数据,一遍一遍的输入来执行多个不同的用例,但是现在利用excel表格准备各种类型的数据,使用Jmeter中Jmeter CSV Data ...

  8. 【Mail.Ru Cup 2018 Round 2 B】 Alice and Hairdresser

    [链接] 我是链接,点我呀:) [题意] [题解] 因为只会增加. 所以. 一开始暴力算出来初始答案 每次改变一个点的话. 就只需要看看和他相邻的数字的值就好. 看看他们是不是大于l 分情况增加.减少 ...

  9. ES6的let和var声明变量的区别

    关于let的描述 let允许你声明一个作用域被限制在块级中的变量.语句或者表达式.与var关键字不同的是,它声明的变量只能是全局或者整个函数块的. 作用域规则 let声明的变量只在其声明的块或子块中可 ...

  10. 最小生成树prime算法模板

    #include<stdio.h> #include<string.h> using namespace std; int map[505][505]; int v, e; i ...