1.Lambda
//1.匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("新线程中执行的代码:"+Thread.currentThread().getName());
}
}).start();
System.out.println("主线程中的代码:"+Thread.currentThread().getName());
Lambda表达式是一个匿名函数,可以理解为一段代码
new Thread(()->{
System.out.println("新线程Lambda表达式。。。");
}).start();
优点:简化了匿名内部类的使用,语法更加简洁;匿名内部类语法冗余;
Lambda语法规则:省去了面向对象的条条框框,Lambda标准格式组成:
(参数类型 参数名称 )->{
代码体;
}
格式说明:
- (参数类型 参数名称 ):参数列表
- {代码体;} :方法体
- -> :箭头,分割参数列表和方法体
1.1 无参,无返回值lambda
定义接口
public interface UserService {
void show();
}
然后创建主方法使用
package com.newfeature;
import com.newfeature.service.UserService;
public class Demo02Lambda {
public static void main(String[] args) {
goShow(new UserService() {
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("执行show()方法");
}
});
System.out.println("------------------");
// 无参,无返回值lambda表达式
goShow(() -> {
System.out.println("Lamda show方法执行了。。");
});
}
public static void goShow(UserService userService) {
userService.show();
}
}
输出
执行show()方法
------------------
Lamda show方法执行了。。
1.2 有参有返回值的lambda表达式
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
private Integer height;
}
list集合保存多个person对象,然后对这些对象做根据age排序操作
package com.newfeature;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.newfeature.domain.Person;
public class Demo03Lambda {
public static void main(String[] args) {
List<Person> list=new ArrayList<>();
list.add(new Person("zhouzhou",33,175));
list.add(new Person("zhouzhou2",23,155));
list.add(new Person("zhouzhou3",53,125));
list.add(new Person("zhouzhou4",63,165));
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
for (Person person : list) {
System.out.println(person);
}
}
}
lambda表达式实现排序
package com.newfeature;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.newfeature.domain.Person;
public class Demo03Lambda {
public static void main(String[] args) {
List<Person> list=new ArrayList<>();
list.add(new Person("zhouzhou",33,175));
list.add(new Person("zhouzhou2",23,155));
list.add(new Person("zhouzhou3",53,125));
list.add(new Person("zhouzhou4",63,165));
Collections.sort(list,(Person o1,Person o2)->{
return o1.getAge() - o2.getAge();
});
for (Person person : list) {
System.out.println(person);
}
}
}
输出结果:
Person(name=zhouzhou2, age=23, height=155)
Person(name=zhouzhou, age=33, height=175)
Person(name=zhouzhou3, age=53, height=125)
Person(name=zhouzhou4, age=63, height=165)
1.3 @FunctionalInterface 注解
/**
* @FunctionalInterface
* 这是一个标志注解,被该注解修饰的接口只能声明一个抽象方法
*/
@FunctionalInterface
public interface UserService {
void show();
}
1.4 lambda表达式的原理
-
匿名内部类的本质是在编译时生产一个class文件,xxx$1.class;
-
lambda表达式原理:lambda表达式在程序运行的时候,会形成一个类。
1.在类中新增一个方法,这个方法的方法体就是lambda表达式中的代码
2.形成一个匿名内部类,实现接口,重写抽象方法3.在接口中重写方法会调用新生成的方法
-
jdk自带工具:javap对字节码进行反编译操作
javap -c -p 文件名.class -c:表示对 代码进行反汇编 -p:显示所有的类和成员
1.5lambda表达式的简写写法
在lambda表达式的标准写法基础上,可以使用简写写法规则为:
- 小括号内的参数类型可以省掉
- 如果小括号内有且仅有一个参数,则小 括号可以省掉
- 如果大括号内有且仅有一个语句,可以省掉大括号、return关键字以及语句分号
定义接口:
public interface StudentService {
String show(String name,Integer age);
}
public interface OrderService {
Integer show(String name);
}
lambda简写写法:
package com.newfeature;
import com.newfeature.service.OrderService;
import com.newfeature.service.StudentService;
public class Demo04lambda {
public static void main(String[] args) {
goStudent((String name,Integer age)->{
return name+age+"***";
});
//简写写法
goStudent((name,age)-> name+age+"0000");
System.out.println("--------------");
goOrder((String name)->{
System.out.println("----->"+name);
return 999;
});
//简写写法
goOrder(name->{
System.out.println("----->"+name);
return 1999;
});
}
public static void goStudent(StudentService studentService){
studentService.show("张三",22);
}
public static void goOrder(OrderService orderService){
orderService.show("李四");
}
}
1.6 lambda表达式的使用前提
- 方法的参数 或局部变量类型必须为接口才能使用
- 接口中有且仅有一个抽象方法@FunctionalInterface
1.7 lambda表达式与匿名内部类的对比
-
所需类型不一样
- 匿名内部类的类型可以是类,抽象类,接口
2.lambda表达式需要的类型必须是接口
-
抽象方法数量不一样
1匿名内部类所需的接口 中的抽象方法的数量是随意的
2 lambda表达式所需的接口中只能有一个抽象方法 -
实现原理不一样
1 匿名内部类是 在编译后形成一个class
2 lambda表达式是在程序运行的时候动态生成class
2. 接口中新增的方法
2.1 jdk8之前,接口定义
interface 接口名{
静态常量;
抽象方法;
}
jdk8之后,对接口做了增加,接口可以有默认方法和静态方法
interface 接口名{
静态常量;
抽象方法;
默认方法;
静态方法;
}
2.2 默认方法
package com.newfeature;
public class InterfaceDemo {
public static void main(String[] args) {
A a=new B();
a.test3();
A c=new C();
c.test3();
}
}
class B implements A{
@Override
public void test1() {
System.out.println("test1方法执行了....");
}
@Override
public void test2() {
System.out.println("test2中方法执行了...");
}
/**
* 重写默认方法
* @return
*/
@Override
public String test3() {
System.out.println("在B实现类中重写了默认方法test3");
return "ok...";
}
}
class C implements A{
@Override
public void test1() {
}
@Override
public void test2() {
}
}
interface A {
void test1();
void test2();
/**
* 定义接口中默认方法
* @return
*/
public default String test3(){
System.out.println("接口中默认方法执行了....");
return "hello";
}
}
接口中默认方法的使用:
- 实现类直接调用接口中的默认方法
- 实现类重写接口的默认方法
2.3 静态方法
jdk8中为接口新增了静态方法,作用是为了接口的扩展
语法规则
interface 接口名{
修饰符 static 返回值类型 方法ming{
方法体;
}
}
package com.newfeature;
public class InterfaceDemo {
public static void main(String[] args) {
A a=new B();
a.test3();
A c=new C();
c.test3();
A.test4();
}
}
class B implements A{
@Override
public void test1() {
System.out.println("test1方法执行了....");
}
@Override
public void test2() {
System.out.println("test2中方法执行了...");
}
@Override
public String test3() {
System.out.println("在B实现类中重写了默认方法test3");
return "ok...";
}
}
class C implements A{
@Override
public void test1() {
}
@Override
public void test2() {
}
}
interface A {
void test1();
void test2();
/**
* 定义接口中默认方法
* @return
*/
public default String test3(){
System.out.println("接口中默认方法执行了....");
return "hello";
}
/**
* 定义接口中静态方法
* @return
*/
public static String test4(){
System.out.println("接口中的静态方法执行了...");
return "Hello2";
}
}
接口中的静态方法使用:
接口中的静态方法在实现类中是不能被重写的,只能通过接口类型来实现:接口名.静态方法名称();
接口中默认方法与静态方法的区别:
1.默认方法通过实例调用,静态方法通过接口名调用
2.默认方法可以被继承,实现类可以直接调用默认方法,也可以重写接口默认方法
3.静态方法不能被继承,实现类不能重写接口的静态方法,只能用接口名调用
3. 函数式接口
3.1 函数式接口的由来
使用lambda表达式的前提是需要函数式接口,而lambda表达式使用时不关心接口的名称、抽象方法名,只关心抽象方法的参数列表和返回值类型。为了使用lambda表达式更方便,jkd提供了大量的函数式接口
package com.newfeature;
public class FuncInterface {
public static void main(String[] args) {
fun1((arr)->{
int sum=0;
for(int i:arr){
sum+=i;
}
return sum;
});
}
public static void fun1(Operator operator){
int[] arr={1,2,3,4};
int sum=operator.getSum(arr);
System.out.println("sum="+sum);
}
}
/**
* 函数式接口
*/
@FunctionalInterface
interface Operator{
int getSum(int[] arr);
}
3.2 函数式接口介绍
在jdk中提供的函数式接口,主要在java.util.function包中
1. Supplier: 无参有返回值接口,对于lambda表达式需要提供一个返回数据的类型; 用于生产数据
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
使用:
package com.newfeature;
import java.util.Arrays;
import java.util.function.Supplier;
/**
* Supplier接口使用
*/
public class SupplierTest {
private static void fun1(Supplier<Integer> supplier){
Integer max=supplier.get();
System.out.println("max="+max);
}
public static void main(String[] args) {
fun1(()->{
int arr[]={22,33,55,66,34,99};
//计算数组中最大值
Arrays.sort(arr);
return arr[arr.length-1];
});
}
}
2. Consumer :有参无返回值的接口,用于消费数据,使用时候需要指定一个泛型来定义参数类型
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
使用:将输入的数据统一转换为小写
package com.newfeature;
import java.util.Locale;
import java.util.function.Consumer;
public class ConsumerTest {
public static void main(String[] args) {
test(msg->{
System.out.println(msg+"->转换为小写:"+msg.toLowerCase());
});
}
public static void test(Consumer<String> consumer){
consumer.accept("Hello World");
}
}
默认方法andThen使用:如果一个方法的参数和返回值全部是consumer类型,就可以实现效果,消费一个数据的时候,首先做一个操作,然后 再做一个操作,实现组合,而这个方法就是Consumer接口中的default方法->andThen方法。
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
具体操作:
package com.newfeature;
import java.util.function.Consumer;
public class ConsumerTest2 {
public static void main(String[] args) {
test2(msg1->{
System.out.println(msg1+"->转换为小写:"+msg1.toLowerCase());
},msg2->{
System.out.println(msg2+"转换为大写:"+msg2.toUpperCase());
});
}
public static void test(Consumer<String> consumer){
consumer.accept("Hello World");
}
public static void test2(Consumer<String> c1,Consumer<String> c2){
String str="Hello World";
// c1.accept(str);//转小写
//c2.accept(str);//转大写
//c1.andThen(c2).accept(str);
c2.andThen(c1).accept(str);
}
}
3.Funciton:有参有返回值的接口,Function接口是根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
使用:传递进去一个字符串,返回一个integer
package com;
import java.util.function.Function;
public class FunctionTest {
public static void main(String[] args) {
test(msg->{
return Integer.parseInt(msg);
});
}
public static void test(Function<String,Integer> function){
Integer apply=function.apply("666");
System.out.println("apply="+apply);
}
}
默认方法:andThen,也是进行组合操作
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
组合操作使用:
package com;
import java.util.function.Function;
public class FunctionTest {
public static void main(String[] args) {
test(msg -> {
return Integer.parseInt(msg);
}, msg2 -> {
return msg2 * 2;
});
}
public static void test(Function<String, Integer> f1, Function<Integer, Integer> f2) {
// Integer apply=f1.apply("666");
// Integer apply2=f2.apply(apply);
Integer apply2 = f1.andThen(f2).apply("666");
System.out.println("apply2=" + apply2);
}
}
默认的compose方法作用顺序和andThen方法刚好相反;
静态方法identity则是,输入什么参数就返回什么参数
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
static <T> Function<T, T> identity() {
return t -> t;
}
4. Predicate:有参且有返回值为boolean型的接口
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}
使用:
package com.newfeature;
import java.util.function.Predicate;
public class PredicateTest {
private static void test(Predicate<String> predicate,String msg){
boolean b = predicate.test(msg);
System.out.println("b="+b);
}
public static void main(String[] args) {
test(msg->{
return msg.length()>3;
},"a");
}
}
在Predicate中的默认方法提供了逻辑操作:
- and
- or
- negate:取反
- isEquals
package com.newfeature;
import java.util.function.Predicate;
public class PredicateTest {
private static void test(Predicate<String> p1,Predicate<String> p2){
//p1包含H,p2包含w
//p1包含H同时p2包含w
boolean rs1= p1.and(p2).test("Hello");
//p1包含H或者p2包含w
boolean rs2=p1.or(p2).test("Hello");
//p1不包含H
boolean rs3=p1.negate().test("Hello");
System.out.println("rs1:"+rs1);//false
System.out.println("rs2:"+rs2);//true
System.out.println("rs3:"+rs3);//false
}
public static void main(String[] args) {
test(msg1->{
return msg1.contains("H");
},msg2->{
return msg2.contains("W");
});
}
}
4.方法引用
4.1 使用方法引用的原因:
使用lambda表达式的时候,出现代码冗余的情况,eg:用lambda表达式求和
package com.newfeature;
import java.util.function.Consumer;
public class FunctionRefTest {
private static void printMax(Consumer<int[]> consumer){
int[] a={10,20,30,50,90};
consumer.accept(a);
}
/**
* 求数组中值的总和
* @param a
*/
public void getTotal(int a[]){
int sum=0;
for(int i:a){
sum+=i;
}
System.out.println("数组之和:"+sum);
}
public static void main(String[] args) {
//lambda表达式中的代码和getTotal中代码出现冗余了
printMax(a->{
int sum=0;
for(int i:a){
sum+=i;
}
System.out.println("数组之和:"+sum);
});
}
}
冗余代码的解决方案:
因为lambda表达式中要执行的代码和另外一个方法中的代码一样,就没必要重写一份代码,就可以使用:引用 重复代码
package com.newfeature;
import java.util.function.Consumer;
public class FunctionRefTest {
private static void printMax(Consumer<int[]> consumer){
int[] a={10,20,30,50,90};
consumer.accept(a);
}
/**
* 求数组中元素的总和
* @param a
*/
public static void getTotal(int a[]){
int sum=0;
for(int i:a){
sum+=i;
}
System.out.println("数组之和:"+sum);
}
public static void main(String[] args) {
// :: 方法引用,jdk8的新特性
printMax(FunctionRefTest::getTotal);
}
}
:: 方法引用,jdk8的新特性的语法
应用场景:如果Lambda表达式所要实现的方案,已经在其他方法存在相同的方案,那么则可以使用方法引用。
常见的引用方式:
- instanceName::methodName 对象::方法名
- ClassName::staticMethodName 类名::静态方法
- ClassName::methodName 类名::普通方法
- ClassName::new 类名::new 调用的构造器
- TypeName[]::new String[]::new 调用数组的构造器
4.2 对象名::方法名
package com.newfeature;
import java.util.Date;
import java.util.function.Supplier;
public class FunctionTest03 {
public static void main(String[] args) {
Date now = new Date();
Supplier<Long> supplier = () -> {
return now.getTime();
};
System.out.println(supplier.get());
//通过引用的方法引用的方式处理
Supplier<Long> supplier1 = now::getTime;
System.out.println(supplier1.get());
}
}
方法引用注意的事项:
- 被用户的方法,参数要和接口中的抽象方法的参数一样
- 当接口抽象方法有返回值时,被引用的方法必须有返回值
4.3 类名 ::静态方法名
package com.newfeature;
import java.util.function.Supplier;
public class FunctionTest04 {
public static void main(String[] args) {
Supplier<Long> supplier1=()->{
return System.currentTimeMillis();
};
System.out.println(supplier1.get());
//通过 方法引用来实现
Supplier<Long> supplier2=System::currentTimeMillis;
System.out.println(supplier2.get());
}
}
4.4 类名::引用实例方法
java面向对象中,类名只能调用静态方法,类名引用实例方法是使用前提,实际上第一个参数作为方法的调用者
package com.newfeature;
import java.util.function.BiFunction;
import java.util.function.Function;
public class FunctionTest05 {
public static void main(String[] args) {
Function<String,Integer> function=(s)->{
return s.length();
};
System.out.println(function.apply("hello"));
//通过方法引用来实现
Function<String,Integer> function1=String::length;
System.out.println(function1.apply("hahah"));
BiFunction<String,Integer,String> function2=String::substring;
String msg = function2.apply("helloworld", 3);
System.out.println(msg);
}
}
4.5 类名::构造器
package com.newfeature;
import com.newfeature.domain.Person;
import java.util.function.BiFunction;
import java.util.function.Supplier;
public class FunctionTest06 {
public static void main(String[] args) {
Supplier<Person> sup=()->{
return new Person();
};
System.out.println(sup.get());
//通过方法引用来实现
Supplier<Person> sup1=Person::new;
System.out.println(sup1.get());
BiFunction<String,Integer,Person> function=Person::new;
System.out.println(function.apply("张三",22));
}
}
4.6 数组::构造器
package com.newfeature;
import java.util.function.Function;
public class FunctionTest07 {
public static void main(String[] args) {
Function<Integer, String[]> fun1 = (len) -> {
return new String[len];
};
String[] a1 = fun1.apply(3);
System.out.println("数值的长度是:" + a1.length);
Function<Integer, String[]> fun2 = String[]::new;
String[] a2 = fun2.apply(5);
System.out.println("数组长度是:" + a2.length);
}
}
方法引用是lambda表达式符合特定情况下的一种缩写方式,不过方法的引用只能引用已经存在的方法
5. stream API
5.1 集合处理数据的弊端
对集合中元素进行操作的时候,除了必须添加、删除、、获取外,典型的操作就是集合的遍历
package com.newfeature;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class StreamTest01 {
public static void main(String[] args) {
//定义一个集合
List<String> list= Arrays.asList("张三","张麻子","李四","王五");
//1.获取所有,姓张的信息
List<String> list2=new ArrayList<>();
for(String s:list){
if(s.startsWith("张")){
list2.add(s);
}
}
//2.获取名称长度为3的用户
List<String> list3=new ArrayList<>();
for(String s:list2){
if(s.length()==3){
list3.add(s);
}
}
//3.输出所有的用户信息
for(String s:list3){
System.out.println(s);
}
}
}
针对上面的需求,总是一次次的遍历循环,希望有高效的方式处理,通过jdk8中stream api优雅处理
package com.newfeature;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class StreamTest02 {
public static void main(String[] args) {
//定义一个集合
List<String> list = Arrays.asList("张三", "张麻子", "李四", "王五");
list.stream()
.filter(s -> s.startsWith("张"))
.filter(s -> s.length() == 3)
.forEach(System.out::println);
// .forEach(s -> {System.out.println(s);});
}
}
上面StreamAPI代码含义:获取流,过滤张,过滤长度,遍历打印;代码更简洁
5.2 stream 流式思想
- Stream和IO流没有任何关系;
- Stream流式思想类似工厂车间的生产流水线,stream流不是一种数据结构,不保存数据,而是对数据进行加工处理,stream可以看做是流水线上的一个工序,在流水线上,通过多个工序让一个原材料加工成一个商品
Stream API能让我们快速完成复杂的操作,eg:筛选,切片,映射、查找,去除重复、统计、匹配和归约
5.3 stream流的获取方式
-
根据Collection获取
首先,java.uitl.Collection接口中加入了默认的方法stream,也就是Collection接口下所有的实现都 可以通过stream方法来获取stream流
package com.newfeature;
import java.util.*;
public class StreamTest03 {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.stream();
Set<String> set=new HashSet<>();
set.stream();
Vector<String> vector=new Vector<>();
vector.stream();
}
}
但是map接口没有实现Collection接口,怎么做呢?可以根据Map获取对应的key value的集合
package com.newfeature;
import java.util.*;
import java.util.stream.Stream;
public class StreamTest04 {
public static void main(String[] args) {
Map<String,Object> map=new HashMap<>();
Stream<String> stream=map.keySet().stream();//key
Stream<Object> ojb= map.values().stream();//value
Stream<Map.Entry<String,Object>> stream2=map.entrySet().stream();//entry
}
}
-
通过stream的of方法
把数组转换成stream,采用of方法
package com.newfeature;
import java.util.stream.Stream;
public class StreamTest05 {
public static void main(String[] args) {
Stream<String> a1= Stream.of("a1","a2","a3");
String[] arr1={"aa","bb","cc"};
Stream<String> arr11=Stream.of(arr1);
Integer[] arr2={1,2,3};
Stream<Integer> arr21=Stream.of(arr2);
arr21.forEach(System.out::println);
//注意:基本的数据类型是不行的,要用包装类型
int[] arr3={1,2,3};
Stream.of(arr3).forEach(System.out::println);
}
}
5.4 stream常用方法介绍
-
终结方法:返回值类型不再是stream类型的方法,不再支持链式调用。eg: foreach() 、count()
-
非终结方法:返回值类型仍然是stream类型的方法,支持链式调用.(除了终结方法,其余方法均为非终结方法)
注意:
-
strem只能操作一次
-
stream返回的是最新的流
-
stream不调用终结方法,中间的操作的不会执行
package com.newfeature; import java.util.stream.Stream; public class StreamTest06 { public static void main(String[] args) { Stream<String> a1 = Stream.of("a1", "a2", "a3"); a1.filter(s -> { System.out.println("------------"); return s.contains("a"); }).forEach(System.out::println); System.out.println("------------->"); } }
5.5 forEach:遍历数据
void forEach(Consumer<? super T> action);
该方法接受一个Consumer接口,会将每一个流元素交给函数处理
package com.newfeature;
import java.util.stream.Stream;
public class StreamTest06 {
public static void main(String[] args) {
Stream.of("a1", "a2", "a3").forEach(System.out::println);
}
}
5.6 count:统计stream流中元素个数的
long count();
package com.newfeature;
import java.util.stream.Stream;
public class StreamTest06 {
public static void main(String[] args) {
long count= Stream.of("a1", "a2", "a3").count();
System.out.println(count);
}
}
5.7 filter:用来过滤数据,返回符合条件的数据
可以通过filter方法将一个流转换为另一个子集流
Stream<T> filter(Predicate<? super T> predicate);
该接口接收一个Predicate函数式接口参数,作为筛选条件
package com.newfeature;
import java.util.stream.Stream;
public class StreamTest07 {
public static void main(String[] args) {
Stream.of("a1", "a2", "a3","bb","dd")
.filter((s)->s.contains("a")).forEach(System.out::println);
}
}
输出:
a1
a2
a3
5.8 limit:对流进行截取处理,只取前n个数据
Stream<T> limit(long maxSize);
参数是一个long类型数值,如果集合当前长度大于参数就进行截取,否则不操作
使用:
package com.newfeature;
import java.util.stream.Stream;
public class StreamTest08 {
public static void main(String[] args) {
Stream.of("a1", "a2", "a3","bb","dd")
.limit(3).forEach(System.out::println);
}
}
输出:
a1
a2
a3
5.9 skip
如果想跳过前面几个元素,可以使用skip方法获取一个截取之后的新流:
Stream<T> skip(long n);
操作:
package com.newfeature;
import java.util.stream.Stream;
public class StreamTest09 {
public static void main(String[] args) {
Stream.of("a1", "a2", "a3","bb","dd")
.skip(3).forEach(System.out::println);
}
}
输出:
bb
dd
5.10 map
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一个R类型数据
package com.newfeature;
import java.util.stream.Stream;
public class StreamTest10 {
public static void main(String[] args) {
Stream.of("1", "2", "3","4","5")
// .map(msg->Integer.parseInt(msg))
.map(Integer::parseInt)
.forEach(System.out::println);
}
}
5.11 sorted:对数据进行排序
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
在使用时候,可以根据自然规则排序,也可以通过比较器指定对应的排序规则
package com.newfeature;
import java.util.stream.Stream;
public class StreamTest11 {
public static void main(String[] args) {
Stream.of("1", "5", "2","3","0")
.map(Integer::parseInt)
.sorted() //根据数据的自然顺序排序
.sorted((o1,o2)->o2-o1) //根据比较器指定对应的排序规则
.forEach(System.out::println);
}
}
5.12 distinct:去掉重复数据
Stream<T> distinct();
package com.newfeature;
import java.util.stream.Stream;
public class StreamTest11 {
public static void main(String[] args) {
Stream.of("1","3", "5", "2","3","0")
.map(Integer::parseInt)
.sorted() //根据数据的自然顺序排序
.sorted((o1,o2)->o2-o1) //根据比较器指定对应的排序规则
.distinct() //去除重复记录
.forEach(System.out::println);
}
}
Stream流中的distinct方法对于基本数据类型是可以直接去重的,但对自定义类型,需要重写hashcode & equals方法来移除重复元素
package com.newfeature.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Objects;
//@Data
//@AllArgsConstructor
//@NoArgsConstructor
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name) && Objects.equals(age, person.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
// private Integer height;
}
package com.newfeature;
import com.newfeature.domain.Person;
import java.util.stream.Stream;
public class StreamTest11 {
public static void main(String[] args) {
Stream.of("1","3", "5", "2","3","0")
.map(Integer::parseInt)
.sorted() //根据数据的自然顺序排序
.sorted((o1,o2)->o2-o1) //根据比较器指定对应的排序规则
.distinct() //去除重复记录
.forEach(System.out::println);
System.out.println("------------------------");
Stream.of(new Person("张三",18),
new Person("李四",33),
new Person("张三",18)
).distinct()
.forEach(System.out::println);
}
}
5.13 match
5.14 find
5.15 max&min
Optional<T> max(Comparator<? super T> comparator);
Optional<T> min(Comparator<? super T> comparator);
max使用:
package com.newfeature;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamTest12 {
public static void main(String[] args) {
Optional<Integer> max = Stream.of(4, 5, 3, 9)
.max((o1,o2)->Integer.compare(o1,o2));
System.out.println(max.get());
}
}
min使用:
package com.newfeature;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamTest12 {
public static void main(String[] args) {
Optional<Integer> min = Stream.of(4, 5, 3, 9)
.min((o1,o2)->Integer.compare(o1,o2));
System.out.println(min.get());
}
}
5.16 reduce方法
如果需要将所有数据归纳得到一个数据,使用reduce方法
T reduce(T identity, BinaryOperator<T> accumulator);
使用:
package com.newfeature;
import java.util.stream.Stream;
public class StreamTest12 {
public static void main(String[] args) {
Integer sum = Stream.of(4, 5, 3, 9)
.reduce(0, (x, y) -> {
//identity默认值
//第一次的时候会将默认值赋值给x
//之后再次会将上一次的操作结果赋值给x,y是每次从数据中获取的元素
System.out.println("x=" + x + ",y=" + y);
return x + y;
});
System.out.println(sum);
//获取最大值、
Integer max = Stream.of(4, 5, 3, 9)
.reduce(0, (x, y) -> {
return x > y ? x : y;
});
System.out.println(max);
}
}
-
map和reduce的组合:实际开发中经常会map与reduce一块使用
-
package com.newfeature; import com.newfeature.domain.Person; import java.util.stream.Stream; public class StreamTest13 { public static void main(String[] args) { //求年龄的总和 Integer sum = Stream.of(new Person("张三", 18), new Person("李四", 33), new Person("张三", 38), new Person("王五", 45), new Person("张三", 19) ).map(Person::getAge) .reduce(0,Integer::sum); System.out.println(sum); //2.求年龄最大值 Integer max = Stream.of(new Person("张三", 18), new Person("李四", 33), new Person("张三", 38), new Person("王五", 45), new Person("张三", 19) ).map(Person::getAge)//实现数据类型的转换,符合reduce对数据的要求 .reduce(0,Math::max);//reduce实现数据的处理 System.out.println(max); //3.统计字符串a出现的次数 Integer count= Stream.of("a","b","c","d","a") .map(ch->"a".equals(ch)?1:0) .reduce(0,Integer::sum); System.out.println(count); } }
5.17 mapToInt
作用:将stream中的Integer类型转换为int类型,可使用mapToInt方法
使用:
package com.newfeature;
import com.newfeature.domain.Person;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamTest14 {
public static void main(String[] args) {
//Integer占用的内存比int多很多,在stream流操作中会自动装箱和拆箱操作
Integer arr[]={1,2,3,4,5,7,9};
Stream.of(arr)
.filter(i->i>0).forEach(System.out::println);
System.out.println("--------------------");
//为提高代码的执行效率,可以将stream中的integer数据先转换为int数据,然后再操作
IntStream intStream = Stream.of(arr)
.mapToInt(Integer::intValue);
intStream.filter(i->i>3).forEach(System.out::println);
}
}
5.18 concat:如果两个流,合并成为一个流,使用stream的concat方法
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
Objects.requireNonNull(a);
Objects.requireNonNull(b);
@SuppressWarnings("unchecked")
Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
(Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
return stream.onClose(Streams.composedClose(a, b));
}
使用:
package com.newfeature;
import java.util.stream.Stream;
public class StreamTest15 {
public static void main(String[] args) {
Stream<String> stream1=Stream.of("a","b","c");
Stream<String> stream2=Stream.of("g","h","s");
//通过concat将两个stream合并为一个新的stream
Stream.concat(stream1,stream2).forEach(System.out::println);
}
}
5.19 综合使用stream
package com.newfeature;
import com.newfeature.domain.Person;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamTest16 {
public static void main(String[] args) {
List<String> list1 = Arrays.asList("a1", "aa2", "a3", "aa4", "a5", "as6", "aa7");
List<String> list2 = Arrays.asList("b1", "张b2", "b3", "张b4", "张bk5", "b6", "b7");
Stream<String> stream1 = list1.stream()
.filter(x -> x.length() == 3)
.limit(3);
Stream<String> stream2 = list2.stream().filter(x -> x.startsWith("张")).skip(2);
Stream.concat(stream1, stream2)
.map(Person::new)
.forEach(System.out::println);
}
}
输出结果:
Person{name='aa2', age=null}
Person{name='aa4', age=null}
Person{name='as6', age=null}
Person{name='张bk5', age=null}
1.结果收集到集合中
package com.newfeature;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test01 {
/**
* Stream结果收集到集合中
*/
@Test
public void test01() {
List<String> list = Stream.of("a", "b", "c", "c")
.collect(Collectors.toList());
System.out.println(list);
//收集到set
Set<String> set = Stream.of("a", "b", "c", "a")
.collect(Collectors.toSet());
System.out.println(set);
//获取的类型为具体的实现,eg:Arraylist,HashSet
ArrayList<String> arrayList = Stream.of("a", "b", "c", "a")
.collect(Collectors.toCollection(ArrayList::new));
//.collect(Collectors.toCollection(()->new ArrayList<>()));
System.out.println(arrayList);
HashSet<String> hashSet = Stream.of("a", "b", "c", "a").collect(Collectors.toCollection(HashSet::new));
System.out.println(hashSet);
}
}
输出:
[a, b, c, c]
[a, b, c]
[a, b, c, a]
[a, b, c]
Process finished with exit code 0
2.结果收集到数组中
Stream中提供toArray方法将结果放到一个数组中,返回值类型是Object[],如果指定返回类型,则使用重载方法toArray(IntFunction f)方法
/**
* 数据收集到数组中
*/
@Test
public void test02(){
Object[] objects = Stream.of("a", "b", "c", "a").toArray();
System.out.println(Arrays.toString(objects));
//指定返回的数组中的数据类型
String[] strings = Stream.of("a", "b", "c", "a").toArray(String[]::new);
System.out.println(Arrays.toString(strings));
}
5.20 对流中数据做聚合计算
使用stream流处理数据后,可以像数据库的聚合函数一样,对一个字段进行操作,eg:min,max,avg,sum,count.
package com.newfeature;
import com.newfeature.domain.Person;
import org.junit.Test;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test01 {
/**
* Stream结果收集到集合中
*/
@Test
public void test01() {
List<String> list = Stream.of("a", "b", "c", "c")
.collect(Collectors.toList());
System.out.println(list);
//收集到set
Set<String> set = Stream.of("a", "b", "c", "a")
.collect(Collectors.toSet());
System.out.println(set);
//获取的类型为具体的实现,eg:Arraylist,HashSet
ArrayList<String> arrayList = Stream.of("a", "b", "c", "a")
.collect(Collectors.toCollection(ArrayList::new));
//.collect(Collectors.toCollection(()->new ArrayList<>()));
System.out.println(arrayList);
HashSet<String> hashSet = Stream.of("a", "b", "c", "a").collect(Collectors.toCollection(HashSet::new));
System.out.println(hashSet);
}
/**
* 数据收集到数组中
*/
@Test
public void test02() {
Object[] objects = Stream.of("a", "b", "c", "a").toArray();
System.out.println(Arrays.toString(objects));
//指定返回的数组中的数据类型
String[] strings = Stream.of("a", "b", "c", "a").toArray(String[]::new);
System.out.println(Arrays.toString(strings));
}
/**
* stream中数据流聚合操作
*/
@Test
public void test03() {
//获取年龄的最大值
Optional<Person> maxAge = Stream.of(new Person("张三", 18),
new Person("李四", 33),
new Person("张三", 18))
.collect(Collectors.maxBy((p1, p2) -> p1.getAge() - p2.getAge()));
System.out.println("最大年龄:" + maxAge.get());
//获取年龄的最小值
Optional<Person> minAge = Stream.of(new Person("张三", 18),
new Person("李四", 33),
new Person("张三", 18))
.collect(Collectors.minBy((p1, p2) -> p1.getAge() - p2.getAge()));
System.out.println("最小年龄:" + minAge.get());
//求所有人年龄之和
Integer sumAge = Stream.of(new Person("张三", 18),
new Person("李四", 33),
new Person("张三", 18))
//.collect(Collectors.summingInt(s -> s.getAge()));
.collect(Collectors.summingInt(Person::getAge));
System.out.println("所有人年龄之和:" + sumAge);
//求所有人年龄的平均值
Double avgAge = Stream.of(
new Person("张三", 18),
new Person("李四", 33),
new Person("张三", 18))
.collect(Collectors.averagingInt(Person::getAge));
System.out.println("所有人年龄的平均值:" + avgAge);
//统计人员数量
Long count = Stream.of(
new Person("张三", 18),
new Person("李四", 33),
new Person("张三", 18))
.filter(p -> p.getAge() > 18)
.collect(Collectors.counting());
System.out.println("统计人员数量:" + count);
}
}
5.21 对流中数据做分组操作
/**
* stream中数据流分组操作
*/
@Test
public void test04() {
//分组
Map<String, List<Person>> map = Stream.of(
new Person("张三", 18),
new Person("李四", 33),
new Person("张三", 18))
.collect(Collectors.groupingBy(Person::getName));
map.forEach((k,v)-> System.out.println("k="+k+",v="+v));
System.out.println("--------------------------------");
//年龄大于等于18成年,否则未成年
Map<String, List<Person>> m2 = Stream.of(
new Person("张三", 17),
new Person("李四", 33),
new Person("张三", 18))
.collect(Collectors.groupingBy(p -> p.getAge() >= 18 ? "成年" : "未成年"));
m2.forEach((k,v)-> System.out.println("k="+k+",v="+v));
}
多级分组实现:
/**
* stream中数据流多级分组操作
*/
@Test
public void test05() {
//根据name,age(成年与未成年)分组
Map<String, Map<Object,List<Person>>> map=Stream.of(
new Person("张三", 17),
new Person("李四", 33),
new Person("张三", 18))
.collect(Collectors.groupingBy(Person::getName,Collectors.groupingBy(p->p.getAge()>=18?"成年":"未成年")
));
map.forEach((k,v)->{
System.out.println("k="+k+",v="+v);
v.forEach((k1,v1)->{
System.out.println("\t"+k1+"="+v1);
});
} );
}
输出结果:
k=李四,v={成年=[Person{name='李四', age=33}]}
成年=[Person{name='李四', age=33}]
k=张三,v={未成年=[Person{name='张三', age=17}], 成年=[Person{name='张三', age=18}]}
未成年=[Person{name='张三', age=17}]
成年=[Person{name='张三', age=18}]
5.22 对流中数据做分区操作
Collectors.partitioningBy根据值是否为true,把集合中的数据分割 为两个列表,一个true列表,一个false列表
@Test
public void test06() {
Map<Boolean, List<Person>> m = Stream.of(
new Person("张三", 17),
new Person("李四", 33),
new Person("张三", 18))
.collect(Collectors.partitioningBy(p -> p.getAge() > 18));
m.forEach((k, v) -> {
System.out.println(k + "\t" + v);
});
}
输出结果:
false [Person{name='张三', age=17}, Person{name='张三', age=18}]
true [Person{name='李四', age=33}]
5.23 对流中数据做拼接
collectors.joining根据指定的连接符,将 所有的元素拼接成一个字符串
@Test
public void test07() {
String s1 = Stream.of(
new Person("张三", 17),
new Person("李四", 33),
new Person("张三", 18))
.map(Person::getName).collect(Collectors.joining());
System.out.println(s1);
String s2 = Stream.of(
new Person("张三", 17),
new Person("李四", 33),
new Person("张三", 18))
.map(Person::getName).collect(Collectors.joining("-"));
System.out.println(s2);
String s3 = Stream.of(
new Person("张三", 17),
new Person("李四", 33),
new Person("张三", 18))
.map(Person::getName).collect(Collectors.joining("-","####","$$$$$"));
System.out.println(s3);
}
5.24 并行的stream流
-
串行的stream流:在一个线程上执行
/** * 串行流 */ @Test public void test01() { long count = Stream.of(5, 6, 9, 0, 3) .filter(s -> { System.out.println(Thread.currentThread() + " " + s); return s > 3; }).count(); }
输出结果:
Thread[main,5,main] 5
Thread[main,5,main] 6
Thread[main,5,main] 9
Thread[main,5,main] 0
Thread[main,5,main] 3
-
并行流
parallelStream是一个并行执行的流,通过默认的ForkJoinPool,可以提高多线程任务的速度。获取并行流的2种方式:
1.通过list接口中的parallelStream()方法获取
2.通过已有的串行流转换为并行流 parallel()
/** * 获取并行流的两种方式 */ @Test public void test02() { List<Integer> list=new ArrayList<>(); //通过list接口直接获取并行流 Stream<Integer> integerStream=list.parallelStream(); //将已有的串行流转换为并行流 Stream<String> parallel = Stream.of("1,2,3").parallel(); }
/**
* 并行流的操作
*/
@Test
public void test03() {
long count = Stream.of(1, 2, 3)
.parallel()//将流转换为并发流,stream处理的时候会通过多线程处理
.filter(s -> {
System.out.println(Thread.currentThread() + " s=" + s);
return s > 2;
}).count();
System.out.println(count);
}
输出:
Thread[ForkJoinPool.commonPool-worker-2,5,main] s=3
Thread[main,5,main] s=2
Thread[ForkJoinPool.commonPool-worker-1,5,main] s=1
1
5.25 并行流与串行流对比
通过for循环,串行stream流,并行流stream对八亿个数字求和,对比消耗时间
package com.newfeature;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.stream.LongStream;
public class Test03 {
private static long times=800000000;
private long start;
@Before
public void before(){
start=System.currentTimeMillis();
}
@After
public void end(){
long end=System.currentTimeMillis();
System.out.println("消耗时间:"+(end-start));
}
/**
* 普通for循环
*/
@Test
public void test01(){
System.out.println("普通for循环:");
long res=0;
for(int i=0;i<times;i++){
res+=i;
}
}
/**
* 串行流处理
*/
@Test
public void test02(){
System.out.println("串行流:serialStream");
LongStream.rangeClosed(0,times)
.reduce(0,Long::sum);
}
/**
* 并行流处理
*/
@Test
public void test03(){
LongStream.rangeClosed(0,times)
.parallel()
.reduce(0,Long::sum);
}
}
通过案例,可以看到parallelStream的效率最高
Stream并行处理的过程会分而治之,也就是将一个大的任务切分成了多个小任务,表示每个任务都是一个线程操作
5.26 并行流的多线程安全问题
-
多 线程处理数据的,线程安全问题
-
package com.newfeature; import org.junit.Test; import java.util.ArrayList; import java.util.List; public class Test04 { //并行流多线程安全问题 @Test public void test01(){ List<Integer> list=new ArrayList<>(); for(int i=0;i<1000;i++){ list.add(i); } System.out.println(list.size()); //使用并行流来向集合中添加数据 List<Integer> listNew=new ArrayList<>(); list.parallelStream() .forEach(listNew::add); System.out.println(listNew.size()); } }
输出结果:
1000 950
或者报异常,针对的解决方案:
- 加同步锁
- 使用线程安全的容器
- 通过stream中的toArray或collect操作
/** * 加同步锁 */ @Test public void test02() { List<Integer> list = new ArrayList<>(); for (int i = 0; i < 1000; i++) { list.add(i); } System.out.println(list.size()); Object obj = new Object(); //使用并行流来向集合中添加数据 List<Integer> listNew = new ArrayList<>(); list.parallelStream() // .forEach(listNew::add); .forEach(s -> { synchronized (obj) { listNew.add(s); } }); System.out.println(listNew.size()); } /** * 同步枷锁 */ @Test public void test03() { List<Integer> listNew = new ArrayList<>(); Object obj = new Object(); IntStream.rangeClosed(1, 1000) .parallel() .forEach(i -> { synchronized (obj) { listNew.add(i); } }); System.out.println(listNew.size()); } /** * 使用线程安全的容器 */ @Test public void test04() { Vector v = new Vector(); IntStream.rangeClosed(1, 1000) .parallel() .forEach(i -> { // synchronized (obj) { v.add(i); // } }); System.out.println(v.size()); } /** * 将线程不安全的容器转换为线程安全的容器 */ @Test public void test05() { List<Integer> listNew=new ArrayList<>(); //将线程不安全的容器,包装成线程安全的容器 List<Integer> synchronizedList= Collections.synchronizedList(listNew); IntStream.rangeClosed(1, 1000) .parallel() .forEach(i -> { synchronizedList.add(i); // } }); System.out.println(synchronizedList.size()); } /** * 通过Stream中的toArray方法或collect方法来操作就是满足线程安全的 */ @Test public void test06() { List<Integer> list = IntStream.rangeClosed(1, 1000) .parallel() .boxed() .collect(Collectors.toList()); System.out.println(list.size()); }
5.27 Fork/Join框架
parallelStream使用的是Fork/Join框架,fork/join框架自jdk7引入,Fork/join框架可以将一个大任务拆分为很多小任务异步执行,fork/join框架主要包括三个模块:
-
线程池:ForkJoinPool
-
任务对象:ForkJoinTask
-
执行任务的线程: ForkJoinWorkerThread
-
fork/join原理:采用分治法(Divide-and-Conquer Algorithm)来解决问题。eg:快速排序算法
-
fork/join原理-工作窃取算法(work-stealing):
利用现代硬件设备多核,在一个操作时候,会有空闲的cpu,利用空闲的cpu提高性能。工作窃取(work-stealing)算法是整个fork/join框架的核心理念,是指一个线程从其他队列里窃取任务来执行。 -
fork/join使用
需求:使用fork/join计算1~10000的和,当任务计算的数量大于3000,拆分任务,数量小于3000不拆分任务计算。
实现:
package com.newfeature;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class Test05 {
public static void main(String[] args) {
long start=System.currentTimeMillis();
ForkJoinPool pool=new ForkJoinPool();
SumRecursiveTask task=new SumRecursiveTask(1,10000L);
Long result = pool.invoke(task);
System.out.println("result="+result);
long end=System.currentTimeMillis();
System.out.println("总耗时:"+(end-start));
}
}
class SumRecursiveTask extends RecursiveTask<Long> {
//定义拆分的临界值
private static final long THRESHOLD=3000L;
private final long start;
private final long end;
public SumRecursiveTask(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length=end-start;
if(length<=THRESHOLD){
//任务不用拆分,可以计算
long sum=0;
for(long i=start;i<=end;i++){
sum+=i;
}
System.out.println("计算:"+start+"-->"+end+",的结果:"+sum);
return sum;
}else{
//数量大于预定的数量,说明任务还需要拆分
long middle=(start+end)/2;
System.out.println("拆分:左边->"+start+"-->"+middle+",右边-->"+(middle+1)+"-->"+end);
SumRecursiveTask left=new SumRecursiveTask(start,middle);
left.fork();
SumRecursiveTask right=new SumRecursiveTask(middle+1,end);
right.fork();
return left.join()+right.join();
}
}
}
输出结果:
拆分:左边->1-->5000,右边-->5001-->10000
拆分:左边->1-->2500,右边-->2501-->5000
拆分:左边->5001-->7500,右边-->7501-->10000
计算:7501-->10000,的结果:21876250
计算:2501-->5000,的结果:9376250
计算:5001-->7500,的结果:15626250
计算:1-->2500,的结果:3126250
result=50005000
总耗时:18
6.optional类
Optional类主要用来解决空指针的问题
6.1 以前对null的处理
@Test
public void Test01(){
// String username="张三";
String username=null;
if(username!=null){
System.out.println("字符串的长度:"+username.length());
}else{
System.out.println("字符串为空");
}
}
6.2 jdk1.8对null的处理->optional类
optional类是一个没有子类的工具类,optional是一个可以为null的容器对象,主要作用:就是为了避免Null检查,防止NullPoiniterException
6.3 optional基本使用
optional对象的创建方式:
package com.newfeature;
import org.junit.Test;
import java.util.Optional;
public class Test06 {
@Test
public void Test01(){
// String username="张三";
String username=null;
if(username!=null){
System.out.println("字符串的长度:"+username.length());
}else{
System.out.println("字符串为空");
}
}
/**
* optional对象的创建方式
*/
@Test
public void test02(){
//通过of方法,of方法不支持null的
Optional<String> op1 = Optional.of("张三");
//Optional<Object> op2 = Optional.of(null);
//第二种通过ofNullable方法支持null
Optional<String> op3 = Optional.ofNullable("lisi");
Optional<Object> op4 = Optional.ofNullable(null);
//第三种方式通过empty方法创建一个空的optional对象
Optional<Object> op5 = Optional.empty();
}
}
常用方法介绍:
/**
* optional中常用方法介绍
* get():如果optional有值就返回,否则抛出NoSuchElementException异常,
* get()通常和isPresent方法一块使用
* isPresent():判断元素是否存在,有返回true,无返回false
* orElse(T t):如果调用对象包含值,返回值,否则 返回t
* orElseGet(Supplier s):如果调用对象包含值,就返回值,否则返回Lambda表达式的返回值
*/
@Test
public void test03(){
Optional<String> op1=Optional.of("张三");
Optional<String> op2=Optional.empty();
//获取optional中的值
if(op1.isPresent()){
String s1=op1.get();
System.out.println("用于名:"+s1);
}
if(op2.isPresent()){
System.out.println(op2.get());
}else {
System.out.println("op2是一个空Optional对象");
}
String s3=op1.orElse("李四");
System.out.println(s3);
System.out.println(op2.orElse("jackma"));
String s5 = op2.orElseGet(()->{
return "hello666";
});
System.out.println(s5);
}
@Test
public void test04() {
Optional<String> op1 = Optional.of("张三");
Optional<String> op2 = Optional.empty();
//op1.ifPresent(s -> System.out.println("有值:"+s));
op1.ifPresent(System.out::println);
op2.ifPresentOrElse(System.out::println, () -> {
System.out.println("op2 对象为null");
});
}
/**
* 自定义一个方法,将person对象中name转换为大写并返回
*/
@Test
public void test05() {
Person p = new Person("zhangsan", 10);
Optional<Person> op = Optional.of(p);
String name = getNameForOptional(op);
System.out.println("name:" + name);
}
public String getNameForOptional(Optional<Person> op) {
if (op.isPresent()) {
String name = op
//.map(p -> p.getName())
.map(Person::getName)
//.map(p -> p.toUpperCase())
.map(String::toUpperCase)
.orElse("空值");
return name;
}
return null;
}
/**
* 根据person对象,将name转换为大写并返回
*
* @param person
* @return
*/
public String getName(Person person) {
if (person != null) {
String name = person.getName();
if (name != null) {
return name.toUpperCase();
} else {
return null;
}
} else {
return null;
}
}
7 .jdk1.8 日期&时间新API
-
旧版的日期处理:1.设计不合理;2.存在线程不安全的问题->java.uitl.Date,所有的日期类都是可变的;3.时区处理麻烦,日期类不提供国际化,没有时区支持。
-
新的日期API介绍:
-
jdk新的日期api常用操作:
package com.newfeature; import org.junit.Test; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; public class Test07 { /** * jdk1.8日期操作 LocalDate */ @Test public void test01(){ //1.创建指定的日期 LocalDate date1 = LocalDate.of(2021, 05, 02); System.out.println("date1="+date1); //2.得到当前的日期 LocalDate now = LocalDate.now(); System.out.println("now="+now); //3.根据localdate对象获取对应的日期信息 System.out.println("Year:"+now.getYear()); System.out.println("Month:"+now.getMonth()); System.out.println("day:"+now.getDayOfMonth()); System.out.println("week:"+now.getDayOfWeek().getValue()); } /** * LocalTime 时间操作 */ @Test public void test02() { //1.获取指定的时间 LocalTime time=LocalTime.of(5,26,33,23145); System.out.println(time); //2.获取当前的时间 LocalTime now=LocalTime.now(); System.out.println(now); //3.获取时间信息 System.out.println(now.getHour()); System.out.println(now.getMinute()); System.out.println(now.getSecond()); System.out.println(now.getNano()); } /** * 时期时间类型 LocalDateTime */ @Test public void test03(){ //1.获取指定的日期时间 LocalDateTime dateTime = LocalDateTime.of(2020, 06, 01, 12, 12, 33, 213); System.out.println(dateTime); //2.获取指定的日期时间 LocalDateTime now = LocalDateTime.now(); System.out.println(now); //获取日期时间信息 System.out.println(now.getYear()); System.out.println(now.getMonth()); System.out.println(now.getDayOfMonth()); System.out.println(now.getDayOfWeek()); System.out.println(now.getHour()); } }
-
日期时间的修改和比较
-
package com.newfeature; import org.junit.Test; import java.time.LocalDate; import java.time.LocalDateTime; public class Test08 { /** * 日期时间的修改 */ @Test public void test01(){ LocalDateTime now=LocalDateTime.now(); System.out.println("now:"+now); //修改日期时间,对日期时间的修改,对已存在的LocalDate对象,创建了模版,并不会修改原来的信息 LocalDateTime localDateTime=now.withYear(1988); System.out.println("now:"+now); System.out.println("修改后的:"+localDateTime); System.out.println("月份:"+now.withMonth(04)); System.out.println("天:"+now.withDayOfMonth(25)); System.out.println("小时:"+now.withHour(8)); //对当前日期时间的基础上,加上或减去指定的时间 System.out.println("两天后:"+now.plusDays(2)); System.out.println("10年后:"+now.plusYears(10)); System.out.println("6个月后: " + now.plusMonths(6)); System.out.println("10年前: " + now.minusYears(10)); System.out.println("半年前:" + now.minusMonths(6)); System.out.println("一周前:" + now.minusDays(7)); } /** * 日期时间比较 */ @Test public void test02(){ LocalDate now=LocalDate.now(); LocalDate date=LocalDate.of(2020,1,22); //jdk8实现日期比较 System.out.println("now.isAfter(date) = " + now.isAfter(date)); System.out.println("now.isBefore(date) = " + now.isBefore(date)); System.out.println("now.isEqual(date) = " + now.isEqual(date)); } }
注意:在进行日期时间修改时候,原来的localdate对象是不会被修改的,每次操作是返回一个新的localdate对象,所以多线程场景下是数据安全的
-
日期时间格式化
在jdk8中通过java.time.format.DateTimeFormatter类进行日期的解析和格式化操作 -
package com.newfeature; import org.junit.Test; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; public class Test8 { /** * 日期时间格式化 */ @Test public void test01(){ LocalDateTime now=LocalDateTime.now(); //指定格式,使用系统默认格式 DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME; //将日期时间转换为字符串 String format = now.format(isoLocalDateTime); System.out.println("format = " + format); //通过ofPattern方法来指定特定的格式 DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String format1 = now.format(dateTimeFormatter1); System.out.println("format1 = " + format1); //将字符串解析为一个日期时间类型 LocalDateTime parse = LocalDateTime.parse("1997-12-13 22:34:12", dateTimeFormatter1); System.out.println("parse = " + parse); } }
-
Instant类: jdk8中新增一个时间戳,内部保存了从1970年1月1日00:00:00以来的秒和纳秒
-
/** * Instant时间戳:可以用来统计时间消耗 */ @Test public void test02() throws InterruptedException { Instant now = Instant.now(); System.out.println("now = " + now); //获取从1970年1月1日 00:00:00到现在的纳秒 System.out.println(now.getNano()); Thread.sleep(5); Instant now1= Instant.now(); System.out.println("耗时:"+(now1.getNano()-now.getNano())); }
-
计算日期时间差
Jdk8提供了工具类:Duration/Period:计算日期时间差
1.Duration:用来计算两个时间差(LocalTime)
2.Period:用来计算两个日期差(LocalDate)package com.newfeature; import org.junit.Test; import java.time.Duration; import java.time.LocalDate; import java.time.LocalTime; import java.time.Period; public class Test9 { /** * 计算日期时间差 */ @Test public void test01(){ //计算时间差 LocalTime now = LocalTime.now(); LocalTime time = LocalTime.of(22, 45, 59); System.out.println("now = " + now); //通过Duration计算 时间差 Duration duration = Duration.between(now,time); System.out.println(duration.toDays()); System.out.println(duration.toHours()); System.out.println(duration.toMinutes()); System.out.println(duration.toSeconds()); System.out.println(duration.toMillis()); //计算日期差 LocalDate nowDate = LocalDate.now(); LocalDate date = LocalDate.of(1997, 12, 5); Period period = Period.between(date, nowDate); System.out.println("period.getYears() = " + period.getYears()); System.out.println("period.getMonths() = " + period.getMonths()); System.out.println("period.getDays() = " + period.getDays()); } }
-
时间校正器:将日期调整到下个月第一天 等操作,通过时间矫正器操作
1.TemporalAdjuster:时间 校正器
2.TemporalAdjusters:通过该类静态方法提供的常用TemporalAdjuster的实现 -
/** * 时间校正器 */ @Test public void test2(){ LocalDateTime now = LocalDateTime.now(); //将当前的日期调整到下个月的1号 TemporalAdjuster adjuster=temporal -> { LocalDateTime dateTime= (LocalDateTime) temporal; LocalDateTime nextMonth = dateTime.plusMonths(1).withDayOfMonth(1); System.out.println("nextMonth = " + nextMonth); return nextMonth; }; //通过TemporalAdjusters来实现 //LocalDateTime nextMonth = now.with(adjuster); LocalDateTime nextMonth=now.with(TemporalAdjusters.firstDayOfNextMonth()); System.out.println("nextMonth = " + nextMonth); }
-
日期时间的时区
jdk8中加入对时区的支持,LocalDate,LocalTime,LocalDateTime是不带时区的,带时区的日期时间类分别是:ZonedDate,ZonedTime,ZoneDateTime;其中每个时区都对应着ID,ID的格式为:区域/城市 eg:Asia/Shanghai等;zoneId:该类中包含的所有的时区信息 -
package com.newfeature; import org.junit.Test; import java.time.Clock; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; public class Test10 { /** * 时区操作 */ @Test public void test01(){ //1.获取所有的时区id //ZoneId.getAvailableZoneIds().forEach(System.out::println); //获取当前时间,使用的是东八区时区;比标准的时间早8个小时 LocalDateTime now=LocalDateTime.now(); System.out.println("now = " + now); //获取标准时间 ZonedDateTime bz = ZonedDateTime.now(Clock.systemUTC()); System.out.println("bz = " + bz); //使用计算机默认的时区,创建日期时间 ZonedDateTime now1=ZonedDateTime.now(); System.out.println("now1 = " + now1); //使用指定的时区创建日期时间 ZonedDateTime now2 = ZonedDateTime.now(ZoneId.of("America/Marigot")); System.out.println("now2 = " + now2); } }
jdk新的日期和时间API的优点:
1.新的日期时间API中,日期和时间对象是不可变,操作日期不会影响原来的值,而是生成一个新的实例
2.提供了两种不同的方式,区分人和机器的操作
3.TemporalAdjuster可以更精确的操作日期,还可以自定义日期调整期
4.线程安全
8 其他新特性
1.重复注解 :@Repeatable
使用步骤:
step1 定义重复注解容器:
package com.newfeature;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}
step2 定义一个可以重复的注解:
package com.newfeature;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
step3 配置多个注解:
package com.newfeature;
@MyAnnotation("test1")
@MyAnnotation("test12")
@MyAnnotation("test13")
public class AnnoTest01 {
@MyAnnotation("fun1")
@MyAnnotation("fun2")
public void test01(){
}
}
step4解析得到指定的注解:
/**
* 解析重复注解
* @param args
*/
public static void main(String[] args) throws NoSuchMethodException {
//获取类标注的重复注解
MyAnnotation[] annotationsByType = AnnoTest01.class.getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation myAnnotation : annotationsByType) {
String value = myAnnotation.value();
System.out.println("value = " + value);
}
//获取方法上标注的重复注解
MyAnnotation[] test01s = AnnoTest01.class.getMethod("test01").getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation test01 : test01s) {
System.out.println("test01 = " + test01.value());
}
}
2.类型注解
jdk8为@Target元注解新增两个类型:
TYPE_PARAMETER 表示该注解能写在类型参数的声明语句中。类型参数 声明如:
step1定义TYPE_PARAMETER :
package com.newfeature;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.TYPE_PARAMETER)
public @interface TypeParam {
}
step2 使用 :
package com.newfeature;
public class TypeDemo01<@TypeParam T> {
public <@TypeParam K extends Object> K test01(){
return null;
}
}
TYPE_USE 表示注解可以在任何用到的类型的地方使用.
step1定义
package com.newfeature;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.TYPE_USE)
public @interface NotNull {
}
step2使用
package com.newfeature;
public class TypeUseDemo01 {
public @NotNull Integer age=10;
public Integer sum(@NotNull Integer a,@NotNull Integer b){
return a+b;
}
}