Java流式思想和方法引用
Java流式思想和方法引用
1. Stream流
1.1 概述
传统集合的多步遍历代码
/*
传统的方式遍历集合,过滤集合,打印输出满足条件的集合
*/
import java.util.ArrayList;
public class Demo01LIst {
public static void main(String[] args) {
// 创建list集合,存储姓名
ArrayList<String> list = new ArrayList<>();
list.add("张无忌");
list.add("赵敏");
list.add("周芷若");
list.add("张强");
list.add("张三丰");
ArrayList<String> listA = new ArrayList<>();
for (String s : list) {
// 对list集合过滤,将张字开头的添加到listA集合中
if (s.startsWith("张")) {
listA.add(s);
}
}
ArrayList<String> listB = new ArrayList<>();
for (String s : listA) {
// 对listA集合过滤,只要名字长度为3的
if (s.length() == 3) {
listB.add(s);
}
}
// 遍历listB集合,打印输出
for (String s : listB) {
System.out.println(s);
}
}
}
Stream的更优写法
import java.util.ArrayList;
/*
使用Stream流的方式,遍历集合,对集合中的数据进行过滤
Stream流式JDK1.8之后出现的
关注的是做什么,而不是怎么做
*/
public class Demo02Stream {
public static void main(String[] args) {
// 创建list集合,存储姓名
ArrayList<String> list = new ArrayList<>();
list.add("张无忌");
list.add("赵敏");
list.add("周芷若");
list.add("张强");
list.add("张三丰");
// 对list集合过滤,将张字开头的添加到listA集合中
// 对listA集合过滤,只要名字长度为3的
// 遍历listB集合,打印输出
list.stream()
.filter(name -> name.startsWith("张"))
.filter(name -> name.length() == 3)
.forEach(name -> System.out.println(name));
}
}
1.2 流式思想的概述
注意:请暂时忘记对传统IO流固有的印象!
filter
、 map
、 skip
都是在对函数模型进行操作,集合元素并没有真正被处理。只有当终结方法 count
执行的时候,整个模型才会按照指定策略执行操作。而这得益于Lambda的延迟执行特性。
备注:“Stream流”其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何 元素(或其地址值)
Stream(流)是一个来自数据源的元素队列
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式,流可以直接调用遍历方法。
当使用一个流的时候,通常包括三个基本步骤:获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。
1.3 获取流
- java.util.stream.Stream
<T>
是 Java 8 新加入的最常用的流接口。(这并不是一个函数式接口。)
获取一个流非常简单,有以下几种常用的方式:
所有的
Collection
集合都可以通过stream
默认方法获取流。- default Stream
<E>
stream()
- default Stream
Stream接口的静态方法 of,可以获取数组对应的流。
- static
<T>
Stream<T>
of(T... values) - 参数是一个可变参数,那么我们就可以传递一个数组。
- static
示例:
/*
获取stream流的两种方式
1. 所有Collection集合都可以通过默认方法 stream获取流
2. 可以使用Stream接口中的静态方法,of,将数组转换成流对象
*/
import java.util.*;
import java.util.stream.Stream;
public class Demo03GetStream {
public static void main(String[] args) {
// 把集合转换为Stream流
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();
Map<String, String> map = new HashMap<>();
// 获取键,存储到set集合中
Set<String> keySet = map.keySet();
Stream<String> stream3 = keySet.stream();
// 获取值,存储到Collection集合
Collection<String> values = map.values();
Stream<String> stream4 = values.stream();
// 获取键值对(键与值的映射关系 entrySet)
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> stream5 = entries.stream();
// 把数组转换为Stream流
Stream<Integer> stream6 = Stream.of(1, 2, 3, 4, 5);
// 可变参数可以传递数组
Integer[] arr = {1, 3, 4, 5};
Stream<Integer> stream7 = Stream.of(arr);
String[] arr2 = {"a", "b", "c", "d"};
Stream<String> stream8 = Stream.of(arr2);
}
}
1.4 常用方法
流模型的操作很丰富,这些方法可以被分成两种:
- 延迟方法:返回值类型仍然是
Stream
接口自身类型的方法,因此支持链式调用。(除终结方法外,其余方法均为延迟方法。) - 终结方法:返回值类型不再是
Stream
接口自身类型的方法,因此不再支持类似StringBuilder
那样的链式调用。本小节中,终结方法包括count
和forEach
方法。
①逐一处理:forEach
虽然名字叫 forEach,但是与for循环中的 for-each不同。
- void forEach(Consumer<? super T> action);
- 该方法接收一个 Consumer接口函数,会将每一个流元素交给该函数进行处理。
- Consumer 接口是一个消费型的函数式接口,可以传递 Lambda表达式,消费数据。
简单记:
forEach方法,用来遍历流中的数据,是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法。
- 示例:
import java.util.stream.Stream;
public class Demo04ForEach {
public static void main(String[] args) {
// 获取一个Stream流
Stream<String> stream = Stream.of("迪丽热巴", "古力娜扎", "马儿扎哈");
// 使用forEach,参数传递Lambda表达式
/*stream.forEach((String name) -> {
System.out.println(name);
});*/
// 优化Lambda表达式
stream.forEach(name -> System.out.println(name));
}
}
/*
print result:
迪丽热巴
古力娜扎
马儿扎哈
*/
②过滤:filter
filter:用于对 Stream流中的数据进行过滤。
Stream filter(Predicate<? super T> predicate) ;
filter方法的参数 Predicate是一个函数式接口,所以可以传递 Lambda表达式,对数据进行过滤。
Predicate中的抽象方法:
boolean test(T t);
示例:
import java.util.stream.Stream;
// Stream<T> filter(Predicate<? super T> predicate);
public class Demo05Filter {
public static void main(String[] args) {
// 获取Stream流
Stream<String> stream = Stream.of("张三丰", "张三疯", "周芷若", "赵敏", "张翠山");
// 使用 filter方法进行过滤
// stream.filter((String name) -> {name.startsWith("张");});
// 使用 filter之后,返回一个新的流
Stream<String> newStream = stream.filter(name -> name.startsWith("张"));
// 逐一输出
newStream.forEach(name -> System.out.println(name));
/*
Stream流属于管道流,只能被消费(使用)一次
第一个Stream流调用完毕,数据就会流到下一个Stream上
而这时第一个Stream流已经使用完毕,就会关闭了
所以第一个Stream流就不能再调用方法了
IllegalStateException: stream has already been operated upon or closed
*/
// 已经被使用一次了,再次使用会抛出异常
// stream.forEach(name -> System.out.println(name));
}
}
③映射:map
- 如果需要将流中的元素映射到另一个流中,可以使用 map方法。
<R>
Stream<R>
map(Function<? super T,? extends R> mapper) ;- 该接口需要一个 Function函数式接口参数,可以将当前流中的 T类型数据转换为另一种 R类型的流。
- Function中的抽象方法:
R apply(T t);
- 示例:
import java.util.stream.Stream;
public class Demo06Map {
public static void main(String[] args) {
// 获取一个String类的Stream流
Stream<String> stream = Stream.of("1", "2", "3", "4");
// 将String类型的整数,转换(映射)为 Integer类型的整数
Stream<Integer> stream2 = stream.map((String s) -> {
return Integer.parseInt(s);
});
// 遍历 stream2流
stream2.forEach(num -> System.out.println(num));
}
}
④统计个数:count
正如旧集合 Collection
当中的 size
方法一样,Stream
提供 count
方法来获取其中元素的个数。
count:用于统计Stream流中元素的个数。
long count();
count方法是一个终结方法,返回值是一个 long类型的整数,所以不能再调用 Stream流中的其他方法了。
示例:
import java.util.ArrayList;
import java.util.stream.Stream;
public class Demo07Count {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
Stream<Integer> stream = list.stream();
long count = stream.count();
System.out.println(count);
}
}
⑤取用前几个:limit
limit:用于截取流中的元素。
limit方法可以对流进行截取,只取用前 n 个。
Stream<T> limit(long maxSize);
- 参数是一个 long类型,如果集合当前长度大于参数,则进行截取;否则不进行操作。
limit方法是一个 延迟方法,只是对流中的元素进行截取,返回的是一个新的流,所以可以继续调用 Stream流中的其他方法。
示例:
import java.util.Arrays;
import java.util.stream.Stream;
public class Demo08Limit {
public static void main(String[] args) {
String[] arr = {"喜羊羊", "美羊羊", "暖羊羊", "灰太狼", "红太狼"};
Stream<String> stream = Arrays.stream(arr);
Stream<String> stream2 = stream.limit(3); // 截取前3个元素,并返回新的Stream流
stream2.forEach(name -> System.out.println(name));
}
}
⑥跳过前几个:skip
- skip:用于跳过元素;
- 如果希望跳过前几个元素,可以使用 skip方法获取一个截取之后的新流。
- Stream
<T>
skip(long n);- 如果流的长度大于 n,则跳过前 n个;否则将会得到一个长度为 0 的空流。
import java.util.stream.Stream;
public class Demo09Skip {
public static void main(String[] args) {
String[] arr = {"喜羊羊", "美羊羊", "暖羊羊", "灰太狼", "红太狼"};
Stream<String> stream = Stream.of(arr);
// 跳过前3个
Stream<String> stream2 = stream.skip(3);
stream2.forEach(name -> System.out.println(name));
}
}
⑦组合:concat
- concat:用于把流组合到一起。
- 如果有两个流,希望合并成一个流,那么可以使用
Stream
接口中的静态方法concat
- public static Stream concat(Stream<? extends T> a, Stream<? extends T> b)
- 示例:
import java.util.stream.Stream;
/*
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
*/
public class Demo10Concat {
public static void main(String[] args) {
String[] arr = {"喜羊羊", "美羊羊", "暖羊羊", "灰太狼", "红太狼"};
Stream<String> stream1 = Stream.of(arr);
Stream<String> stream2 = Stream.of("张三丰", "张三疯", "周芷若", "赵敏", "张翠山");
// 把以上两个流组合成为一个流
Stream<String> concat = Stream.concat(stream1, stream2);
// 遍历输出
concat.forEach(name -> System.out.println(name));
}
}
1.5 练习:集合元素处理(传统方式)
import java.util.ArrayList;
/*
题目:
现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
练习:集合元素处理(传统方式)
现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
5. 将两个队伍合并为一个队伍;存储到一个新集合中。
6. 根据姓名创建Person对象;存储到一个新集合中。
7. 打印整个队伍的Person对象信息。
*/
public class DemoStreamTest {
public static void main(String[] args) {
//第一支队伍
ArrayList<String> one = new ArrayList<>();
one.add("迪丽热巴");
one.add("宋远桥");
one.add("苏星河");
one.add("石破天");
one.add("石中玉");
one.add("老子");
one.add("庄子");
one.add("洪七公");
// 1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
ArrayList<String> oneA = new ArrayList<>();
for (String name : one) {
if (name.length() == 3) {
oneA.add(name);
}
}
// 2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
ArrayList<String> oneB = new ArrayList<>();
for (int i = 0; i < 3; i++) {
oneB.add(oneA.get(i));
}
//第二支队伍
ArrayList<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("张无忌");
two.add("赵丽颖");
two.add("张三丰");
two.add("尼古拉斯赵四");
two.add("张天爱");
two.add("张二狗");
// 3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
ArrayList<String> twoA = new ArrayList<>();
for (String name : two) {
if (name.startsWith("张")) {
twoA.add(name);
}
}
// 4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
ArrayList<String> twoB = new ArrayList<>();
for (int i = 2; i < twoA.size(); i++) {
twoB.add(twoA.get(i));
}
// 5. 将两个队伍合并为一个队伍;存储到一个新集合中。
ArrayList<String> all = new ArrayList<>();
all.addAll(twoB);
all.addAll(oneB);
// 6. 根据姓名创建Person对象;存储到一个新集合中。
ArrayList<Person> list = new ArrayList<>();
for (String s : all) {
list.add(new Person(s));
}
for (Person person : list) {
System.out.println(person);
}
}
}
1.6 练习:集合元素处理(Stream流方式)
import java.util.ArrayList;
import java.util.stream.Stream;
/*
题目:
现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
练习:集合元素处理(传统方式)
现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
5. 将两个队伍合并为一个队伍;存储到一个新集合中。
6. 根据姓名创建Person对象;存储到一个新集合中。
7. 打印整个队伍的Person对象信息。
*/
public class DemoStreamTest2 {
public static void main(String[] args) {
//第一支队伍
ArrayList<String> one = new ArrayList<>();
one.add("迪丽热巴");
one.add("宋远桥");
one.add("苏星河");
one.add("石破天");
one.add("石中玉");
one.add("老子");
one.add("庄子");
one.add("洪七公");
// 1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
// 2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
Stream<String> oneStream = one.stream().filter(name -> name.length() == 3).limit(3);
//第二支队伍
ArrayList<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("张无忌");
two.add("赵丽颖");
two.add("张三丰");
two.add("尼古拉斯赵四");
two.add("张天爱");
two.add("张二狗");
// 3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
// 4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
Stream<String> twoStream = two.stream().filter(name -> name.startsWith("张")).skip(2);
// 5. 将两个队伍合并为一个队伍;存储到一个新集合中。
// 6. 根据姓名创建Person对象;存储到一个新集合中。
// 7. 打印整个队伍的Person对象信息。
Stream.concat(oneStream, twoStream).map(name -> new Person(name)).forEach(name-> System.out.println(name));
}
}
2. 方法引用
2.1 基本介绍
双冒号::
为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。
- Printable接口
/*
定义一个打印的函数式接口
*/
@FunctionalInterface
public interface Printable {
// 对字符串的抽象方法
public abstract void print(String s);
}
- Demo01Printable.java
public class Demo01Printable {
// 定义一个方法,参数传递Printable接口,对字符串进行打印
public static void printString(Printable p) {
p.print("HelloWorld");
}
public static void main(String[] args) {
printString((s) -> {
System.out.println(s);
});
/*
分析:
Lambda表达式的目的:打印参数传递的字符串
把参数 s 传递给 System.out对象,调用 out对象中的方法println对字符串输出
使用前提(注意):
1.System.out对象已经存在。
2.println方法也是存在的。
所以我们可以直接使用方法引用来优化 Lambda表达式
可以使用System.out方法直接引用(调用)println方法
*/
printString(System.out::println);
}
}
语义分析
例如上例中,System.out
对象中已经有了一个重载print(String x)
方法恰好就是我们需要的,那么对于printString
方法的函数式接口参数,对比下面的两种写法,完全等效:
- Lambda写法:s -> System.out.println(s);
- 方法引用写法:System.out::println();
- 第一个种语义是指:拿到参数之后经Lambda之手,继而传递给
System.out.println()
方法去处理。 - 第二种等效的语义是指:直接让
System.out
中的println
方法来取代Lambda。两种写法的执行效果完全一样,而第二种方法引用复用已有的方案,更加简洁。
注意事项:
Lambda中传递的参数一定式方法引用中的方法可以接收的类型,否则会抛出异常。
2.2 通过对象名引用【成员方法】
- Printable接口
/*
定义一个打印的函数式接口
*/
@FunctionalInterface
public interface Printable {
// 对字符串的抽象方法
public abstract void print(String s);
}
- MethodRefObject.java
public class MethodRefObject {
// 定义一个成员方法,参数传递字符串,将字符串大写输出
public void printUpperCaseString(String s) {
System.out.println(s.toUpperCase());
}
}
- DemoObjectMethodReference.java
public class DemoObjectMethodReference {
// 定义一个方法,参数传递Printable接口
public static void printString(Printable p) {
p.print("Hello Java");
}
public static void main(String[] args) {
printString((s) -> {
MethodRefObject obj = new MethodRefObject();
obj.printUpperCaseString(s);
});
/*
使用方法引用对Lambda进行优化
使用前提:对象和成员方法已经存在
*/
// 创建对象
MethodRefObject obj = new MethodRefObject();
printString(obj::printUpperCaseString);
}
}
2.3 通过类名称引用【静态方法】
- Calcable接口
public interface Calcable {
// 定义一个抽象方法,参数传递一个整数,进行取模运算
public int methodAbs(int num);
}
- DemoStaticMethodRef.java
public class DemoStaticMethodRef {
// 定义一个方法,参数传递一个int型整数,一个Calcalbe接口
public static int method(int num, Calcable c) {
return c.methodAbs(num);
}
public static void main(String[] args) {
// 调用method方法,传递一个整数,取模运算
int num = method(-10, (n) -> {
return Math.abs(n);
});
System.out.println(num);
/*
通过类名称引用静态成员方法的前提:
1. 类存在
2. 静态成员方法存在且已经实现了
*/
int num2 = method(-100, Math::abs);
System.out.println(num2);
}
}
2.4 通过super引用父类的普通成员方法
- Greetable接口
// 定义一个问候的接口
public interface Greetable {
public abstract void greet();
}
- Fu.java
public class Fu {
// 定义一个sayHello方法
public void sayHello() {
System.out.println("我是师父");
}
}
- Zi.java
/*
通过super引用父类的普通成员方法
*/
public class Zi extends Fu {
// 覆盖重写父类的 sayHello方法
@Override
public void sayHello() {
System.out.println("我是徒弟");
}
// 定义一个方法,参数传递Greetable接口
public void method(Greetable g) {
g.greet();
}
// 展示输出的方法,参数传递一个Greetable接口
public void show() {
/*method(() -> {
Fu fu = new Fu();
fu.sayHello();
});*/
// method(() -> super.sayHello());
method(super::sayHello);
}
}
- main方法
public class DemoMain {
public static void main(String[] args) {
new Zi().show();
}
}
2.5 通过this引用本类的普通成员方法
- Richable接口
public interface Richable {
// 定义一个买东西的方法
public abstract void buy();
}
- Husband.java
public class Husband {
// 定义买房子的方法
public void buyHouse() {
System.out.println("杭州一套小楼!");
}
// 定义结婚的方法, 参数传递Richable接口
public void marry(Richable r) {
r.buy();
}
// 高兴方法,因为要结婚
public void soHappy() {
// this.marry(() -> {this.buyHouse();});
/*
this和调用的本类成员方法已经存在且实现
可以使用this引用本类的成员方法
*/
marry(this::buyHouse);
}
public static void main(String[] args) {
new Husband().soHappy();
}
}
2.6 类的构造器(构造方法)引用
- PersonBuilder接口
@FunctionalInterface
public interface PersonBuilder {
// 定义方法,用来根据传递的姓名,创建Person对象,并返回
public abstract Person builderPerson(String name);
}
- DemoPerson.java
public class DemoPerson {
// 定义一个方法,参数传递姓名和PersonBuilder接口,打印对象姓名
public static void printName(String name, PersonBuilder pb) {
Person person = pb.builderPerson(name);
System.out.println(person);
}
public static void main(String[] args) {
// printName("迪丽热巴", (s) -> new Person(s));
/*
使用方法引用优化Lambda表达式
构造方法:new Person(String name);已知
创建对象:new 已知
就可以使用Person引用new创建对象
*/
printName("周元", Person::new); // 使用Person类的带参构造方法,创建对象
}
}
2.7 数组的构造器引用
- ArrayBuilder接口
/*
数组创建接口
*/
@FunctionalInterface
public interface ArrayBuilder {
// 根据参数传递的长度,创建int型指定长度的数组
public abstract int[] builderArray(int length);
}
- DemoArray.java
import java.util.Arrays;
public class DemoArray {
/*
定义一个方法,参数传递要创建数组的长度和ArrayBuilder接口
方法内部传递的长度使用ArrayBuilder中的方法创建数组并返回
*/
public static int[] creatArray(int length, ArrayBuilder ab) {
return ab.builderArray(length);
}
public static void main(String[] args) {
int[] arrA = creatArray(10, (len) -> new int[len]);
System.out.println(Arrays.toString(arrA));
System.out.println(arrA.length);
/*
创建的是 int[] 类型的数组
数组的长度已知
使用 int[]引用new,根据传递的长度创建数组
*/
int[] arrB = creatArray(10, int[]::new);
System.out.println(arrB.length);
}
}
Java流式思想和方法引用的更多相关文章
- Java学习:Stream流式思想
Stream流 Java 8 API添加了一种新的机制——Stream(流).Stream和IO流不是一回事. 流式思想:像生产流水线一样,一个操作接一个操作. 使用Stream流的步骤:数据源→转换 ...
- 流式思想概述和两种获取Stream流的方式
流式思想概述 整体来看,流式思想类似于工厂车间的生产流水线 当需要对多个元素进行操作(特别是多步操作)的时候,考虑到性能及便利性,我们应该首先拼好一个模型步骤方案,然后再按照方法去执行他 这张图中展示 ...
- 实现 MyBatis 流式查询的方法
基本概念流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果.流式查询的好处是能够降低内存使用.如果没有流式查询,我们想要从数据库取 1000 万条记录而又没有足 ...
- JAVA流式布局管理器--JAVA基础
JAVA流式布局管理器的使用: FlowLayoutDeme.java: import java.awt.*;import javax.swing.*;public class FlowLayoutD ...
- NSStream 流式思想
流式思想的本质是将数据或信号看作流.流的管理者NSStream看作管道. 内容包含两方面: 1.流的建立:源.目的地: 2.流的管理:状态事件与数据事件. 本质上是建立联系.处理数据.处理状态.
- Effective Java 第三版——43.方法引用优于lambda表达式
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Java8新特性 Stream流式思想(二)
如何获取Stream流刚开始写博客,有一些不到位的地方,还请各位论坛大佬见谅,谢谢! package cn.com.zq.demo01.Stream.test01.Stream; import org ...
- Java 8新特性-4 方法引用
对于引用来说我们一般都是用在对象,而对象引用的特点是:不同的引用对象可以操作同一块内容! Java 8的方法引用定义了四种格式: 引用静态方法 ClassName :: staticMetho ...
- JAVA 流式布局管理器
//流式布局管理器 import java.awt.*; import javax.swing.*; public class Jiemian2 extends JFrame{ //定义组件 JBut ...
随机推荐
- Java中都通用文件下载(ContentType、文件头、response、out四步骤)
Java中都通用文件下载(ContentType.文件头.response.out四步骤) 新浪微博:IT国子监(记得关注噢) http://weibo.com/itguozijian 我们就直接 ...
- 数据结构和算法(Golang实现)(24)排序算法-优先队列及堆排序
优先队列及堆排序 堆排序(Heap Sort)由威尔士-加拿大计算机科学家J. W. J. Williams在1964年发明,它利用了二叉堆(A binary heap)的性质实现了排序,并证明了二叉 ...
- vue的组件缓存(返回页面不刷新)
每次使用返回是页面总是会刷新 导致了一些体验上的不愉快 现在 发现vue中的一个很方便的方法还可以用来优化性能就是: keep-alive缓存组件 <router-view v-if=" ...
- 浅谈Vector
浅谈Vector 在之前的文章中,我们已经说过线程不安全的ArrayList和LinkedList,今天我们来讲讲一个线程安全的列表容器,他就是Vector,他的底层和ArrayList一样使用数组来 ...
- python从零开始基础入门——开发环境搭建
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:山海皆可平z PS:如有需要Python学习资料的小伙伴可以加点击下方 ...
- Daily Scrum 12/17/2015
Process: Zhaoyang:完成了相册图片的异步加载. Yandong&Dong: 对Azure的体系架构进行学习和相应的编程. Fuchen: 对Oxford计划中的NLP接 ...
- C++ 11 +,开坑。
最近换新工作了.工作中需要用到高端的c++11的一些操作,至于我后面又plus一下还是因为可能是c++14或者17中提供的一些操作.反正都是c++11以及之后的一些特性. 首先,今天看一下关于函数模板 ...
- mysql搭建亿级cmd5数据库,毫秒级查询 完全过程
前言: 最近也在玩数据库,感觉普通机子搞数据库,还是差了点,全文查找,慢的要查一分钟更久. 但是搞cmd5库很不错,亿级数据库,毫秒级. qq 944520563好吧,下面开始,首先你得需要一个mys ...
- 【LeetCode】57. Insert Interval [Interval 系列]
LeetCode中,有很多关于一组interval的问题.大体可分为两类: 1.查看是否有区间重叠: 2.合并重叠区间; 3.插入新的区间: 4. 基于interval的其他问题 [ 做题通用的关键 ...
- weblogic漏洞(一)----CVE-2017-10271
WebLogic XMLDecoder反序列化漏洞(CVE-2017-10271) 0x01 漏洞原因: Weblogic的WLS Security组件对外提供webservice服务,其中使用了XM ...