有以下代码:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("新线程中执行的代码"+Thread.currentThread().getName());
}
}).start();
代码分析:
上面代码可以写成lambda形式如下:
new Thread(()->{
System.out.println("lambda表达式新线程"+Thread.currentThread().getName());
}).start();
Lambda表达式是一个匿名函数,可以理解为一段可以传递的代码。
lambda运行就是将函数作为一个参数传递到方法中。用以简化接口的使用,让代码更加简洁。
lambda的基础语法:
(参数类型 参数1,参数类型 参数2,...)->{
代码体;
}
格式1:无参,无返回值
public interface UserService {
void show();
}
public class Demo02Lambda {
public static void main(String[] args) {
goShow(new UserService() {
@Override
public void show() {
System.out.println("show方法执行了");
}
});
goShow(()->{
System.out.println("lambda show方法执行了");
});
}
public static void goShow(UserService userService){
userService.show();
}
}
格式2:有参且有返回值的
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
private Integer height;
}
public class Demo03Lambda {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add(new Person("zans",12,156));
list.add(new Person("lis",13,170));
list.add(new Person("ww",21,168));
list.add(new Person("zl",32,163));
// Collections.sort(list, new Comparator() {
// @Override
// public int compare(Person o1, Person o2) {
// return o1.getAge()-o2.getAge();
// }
// });
Collections.sort(list,((o1, o2) -> {
return o1.getAge()-o2.getAge();
}));
for (Person person : list) {
System.out.println(person);
}
}
}
在lambda表达式的标准写法基础上,可以使用省略写法的规则为:
例如下面例子:
public interface OrderService {
String show(String name);
}
public interface StudentService {
String show(String name,Integer age);
}
public class Demo04Lambda {
public static void main(String[] args) {
goStudent((String name,Integer age)->{
return name+age+"666";
});
//省略写法
goStudent((name,age)->name+age+"123");
goOrder((String name)->{
return name+"333";
});
//省略写法
goOrder(name->name+"123");
}
public static void goStudent(StudentService studentService){
studentService.show("sja",22);
}
public static void goOrder(OrderService orderService){
orderService.show("lili");
}
}
Lambda表达式的使用前提
Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意:
1.方法的参数或局部变量类型必须为接口才能使用Lambda
2.接口中有且仅有一个抽象方法(@Functionallnterface)
//@FunctionalInterface:被该注解修饰的接口只能有一个抽象方法
@FunctionalInterface
public interface UserService {
void show();
}
Lambda和匿名内部类的对比
1.所需类型不—样
2.抽象方法的数量不—样
3.实现原理不—样
如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。
我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。
Lambda表达式的本质:作为函数式接口的实例
Java8中关于Lambda表达式提供的4个基本的函数式接口(作用:在写lambda表达式的时候没有必要自己声明接口):
Consumer(有参无返回值的接口),对于lambda表达式需要提供一个返回数据的类型,是用来消
费数据的,使用时需要指定一个泛型来定义参数类型。源码如下:
@FunctionalInterface
public interface Consumer {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
使用:
public class ConsumerText {
public static void main(String[] args) {
test(msg->{
System.out.println(msg+"->转换为小写"+msg.toLowerCase());
});
}
public static void test(Consumer consumer){
consumer.accept("Hello Word");
}
}
默认方法:andThen
如果一个方法的参数和返回值全部是Consumer类型,那么就可以实现效果,消费一个数据的时
候,首先做一个操作,然后再做一个操作,实现组合,而这个方法就是Consumer接口中的default
方法andThen方法。
public class ConsumerText {
public static void main(String[] args) {
test(msg1->{
System.out.println(msg1+"->转换为小写"+msg1.toLowerCase());
},msg2->{
System.out.println(msg2+"->转换为小写"+msg2.toUpperCase());
});
}
public static void test(Consumer c1,Consumer c2){
String str = "Hello Word";
// c1.accept("Hello Word");
// c2.accept("Hello Word");
c1.andThen(c2).accept(str);
c2.andThen(c1).accept(str);
}
}
Function(有参有返回值接口),Function接口是根据一个类型的数据得到另一个类型的数据,前
者称为前置条件,后者称为后置条件。源码如下:
public interface Function {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
使用:传递一个字符串,返回一个数字
public class FunctionTest {
public static void main(String[] args) {
test(msg->{
return Integer.parseInt(msg);
});
}
public static void test(Function function){
Integer apply = function.apply("666");
System.out.println("apply="+apply);
}
}
默认方法:andThen,也是用来进行组合操作,compose就是andthen反过来的效果,而静态方法
identity则是输入什么参数就返回什么参数 。
public class FunctionTest {
public static void main(String[] args) {
test(msg->{
return Integer.parseInt(msg);
},msg2->{
return msg2 * 10;
});
}
public static void test(Function function1,Function function2){
Integer i2 = function1.andThen(function2).apply("88");
Integer i2 = function2.compose(function1).apply("88");
System.out.println("i2"+i2);
}
}
Supplier(无参有返回值类型,是用来生产数据的)
@FunctionalInterface
public interface Supplier {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
使用:
import java.util.Arrays;
import java.util.function.Supplier;
public class SupplierTest {
public static void main(String[] args) {
fun1(()->{
int arr[]={22,11,56,42,99,8};
//计算出数组中的最大值
Arrays.sort(arr);
return arr[arr.length-1];
});
}
private static void fun1(Supplier supplier){
Integer max = supplier.get();
System.out.println("max="+max);
}
}
Predicate(有参,返回值为Boolean型接口)
@FunctionalInterface
public interface Predicate {
/**
* 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);
default Predicate and(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
default Predicate negate() {
return (t) -> !test(t);
}
default Predicate or(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static Predicate isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
使用:如果msg长度大于3,返回true
public class predicateTest {
public static void main(String[] args) {
test(msg->{
return msg.length()>3;
},"hahahha");
}
public static void test(Predicate predicate,String msg){
boolean b = predicate.test(msg);
System.out.println("b"+b);
}
}
在Predicate中的默认方法提供了逻辑关系操作 and or negate isEquals方法
public class predicateTest {
public static void main(String[] args) {
test(msg1->{
return msg1.contains("H");
},msg2->{
return msg2.contains("W");
});
}
public static void test(Predicate p1,Predicate p2){
//P1包含H,同时p2包含w
boolean pp1 = p1.and(p2).test("Hello");
//P1包含H,或者p2包含w
boolean pp2 = p1.or(p2).test("Hello");
//p1不包含H
boolean pp3 = p1.negate().test("Hello");
}
}
在Lambda表达式中要执行的代码和我们另一个方法中的代码是一样的,这时就没有必要重写一份
逻辑了,这时我们就可以“引用”重复代码。
public class FunctionRefTest02 {
public static void main(String[] args) {
printMax(FunctionRefTest02::getTotal);
}
public static void getTotal(int a[]){
int sum=0;
for (int i : a) {
sum+=i;
}
System.out.println("数组织和:"+sum);
}
private static void printMax(Consumer consumer){
int[] a = {10,20,30,40,50};
consumer.accept(a);
}
}
符号表示 ::
应用场景:如果Lambda表达式所要实现的方案,已经有其他方法存在相同的方案,那么则可以使用方法引用。
常见的引用方式:
方法引用在JDK8中使用是相当灵活的,有以下几种形式:
1. instanceName::methodName 对象::方法名
2. ClassName::staticMethodName 类名::静态方法
3. ClassName::methodName 类名::普通方法
4. ClassName::new 类名::new 调用的构造器
5. TypeName[]::new String[]::new 调用数组的构造器
方法引用的注意事项:
1. 被引用的方法,参数要和接口中的抽象方法的参数一样
2. 当接口抽象方法有返回值时,被引用的方法也必须有返回值
这是最常见的一种用法。如果一个类中已经存在了一个成员方法,则可以通过对象名引用成员方法
public class FunctionRefTest3 {
public static void main(String[] args) {
Date now = new Date();
Supplier supplier = ()->{return now.getTime();};
System.out.println(supplier.get());
//通过方法引用
Supplier supplier1 =now::getTime;
System.out.println(supplier1.get());
}
}
public class FunctionRefTest4 {
public static void main(String[] args) {
Supplier supplier1 = ()->{
return System.currentTimeMillis();
};
System.out.println(supplier1.get());
//通过方法引用
Supplier supplier2 = System::currentTimeMillis;
System.out.println(supplier2.get());
}
}
Java面向对象中,类名只能调用静态方法,类名引用实例方法是有前提的,实际上是拿第一个参数
作为方法的调用者。
public class FunctionRefTest5 {
public static void main(String[] args) {
Function function=(s)->{
return s.length();
};
System.out.println(function.apply("hello"));
//通过方法引用实现
Function function2=String::length;
System.out.println(function2.apply("hahahha"));
BiFunction function3 = String::substring;
System.out.println(function3.apply("hhhhhhhh", 3));
}
}
由于构造器的名称和类名完全一致,所以构造器引用使用 ::new 的格式使用
public class FunctionRefTest6 {
public static void main(String[] args) {
Supplier person1 = () -> {
return new Person();
};
System.out.println(person1.get());
//方法引用
Supplier person2 = Person::new;
System.out.println(person2.get());
BiFunction function = Person::new;
System.out.println(function.apply("zs", 22));
}
}
public class FunctionRefTest7 {
public static void main(String[] args) {
Function fun = (len)->{
return new String[len];
};
String[] strings = fun.apply(3);
System.out.println("数组的长度:"+strings.length);
//方法引用,调用数组的构造器
Function fun1 = String[]::new;
String[] strings1 = fun1.apply(5);
System.out.println("数组的长度:"+strings1.length);
}
}
当我们在需要对集合中的元素进行操作的时候,除了必需的添加,删除,获取外,最典型的操作就
是集合遍历。针对与我们不同的需求总是一次次的循环,这时我们希望有更加高效的处理方式,就
可以通过JDK8中提供的Stream API来解决这个问题。
public class FunctionRefTest8 {
public static void main(String[] args) {
//定义一个集合
List list = Arrays.asList("张三","张三丰","成龙","周星驰");
// 1.获取所有 姓张的信息
// 2.获取名称长度为3的用户
// 3. 输出所有的用户信息
list.stream().filter(s->s.startsWith("张"))
.filter(s->s.length()==3)
.forEach(System.out::println);
}
}
注意:Stream和IO流(InputStream/OutputStream)没有任何关系。
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是
对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一
个原材料加工成一个商品。
首先,java.util.Collection 接口中加入了default方法 stream,也就是说Collection接口下的所有的实
现都可以通过steam方法来获取Stream流。
public class streamTest1 {
public static void main(String[] args) {
ArrayList
但是Map接口别没有实现Collection接口,那这时怎么办呢?这时我们可以根据Map获取对应的key
value的集合。
public class streamTest1 {
public static void main(String[] args) {
HashMap map = new HashMap<>();
Stream stream = map.keySet().stream();
Stream stream1 = map.values().stream();
Stream> stream2 = map.entrySet().stream();
}
}
在实际开发中我们不可避免的还是会操作到数组中的数据,由于数组对象不可能添加默认方法,所
有Stream接口中提供了静态方法of。
public class streamTest1 {
public static void main(String[] args) {
Stream a = Stream.of("a", "b", "c");
String[] a1 = {"aa","bb","cc"};
Stream a11 = Stream.of(a1);
Integer[] c1 = {1,2,3,4};
Stream c11 = Stream.of(c1);
//基本数据类型不能获取stream
int[] d1 = {1,2,3,4};
Stream.of(d1).forEach(System.out::println);
}
}
终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。本小节中,终结方法包括 count 和forEach 方法。
非终结方法:返回值类型仍然是 Stream 类型的方法,支持链式调用(除了终结方法外,其余方法均为非终结方法)。
Stream注意事项(重要):
forEach用来遍历流中的数据,该方法接收一个Consumer接口,会将一个流元素交给函数处理
void forEach(Consumer super T> action);
public class streamTest1 {
public static void main(String[] args) {
Stream.of("a","s","d","f").forEach(System.out::println);
}
}
tream流中的count方法用来统计其中的元素个数
long count();
public class streamTest1 {
public static void main(String[] args) {
long count = Stream.of("a", "s", "d", "f").count();
System.out.println(count);
}
}
filter方法的作用是用来过滤数据的。返回符合条件的数据
Stream
filter(Predicate super T> predicate);
public class streamTest1 {
public static void main(String[] args) {
Stream.of("ab","scd" ,"sz2", "ded", "f2")
.filter(s->s.length()>2)
.filter(s->s.startsWith("s"))
.filter(s->s.contains("z")).forEach(System.out::println);
}
}
Stream
limit(long maxSize);
public static void main(String[] args) {
Stream.of("a1", "a2", "a3","bb","cc","aa","dd")
.limit(3)
.forEach(System.out::println);
}
如果希望跳过前面几个元素,可以使用skip方法获取一个截取之后的新流:
Stream
skip(long n);
public static void main(String[] args) {
Stream.of("a1", "a2", "a3","bb","cc","aa","dd")
.skip(3)
.forEach(System.out::println);
}
如果我们需要将流中的元素映射到另一个流中,可以使用map方法:
Stream
map(Function super T, ? extends R> mapper);
public class streamTest1 {
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);
}
}
Stream
sorted();
public class streamTest1 {
public static void main(String[] args) {
Stream.of("1","2" ,"3","4","5")
.map(Integer::parseInt)
// .sorted()//自然排序
.sorted((o1,o2)->o2-o1)//根据比较器指定排序规则(倒序)
.forEach(System.out::println);
}
}
Stream
distinct();
public class streamTest1 {
public static void main(String[] args) {
Stream.of("1","2" ,"2","4","1")
.distinct()
.forEach(System.out::println);
Stream.of(
new Person("zs",21)
,new Person("lis",21)
,new Person("zs",21)
).distinct().forEach(System.out::println);
}
}
Stream流中的distinct方法对于基本数据类型是可以直接去重的,但是对于自定义类型,我们是需
要重写hashCode和equals方法来移除重复元素。
如果需要判断数据是否匹配指定的条件,可以使用match相关的方法
boolean anyMatch(Predicate super T> predicate); // 元素是否有任意一个满足条件
boolean allMatch(Predicate super T> predicate); // 元素是否都满足条件
boolean noneMatch(Predicate super T> predicate); // 元素是否都不满足条件
public class streamTest1 {
public static void main(String[] args) {
boolean b = Stream.of("1", "4", "5", "3", "6")
.map(Integer::parseInt)
// .anyMatch(s->s>3)
// .allMatch(s->s>0)
.noneMatch(s -> s > 7);
System.out.println(b);
}
}
Optional
findFirst(); Optional
findAny();
public class streamTest1 {
public static void main(String[] args) {
Optional first = Stream.of("1", "4", "5", "3", "6")
.findFirst();
System.out.println(first.get());
Optional any = Stream.of("1", "4", "5", "3", "6").findAny();
System.out.println(any.get());
}
}
Optional
min(Comparator super T> comparator); Optional
max(Comparator super T> comparator);
public class streamTest1 {
public static void main(String[] args) {
Optional max = Stream.of("1", "4", "5", "3", "6")
.map(Integer::parseInt)
.max((o1, o2) -> o1 - o2);
System.out.println(max.get());
Optional min = Stream.of("1", "4", "5", "3", "6")
.map(Integer::parseInt).min((o1, o2) -> o1 - o2);
System.out.println(min.get());
}
}
归并计算,为终端操作,可以对中间操作筛选出来的流数据作出一系列的加减乘除等操作。
T reduce(T identity, BinaryOperator
accumulator);
public class streamTest1 {
public static void main(String[] args) {
Integer reduce = Stream.of("1", "4", "5", "3", "6")
.map(Integer::parseInt)
.reduce(0, Integer::sum);
System.out.println(reduce);
Integer reduce1 = Stream.of("1", "4", "5", "3", "6")
.map(Integer::parseInt)
.reduce(0, (x, y) -> x > y ? x : y);
System.out.println(reduce1);
}
}
public class streamTest1 {
public static void main(String[] args) {
// 1.求出所有年龄的总和
Optional reduce = Stream.of(
new Person("zs", 12)
, new Person("zs", 23)
, new Person("zs", 25)
).map(Person::getAge).reduce(Integer::sum);
System.out.println(reduce.get());
// 1.求出所有年龄的最大值
Optional reduce1 = Stream.of(
new Person("zs", 12)
, new Person("zs", 23)
, new Person("zs", 25)
).map(Person::getAge).reduce(Math::max);
System.out.println(reduce1.get());
//统计字符a出现的次数
Integer total = Stream.of("a", "a", "a", "b", "b", "c").map(s -> "a".equals(s) ? 1 : 0).reduce(0, Integer::sum);
System.out.println(total);
}
}
public class streamTest1 {
public static void main(String[] args) {
Stream stream1 = Stream.of("1", "2", "3");
Stream stream2 = Stream.of("4", "5", "6");
Stream.concat(stream1,stream2).forEach(System.out::println);
}
}
如果需要将Stream中的Integer类型转换成int类型,可以使用mapToInt方法来实现
public class streamTest1 {
public static void main(String[] args) {
Integer arr[] = {1,2,3,4,5};
IntStream toInt = Stream.of(arr).mapToInt(Integer::intValue);
System.out.println(toInt);
}
}