概述
Java8 (又称 JKD1.8) 是 Java 语言开发的一个主要版本。
Oracle公司于2014年3月18日发布Java8 。
- 支持Lambda表达式
- 函数式接口
- 新的Stream API
- 新的日期 API
- 其他特性
一、Lambda表达式
1.1概述
Lambda是一种特殊的匿名内部类,语法更简洁
允许吧函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递
1.2语法
<函数式接口> <变量名> = (参数1,参数2…) -> {
// 方法体
};
Lambda引入了新的操作符:->(箭头操作符),->将表达式分成两部分:
- 左侧:(参数1,参数2…)表示参数列表
- 右侧:{ }内部是方法体
注意事项:
- 形参列表的数据类型会自动推断。
- 如果形参列表为空,只需保留() 。
- 如果形参只有1个,()可以省略,只需要参数的名称即可。
- 如果执行语句只有一句,且无返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有一句。
- [Lambda不会生成一个单独的内部类文件。
二、方法的引用
2.1概念
方法引用来自官方的说明
You use lambda expressions to create anonymous methods. Sometimes, however, a lambda expression does nothing but call an existing method. In those cases, it's often clearer to refer to the existing method by name. Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.
你使用lambda表达式创建匿名方法。 但是,有时lambda表达式除了调用现有方法外什么也不做。 在这种情况下,通常更容易按名称引用现有方法。 方法引用使你可以执行此操作; 它们是紧凑的,对于已经具有名称的方法lambda表达式更易于阅读。
- 方法引用是Lambda表达式的一种简写形式。
- 如果Lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。
2.2语法
方法引用符 -----> “::”
双冒号::
为方法引用符,而它所在的表达式被称为方法引用。如果Lambda表达式赋值的方法已经在某个类中有具体的实现,那么则可以通过双冒号来引用该方法作为Lambda表达式的替代者。
案例:
public interface Actor {
/**
* 演员表演节目
*/
void perform(String item);
}
public class ActorTest {
public static void main(String[] args) {
// 使用匿名内部类实现
Actor actor1 = new Actor() {
@Override
public void perform(String item) {
System.out.println(item);
}
};
actor1.perform("跳舞");
System.out.println("---------------------");
// 使用lambda的形式
Actor actor2 = item -> System.out.println(item);
actor2.perform("唱歌");
System.out.println("--------------------");
// 方法引用
/*
这个接口方法的实现,本质来说就是调用了System类中out对象的println方法
JDK8新特性 - 方法引用符 :: 双冒号
语法规则:
成员方法引用: 对象名::方法名
静态方法引用: 类名::方法名
this this::方法名
supper supper::方法名
*/
Actor actor = System.out::println; // 成员方法引用
actor1.perform("弹钢琴");
}
}
分析:
上面的示例中,Lambda表达式的作用就是调用System.out
中的println(String msg)
方法,这个方法已经有具体的实现。
使用方法引用进一步简化代码
总结:
方法引用与Lambda表达式一样,只能应用于函数式接口。方法有静态方法、成员方法和构造方法之分,方法引用因此也分为静态方法引用、成员方法引用和构造方法引用
2.3静态方法引用
语法:
类名::方法名
案例:
// 计算器接口
public interface Calculator {
int calculate(int a,int b);
}
public class MathUtil {
public static int add(int a, int b){
return a + b;
}
public static int minus(int a, int b){
return a - b;
}
public static int multiply(int a, int b){
return a * b;
}
public static int divided(int a, int b){
return a / b;
}
}
public class CalculatorTest {
public static void main(String[] args) {
// Calculator c = new Calculator() {
// @Override
// public int calculate(int a, int b) {
// return MathUtil.minus(a, b);
// }
// };
// Calculator c = (int a, int b) -> {
// return MathUtil.minus(a, b);
// };
// Calculator c = (a, b) -> MathUtil.minus(a, b);
Calculator c = MathUtil::minus;
int result = c.calculate(1, 10);
System.out.println(result);
Calculator c1 = MathUtil::multiply;
int result1 = c1.calculate(1, 10);
System.out.println(result1);
}
}
//
public class CalculatorTest {
public static void main(String[] args) {
// 使用匿名内部类实现
Calculator c1 = new Calculator() {
@Override
public int calculate(int a, int b) {
return Integer.sum(a,b);// 求和
}
};
System.out.println("c1 : " + c1.calculate(1,2));
System.out.println("---------------------");
// 这里接口的实现只是调用了Integer类提供的静态方法sum,可以实现代码简化
// 使用lambda实现
Calculator c2 = (a, b) -> Integer.sum(a, b);
System.out.println("c2 : " + c2.calculate(3,4));
System.out.println("---------------------");
// 使用方法引用
Calculator c3 = Integer::sum;
System.out.println("c3 : " + c3.calculate(5,6));
System.out.println("---------------------");
// 比较器接口 Comparator
// 匿名内部类实现
Comparator<Double> comparator1 = new Comparator<Double>() {
@Override
public int compare(Double o1, Double o2) {
return Double.compare(o1,o2); // o1大于o2返回1 ,o1小于o2返回-1,等于比较复杂
/*
// Cannot use doubleToRawLongBits because of possibility of NaNs.
long thisBits = Double.doubleToLongBits(d1);
long anotherBits = Double.doubleToLongBits(d2);
return (thisBits == anotherBits ? 0 : // Values are equal
(thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
1)); // (0.0, -0.0) or (NaN, !NaN)
*/
}
};
System.out.println("小于的情况: " + comparator1.compare(1.0,10.0));
System.out.println("大于的情况: " + comparator1.compare(6.0,5.0));
System.out.println("等于的情况: " + comparator1.compare(1.0,1.0));
System.out.println("---------------------");
// 使用方法引用
Comparator<Double> comparator2 = Double::compare;
System.out.println("小于的情况: " + comparator2.compare(1.0,10.0));
System.out.println("大于的情况: " + comparator2.compare(6.0,5.0));
System.out.println("等于的情况: " + comparator2.compare(0.0,0.0));
}
}
2.4成员方法引用
语法:
对象名:: 方法名
案例:
public interface Printable {
// 打印信息
void print(String msg);
}
public class StandardConsole {
public void print(String msg){
System.out.println("控制台打印" + msg);
}
}
public class PrintableTest {
public static void main(String[] args) {
// 匿名内部类实现
Printable p1 = new Printable() {
@Override
public void print(String msg) {
new StandardConsole().print(msg);
}
};
p1.print("方法实现1");
// 方法引用
Printable p2 = new StandardConsole()::print;
p2.print("成员方法引用实现2");
}
}
2.5this引用成员方法
语法:
this::方法名
案例:
// 照相机解耦
public interface Camera {
// 拍照方法
void takePhoto(String name);
}
// 人类
public class Person {
public void dance(){
System.out.println("跳舞");
}
public void sing(String s){
System.out.println(s);
}
public void run(){
System.out.println("跑步");
}
public void takePhoto(String name){
System.out.println("给" + name + "拍照");
}
public void travel(String name) {
Camera c1 = new Camera() {
@Override
public void takePhoto(String name) {
Person.this.takePhoto(name);
}
};
// 成员方法引用
Camera c2 = Person.this::takePhoto;
// this方法引用
Camera c3 = this::takePhoto;
c3.takePhoto(name);
}
}
public class PersonTest {
public static void main(String[] args) {
Person p = new Person();
p.travel("中国");
}
}
2.6super引用父类成员方法
语法:
super::方法名
案例:
public interface Customer {
// 交流业务
void communicateBusiness();
}
public class SoftEngineer {
public void analysBusyness(){
System.out.println("软件工程师分析业务");
}
}
public class JavaProgammer extends SoftEngineer{
// 重写方法
public void analysBusyness(){
System.out.println("java程序员与客户交流业务");
}
// Java程序员与客户交流业务
// 匿名内部类形式
public void communicateWithCustomer() {
Customer c1 = new Customer() {
@Override
public void communicateBusiness() {
JavaProgammer.super.analysBusyness();
}
};
// super方法引用
Customer c2 = super::analysBusyness;
c2.communicateBusiness();
}
}
public class JavaProgammerTest {
public static void main(String[] args) {
JavaProgammer javaProgammer = new JavaProgammer();
javaProgammer.communicateWithCustomer();
javaProgammer.analysBusyness();
}
}
2.7构造方法引用
语法:
类名::new
案例:
public class Student {
private String name;
private String sex;
public Student() {
}
public Student(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
public interface StudentBuilder {
Student build(String name,String sex);
}
public class StudentBuilderTest {
public static void main(String[] args) {
StudentBuilder builder1 = new StudentBuilder() {
@Override
public Student build(String name, String sex) {
return new Student(name,sex);
}
};
Student s1 = builder1.build("张三","男");
System.out.println("匿名方法1" + s1);
StudentBuilder builder2 = Student::new; // 构造方法引用
Student s2 = builder2.build("李四","女");
System.out.println("成员方法: " + s2);
}
}
三、函数式接口
3.1. 什么是函数式接口
A functional interface is any interface that contains only one abstract method. (A functional interface may contain one or more default methods or static methods.) Because a functional interface contains only one abstract method, you can omit the name of that method when you implement it.
函数式接口是仅包含一种抽象方法的任何接口。 (一个函数式接口可能包含一个或多个默认方法或静态方法。)由于一个函数式接口仅包含一个抽象方法,因此在实现该方法时可以省略该方法的名称。
示例:
package com.qf.functional;
@FunctionalInterface
public interface Hello {
void sayHello(String name);
static void show(){} //JDK8新特性
default void print(){} //JDK8新特性
private void test(){} // JDK9特性
}
JDK8
专门为函数式接口提供了一个注解标识@FunctionalInterface
,该注解只能使用在接口类型的定义上,表明这是一个函数式接口,编译器在编译时就是会对该接口进行检测:接口中是否只有一个抽象接口方法。如果有多个抽象接口方法或者一个抽象接口方法也没有,则将报编译错误
3.2Consumer接口
Consumer
顾名思义就是消费者的意思。可以消费一个被接收到的数据,至于如何消费,就需要看这个接口被如何实现。
// 源码
@FunctionalInterface
public interface Consumer<T> {
void accept(T t); // 接受一个被消费的数据
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
案例:
/**
* 函数接口 - 消费接口,通常会被应用在集合的遍历上
* 如何消费,看具体的时实现
*/
public class ConsumerTest {
public static void main(String[] args) {
// 第一种,匿名内部类的形式
Consumer<String> c1 = new Consumer<String>(){
@Override
public void accept(String s) {
System.out.println(s + "这个字符串被消费了");
}
};
c1.accept("abcd");
System.out.println("-------------------");
// 第二种使用lambda
Consumer<String> c2 = (String s) -> {
System.out.println(s + "这个字符串被消费了");
};
c2.accept("efgh");
System.out.println("-------------------");
// 第三种 lambda的省略版
Consumer<String> c3 = s -> System.out.println(s + "这个字符串被消费了");
c3.accept("ijkl");
System.out.println("-------------------");
// 集合List Consumer接口
List<String> names = Arrays.asList("张三", "莉莉丝", "王五");
// 第一lambda遍历
names.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("-------------------");
// 第二种lambda遍历
names.forEach((String s) -> {
System.out.println(s);
});
System.out.println("-------------------");
// 第三种lambda遍历
names.forEach(s -> System.out.println(s));
System.out.println("-------------------");
// 第四种lambda遍历 方法引用符的使用 ::
names.forEach(System.out::println);
}
}
3.3BiConsumer接口
BiConsumer
也是一个消费者,只是这个消费者可以一次性消费两个数据(一般是键值对)。至于如何消费,就需要看这个接口被如何实现。
// 源码
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u); // 接受两个被消费的数据
default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
Objects.requireNonNull(after);
return (l, r) -> {
accept(l, r);
after.accept(l, r);
};
}
}
// 函数接口 - BiConsumer 一次消费两个数据的消费接口 一般用于map 实现键值对的,具体消费看如何实现
public class BiConsumerTest {
public static void main(String[] args) {
// 第一种使用匿名内部类的形式
BiConsumer<String,Double> consumer01 = new BiConsumer<String, Double>() {
@Override
public void accept(String s, Double d) {
System.out.println(s + "==>" + d);
}
};
consumer01.accept("张三",10.0);
System.out.println("----------------------------");
// 第二种使用lambda的第一种形式
BiConsumer<String,Double> consumer02 = (String s,Double d) -> {
System.out.println(s + "==>" + d);
};
consumer02.accept("李四",20.0);
System.out.println("----------------------------");
// 第三种使用lambda的第二种形式
BiConsumer<String, Double> consumer03 = (s,d) -> System.out.println(s + "==>" + d);
consumer03.accept("王五",40.0);
System.out.println("----------------------------");
// BiConsume中的 HashMap
System.out.println("BiConsumer中的HashMap");
HashMap<String, Double> map = new HashMap<>();
map.put("张三",10.0);
map.put("李四",20.0);
map.put("王五",30.0);
// 第一种,匿名内部类的形式
// HashMap的遍历
map.forEach(new BiConsumer<String, Double>() {
@Override
public void accept(String s, Double d) {
System.out.println(s + "==>" + d);
}
});
System.out.println("----------------------------");
//第二种,使用lambda的第一种形式
map.forEach((String s,Double d) -> {
System.out.println(s + "==>" + d);
});
System.out.println("----------------------------");
// 第三种,使用lambda的第二种形式
map.forEach((s,d) -> System.out.println(s + "==>" + d));
}
}
3.4Predicate接口
Predicate
是条件的意思,可以检测给定数据是否满足条件,也可以与其他条件进行衔接。至于如何检测,就需要看这个接口被如何实现。
// 源码
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
// and
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
// 取反
default Predicate<T> negate() {
return (t) -> !test(t);
}
// 或
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
// 相等
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
案例:
/*
Predicate接口测试:Preadicate表示条件,所需的条件
条件之间的衔接 (|| or)与 (&& and)非 (!negate)
boolean test(T t);//检测是否满足条件
default Predicate<T> and(Predicate<? super T> other);//条件之间的逻辑与衔接
default Predicate<T> negate();//条件取反
default Predicate<T> or(Predicate<? super T> other);//条件之间的逻辑或衔接
*/
public class PredicateTest {
public static void main(String[] args) {
// 第一种方式 匿名内部类
Predicate<Integer> p1 = new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer > 10; // 自定义条件
}
};
System.out.println("p1: " + p1.test(20));
System.out.println("-----------------------");
// 第二种方式 lambda的形式1
Predicate<Integer> p2 = (Integer integer) -> {
return integer > 10;
};
System.out.println("p2: " + p2.test(20));
System.out.println("-----------------------");
// 第二种方式 lambda的形式2
Predicate<Integer> p3 = num -> num > 10;
Predicate<Integer> p4 = num -> num < 20;
Predicate<Integer> p5 = num -> num < 50;
System.out.println("p3: " + p3.test(20));
System.out.println("p4: " + p4.test(20));
System.out.println("p5: " + p5.test(20));
System.out.println("-----------------------");
// Prediate 条件衔接 && || ! 与或非
// &&
Predicate<Integer> p6 = p3.and(p4); // num > 10 && num < 20
System.out.println("p6: " + p6.test(20));
System.out.println("-----------------------");
// ||
Predicate<Integer> p7 = p3.or(p4); // num > 10 || num < 20
System.out.println("p7: " + p7.test(20));
System.out.println("-----------------------");
// 取反1
Predicate<Integer> p8 = p3.negate(); // num <= 10
System.out.println("p8: " + p8.test(20));
System.out.println("-----------------------");
// 取反2
Predicate<String> p9 = s -> s.contains("abc"); // num <= 10
System.out.println("p9: " + p9.test("abc"));
Predicate<String> p10 = p9.negate();
System.out.println("p10: " + p10.test("abc"));
System.out.println("-----------------------");
// 调用show方法
show();
}
// 按要求打印集合中的元素
public static void show(){
// 创建集合
List<String> names = new ArrayList<String>();
names.add("青鸢");
names.add("赤鸢");
names.add("青鸾");
names.add("赤鸾");
// 匿名内部类实现要求
Predicate<String> p1 = new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("青"); // 第一字符为青
}
};
// lambda 实现
Predicate<String> p2 = s -> s.startsWith("赤");// 第一字符为赤
names.forEach(s -> {
if (p1.test(s)){ // 按p1 的条件执行
System.out.println(s);
}
if (p2.test(s)){ // 按 p2 的条件执行
System.out.println(s);
}
});
}
}
3.5Function接口
Function
是功能的意思,可以将一种数据类型的对象转换为另一种数据类型的对象,至于如何转换,就需要看这个接口被如何实现。
// 源码
@FunctionalInterface
public interface Function<T, R> {
R apply(T t); // 将此函数应用于给定的参数
/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
// 接着
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* Returns a function that always returns its input argument.
* @param <T> the type of the input and output objects to the function
* @return a function that always returns its input argument
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
案例:
/*
函数式接口 - Function 测试
数据类型的转换
*/
public class FunctionTest {
public static void main(String[] args) {
// 原类型 目标类型
Function<String, Integer> f1 = new Function<String, Integer>() {
@Override // 匿名内部类实现
public Integer apply(String s) {
return Integer.parseInt(s); // 字符串转数字
}
};
System.out.println("f1: " + f1.apply("1234"));
System.out.println("---------------------------");
// lambda 实现
Function<String, Integer> f2 = s -> Integer.parseInt(s);
System.out.println("f2: " + f2.apply("4567"));
System.out.println("---------------------------");
// Function 的andThen方法
// 完成转换后接着执行 - 案例:字符串转数字后进行平方运算
Function<Integer,Long> f3 = number -> Long.valueOf(number * number);
Function<String,Long> f4 = f2.andThen(f3);
System.out.println("f3: " + f3.apply(8));
System.out.println("f4: " + f4.apply("9"));
System.out.println("---------------------------");
}
}
练习:
现有文本存储学生信息如下: - student.txt
谢霆锋,男,37
刘德华,男,52
郭富城,男,46
张学友,男,40
要求将学生信息从文本中读取出来并转换为学生对象,然后存储在集合中。
public class FunctionExercise {
public static void main(String[] args) {
// 路径
String path = "d:/imgs/student.txt";
// 按字符串读取
// 匿名内部类实现 Function
System.out.println("按字符串读取:");
Function<String,Student> f1 = new Function<String, Student>() {
@Override
public Student apply(String s) {
return new Student(s.split(","));
}
};
List<Student> students1 = readStudent1(path,f1); // 读取文本
students1.forEach(System.out::println); // 遍历打印
System.out.println("------------------------------");
// 使用lambda
Function<String,Student> f2 = s -> new Student(s.split(","));
List<Student> students2 = readStudent1(path,f1);
students2.forEach(System.out::println);
System.out.println("------------------------------");
// 按字符串数组读取
// 匿名内部类实现
System.out.println("按字符串数组读取:");
Function<String[],Student> f3 = new Function<String[], Student>() {
@Override
public Student apply(String[] strings) {
return new Student(strings);
}
};
List<Student> students3 = readStudent2(path,f3);
students3.forEach(System.out::println);
System.out.println("------------------------------");
// 方法引用实现
Function<String[],Student> f4 = Student::new;
List<Student> students4 = readStudent2(path,f4);
students4.forEach(System.out::println);
}
// 读取文本方法 按字符串读
public static List<Student> readStudent1(String path,Function<String,Student> function){
ArrayList<Student> students = new ArrayList<>();
// JDK7新特性 try()
try (FileReader reader = new FileReader(path);
BufferedReader br = new BufferedReader(reader)){
String line;
while ((line = br.readLine()) != null){
Student stu = function.apply(line);
students.add(stu);
}
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
return students;
}
// 按字符串数组读取
public static List<Student> readStudent2(String path,Function<String[],Student> function){
ArrayList<Student> students = new ArrayList<Student>();
try (FileReader reader = new FileReader(path);
BufferedReader br = new BufferedReader(reader)){
String line;
while ((line = br.readLine()) != null){
String[] arr = line.split(",");
Student stu = function.apply(arr);
students.add(stu);
}
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
return students;
}
}
// 学生类
public class Student {
private String name;
private char sex;
private int age;
// 无参构造
public Student() {
}
// 有参构造
public Student(String name, char sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex=" + sex +
", age=" + age +
'}';
}
}
3.6Supplier接口
供给型接口
案例:
public class SupplierTest {
public static void main(String[] args) {
// 生成随机数组
int[] arr1 = getNums(() -> new Random().nextInt(100),5);
System.out.println(Arrays.toString(arr1));
}
// 供给型接口
public static int[] getNums(Supplier<Integer> supplier,int count){
int[] arr = new int[count];
for (int i = 0; i < count; i++){
arr[i] = supplier.get();
}
return arr;
}
}
四、Stream
4.1概述
A pipeline is a sequence of aggregate operations.
管道就是一系列的聚合操作。
A pipeline contains the following components:
管道包含以下组件:
A source: This could be a collection, an array, a generator function, or an I/O channel.
源:可以是集合,数组,生成器函数或I / O通道。
Zero or more intermediate operations. An intermediate operation, such as filter, produces a new stream.
零个或多个中间操作。诸如过滤器之类的中间操作产生新的流。
A terminal operation. A terminal operation, such as forEach, produces a non-stream result, such as a primitive value (like a double value), a collection, or in the case of forEach, no value at all.
终结操作。终端操作(例如forEach)会产生非流结果,例如原始值(如双精度值),集合,或者在forEach的情况下根本没有任何值。
A stream is a sequence of elements. Unlike a collection, it is not a data structure that stores elements. Instead, a stream carries values from a source through a pipeline.
流是一系列元素。 与集合不同,它不是存储元素的数据结构。 取而代之的是,流通过管道携带来自源的值。
The filter operation returns a new stream that contains elements that match its predicate (this operation's parameter).
筛选器操作返回一个新流,该流包含与筛选条件(此操作的参数)匹配的元素。
4.2获取流的方式
Collection
接口
default Stream<E> stream();
Stream
接口
//获取流
public static<T> Stream<T> of(T... values);
//将两个流拼接形成一个新的流
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b);
4.3聚合操作和终结操作
Stream
接口
聚合操作
Stream<T> filter(Predicate<? super T> predicate);//根据给定的条件过滤流中的元素
<R> Stream<R> map(Function<? super T, ? extends R> mapper);//将流中元素进行类型转换
Stream<T> distinct();//去重
Stream<T> sorted();//排序,如果存储元素没有实现Comparable或者相关集合没有提供Comparator将抛出异常
Stream<T> limit(long maxSize);//根据给定的上限,获取流中的元素
Stream<T> skip(long n);//跳过给定数量的元素
IntStream mapToInt(ToIntFunction<? super T> mapper);//将流中元素全部转为整数
LongStream mapToLong(ToLongFunction<? super T> mapper);//将流中元素全部转为长整数
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);//将流中元素全部转为双精度浮点数
终结操作
void forEach(Consumer<? super T> action);//遍历操作流中元素
<A> A[] toArray(IntFunction<A[]> generator);//将流中元素按照给定的转换方式转换为数组
<R, A> R collect(Collector<? super T, A, R> collector);//将流中的元素按照给定的方式搜集起来
Optional<T> min(Comparator<? super T> comparator);//根据给定的排序方式获取流中最小元素
Optional<T> max(Comparator<? super T> comparator);//根据给定的排序方式获取流中最大元素
Optional<T> findFirst(); //获取流中第一个元素
long count();//获取流中元素数量
boolean anyMatch(Predicate<? super T> predicate);//检测流中是否存在给定条件的元素
boolean allMatch(Predicate<? super T> predicate);//检测流中元素是否全部满足给定条件
boolean noneMatch(Predicate<? super T> predicate);//检测流中元素是否全部不满足给定条件
案例:
public class StreamTest {
/**
* 流的基本操作
* @param args
*/
public static void main(String[] args) {
// 定义 数组的方式
Integer[] numbers = {1,2,3,4,5,6,7,8,9};
// 两种定义方法
Stream<Integer> s1 = Stream.of(1,2,3,4,5,6,7,8,9);
Stream<Integer> s2 = Stream.of(numbers);
// 过滤操作
Stream<Integer> numberStream1 = s2.filter(num -> num > 5); // 保留大于5的元素
// 进行一次终结操作 终结操作不会返回流,因此流不够再使用 遍历
numberStream1.forEach(System.out::println);
System.out.println("-------------------------");
// 集合的方式
List<Integer> list = Arrays.asList(numbers);
Stream<Integer> s3 = list.stream();
// Integre转Double
Stream<Double> doubleStream = s3.map(num -> num * 1.0);
doubleStream.forEach(System.out::println);
System.out.println("-------------------------");
// 流的去重
// 上面的流终结了,不能在使用,新定义一个流
Stream<Integer> s4 = Stream.of(1,2,1,5,5,2,3,5,1,6);
// 实现去重
Stream<Integer> distinctStream = s4.distinct(); // 此时s4就消亡了
distinctStream.forEach(System.out::println);
System.out.println("-------------------------");
// 流的排序操作
Stream<Integer> s5 = Stream.of(1,2,1,5,5,2,3,5,1,6);
// Stream<Integer> sorted = s5.sorted((o1, o2) -> o1 - o2);
// sorted.forEach(System.out::println);
// s4.sorted((o1, o2) -> o1 - o2).forEach(System.out::println); // 报错 - 状态异常
s5.sorted((o1, o2) -> o1 - o2).forEach(System.out::println); // 升序
// s5.sorted((o1, o2) -> o2 - o1).forEach(System.out::println); // 降序
System.out.println("-------------------------");
// Map 的方式 Map没有直接转换成Stream的方法
HashMap<String,Double> map = new HashMap<>();
map.put("张三",20.0);
map.put("李四",30.0);
map.put("王五",10.0);
// 拿键
Set<String> set = map.keySet();
Stream<String> s6 = set.stream();
s6.forEach(System.out::println);
System.out.println("-------------------------");
// 拿到键值
Stream<Map.Entry<String,Double>> s7 = map.entrySet().stream();
s7.forEach(System.out::println);
System.out.println("-------------------------");
// 获取给定的上限
Stream<Integer> s8 = Stream.of(numbers);
s8.limit(4).forEach(System.out::println); // 获取前4个元素
System.out.println("-------------------------");
// 跳过给定数量的元素
Stream<Integer> s9 = Stream.of(1,3,2,4,5,7,6,8);
s9.skip(2).forEach(System.out::println); // 从头跳过2个元素
System.out.println("-------------------------");
// 将流中的元素全部转换为String型
Stream<Integer> s10 = Stream.of(1,3,2,4,5,7,6,8);
// s10.map(new Function<Integer, String>() {
// @Override
// public String apply(Integer integer) {
// return "str: " + integer;
// }
// }).forEach(System.out::println);
s10.map((Integer integer) -> "str: " + integer).forEach(System.out::println);
System.out.println("-------------------------");
// 根据给定的排序方式获取流中最小元素
Optional<Integer> opt1 = Stream.of(1,2,3,4,5,6).min((o1, o2) -> o1 - o2);
if (opt1.isPresent()){
System.out.println("流中的最小值为: " + opt1.get());
}
System.out.println("-------------------------");
// Optional 容器 根据给定的排序方式获取流中最大元素
Optional<Integer> opt2 = Stream.of(1,2,3,4,5,6).max((o1, o2) -> o1 - o2);
if (opt2.isPresent()){
System.out.println("流中的最大值为: " + opt2.get());
}
System.out.println("-------------------------");
// 获取流中第一个元素,通常和filter配合使用
// 找到流中第一个奇数
// 实现思路 :首先将全部奇数找出,用filter过滤,然后去第一个数据元素
Optional<Integer> opt3 = Stream.of(2,2,1,3,5).filter(num -> num %2 != 0).findFirst();
Integer integer = opt3.orElse(-1);
System.out.println("流中第一个奇数为: " + integer);
System.out.println("-------------------------");
// 获取流中元素数量count
long count = Stream.of(1, 2, 3, 4, 5, 6, 7, 8).count();
System.out.println("流中的元素个数为: " + count);
System.out.println("-------------------------");
//anyMatch表示任何一个元素匹配即可
boolean anyMatch = Stream.of("user", "password").anyMatch(role -> "user".equals(role));
System.out.println("anyMatch的结果为: " + anyMatch);
System.out.println("-------------------------");
//noneMatch表示任何一个元素匹配即可
boolean noneMatch = Stream.of("user", "password").noneMatch(role -> "user".equals(role));
System.out.println("noneMatch的结果为: " + noneMatch);
System.out.println("-------------------------");
//allMatch表示任何一个元素匹配即可
boolean allMatch = Stream.of("user", "password").allMatch(role -> "user".equals(role));
System.out.println("noneMatch的结果为: " + allMatch);
System.out.println("-------------------------");
// 使用Stream流将一个基本数据类型的数组转换为包装类型的数组,
// 再将包装类型的数组转换基本数据类型数组。
Integer[] integers = {1,2,3,4,5,6};
// Integer转int
int[] ints = Arrays.stream(integers).mapToInt(Integer::intValue).toArray();
System.out.println(Arrays.toString(ints));
// int转Interger
// toArray将流中元素按照给定的转换方式转换为数组
Integer[] intes = Arrays.stream(ints).boxed().toArray(Integer[]::new);
System.out.println(Arrays.toString(intes));
System.out.println("-------------------------");
}
}
五、计时器
timer、timerTask
Timer是一种定时器工具,用来在一个后台线程计划执行指定任务。
它可以安排任务“执行一次”或者定期“执行多次
案例:
public class TimerTest {
// 计时器 计时器任务
public static void main(String[] args) {
// 使用原子类来记数
AtomicInteger counter = new AtomicInteger(0);
// 定时做什么事
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() { // 计时器任务要执行的
System.out.println("定时任务");
// 原子类实现自增1
int currentCount = counter.incrementAndGet();
if (currentCount == 10){ // 满足条件就取消定时任务
timer.cancel();// 取消定时任务
}
}
};
// 日历类Calendar
// Duration 一个时间方面的工具类,进行时间单位的计量转换
// 解决问题 - 从当前时间延迟多少毫秒是明天的9点
// delay 表示第一次执行任务的延迟时间,0表示不延迟,period表示任务循环执行的周期,单位是毫秒
// timer.schedule(task,0, Duration.ofDays(1).toMillis());
// 获取现在的时间
Long now = System.currentTimeMillis();
// 获取一个日历对象 , 默认时间就是现在
Calendar c = Calendar.getInstance();
// 获取小时数
int hours = c.get(Calendar.HOUR_OF_DAY);
if (hours > 9){ // 判断是否跨天 - 当天是否已过目标时间
c.roll(Calendar.DAY_OF_MONTH,1); // 天数延长一天
}
// 设置定时任务的启动时间
c.set(Calendar.HOUR_OF_DAY,9);
c.set(Calendar.MINUTE,0);
c.set(Calendar.SECOND,0);
c.set(Calendar.MILLISECOND,0);
Long targetTime = c.getTimeInMillis();
Long delay = targetTime - now;
timer.schedule(task,0, 2000);
}
}
六、日期类
案例:日历的制作
public class MyDay {
// 天数
private int days;
// 天数是否是当前月份的
private boolean crrentDayOfMonth;
public MyDay(int days, boolean crrentDayOfMonth) {
this.days = days;
this.crrentDayOfMonth = crrentDayOfMonth;
}
// 打印天 - 是当月的为绿色字体 ,不是当月的为红色字体
public void showDays(){ // 又问题的 System out和err 之间线程安全
if (crrentDayOfMonth){
// System.out.print(days + "\t");
System.out.print("\33[36;1m"+days+"\t\33[0m");
}else{
// \33[%d;%d;%dm可以更改输出样式,改变的内容包括字体颜色、背景颜色、样式(粗体、斜体、下划线)
// \33[%d;%d;%dm的第一个数字是前景色(字体颜色),范围31-36;第二个数字是背景色,范围41-46;第三个数字是样式,取值1,3,4
// 红黄橙蓝紫绿
System.out.print("\33[31;1m"+days+"\t\33[0m");
}
}
@Override
public String toString() {
return "MyDay{" +
"days=" + days +
", crrentDayOfMonth=" + crrentDayOfMonth +
'}';
}
}
public class MyCalendar {
// 42个格子,打印42个 6 * 7 = 42
private static final int CAPACITY = 42;
// 星期字符串数组
String[] weeks = {"一","二","三","四","五","六","日"};
// 日期格式1
String format1 = "yyyy年MM月dd日 hh:mm:ss";
SimpleDateFormat sdf1 = new SimpleDateFormat(format1);
// 打印当前时间的方法
public void showNowDay(){
Date date = new Date();
System.out.println("当前日期为:");
System.out.println(sdf1.format(date));
}
// 得到需要打印天数的42个天数 (包括上一个月、当前月和下一个月)
public List<MyDay> getListOfDays(int year,int month){
ArrayList<MyDay> days = new ArrayList<>(CAPACITY); // 固定42个大小的集合
// 根据传入的年份和月份获取目标日期对象
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR,year);
calendar.set(Calendar.MONTH,month-1);
// 计算当前月的天数
int daysOfCurrentMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
// 上一个月天数 - 当前月星期数-1
int daysOfLastMonth = calendar.get(Calendar.DAY_OF_WEEK) - 1;
// 计算上一个月的最大天数
int daysOfLastMonthMax;
// 当前月是1月
if (month == 1){ // 表示上一个月最大天数是31天
daysOfLastMonthMax = 31; // 上一年的12月
} else {
calendar.set(Calendar.MONTH,month-2);
daysOfLastMonthMax = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
}
// 计算下一个月的天数
int daysOfNextMonth = CAPACITY - daysOfCurrentMonth - daysOfLastMonth;
// 先保存上一个月的天数到集合中
for (int i = (daysOfLastMonthMax - daysOfLastMonth + 1); i <= daysOfLastMonthMax; i++){
days.add(new MyDay(i,false));
}
// 接着保存当前月的天数到集合中
for (int i = 1; i <= daysOfCurrentMonth; i++){
days.add(new MyDay(i,true));
}
// 最后保存下一个月的天数到集合中
for (int i = 1; i <= daysOfNextMonth; i++){
days.add(new MyDay(i,false));
}
return days;
}
// 打印日历的方法 - 全
public void showCalendar(int year,int month){
// 打印当前日期
showNowDay();
System.out.println("---------------------");
// 打印目标日期
System.out.println("查询日历为: " + year + "年" + month + "月" );
for (int i = 0; i < weeks.length; i++){
System.out.print(weeks[i] + "\t"); // 打印星期
}
System.out.println();// 换行
// 调用打印日期的方法
printCalendar(year,month);
}
// 打印目标日期的方法
public void printCalendar(int year,int month){
// 调用方法,判断年份,月份是否符合
isYearMonth(year,month);
// 获取需要打印的日期
List<MyDay> days = getListOfDays(year, month);
// 遍历打印
for (int i = 1; i <= days.size(); i++){
MyDay myDay = days.get(i-1); // 获取MyDay对象
myDay.showDays(); // 调用MyDay的打印方法
if (i % 7 == 0){
System.out.println();
}
}
}
// 判断year和month是否符合规范
public boolean isYearMonth(int year,int month){
// 月份1-12月
if (1 <= month && month <= 12){
return true;
}else {
throw new RuntimeException("月份不符合!");
// return false;
}
}
}
public class MyCalendarTest {
/**
* MyCalendar测试
*/
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入要查询日期的年份:");
int year = input.nextInt();
System.out.println("请输入要查询日期的月份(1-12):");
int month = input.nextInt();
MyCalendar myCalendar = new MyCalendar();
myCalendar.showCalendar(year,month);
// myCalendar.showCalendar(2022,7);
input.close();
}
}
运行结果: