赞
踩
创建一个线程,指定线程要执行的任务:
package jdk8.jdk8;
public class lambdaText01 {
public static void main(String[] args) {
//开启一个新线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("新线程中执行的代码:" + Thread.currentThread().getName());
}
}).start();
System.out.println("主线程的代码:" + Thread.currentThread().getName());
}
}
代码分析:
1、Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的核心;
2、为了指定run方法体,不得不需要Runnable的实现类;
3、为了省去定义一个Runnable的实现类,不得不使用匿名内部类;
4、必须覆盖写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不重写一遍,而且不能出错;
5、而实际上,我们只在乎方法体中的代码(也就是Runnable中的run方法)
针对上面的代码我们进行一个lambda的实现,
Lambda表达式是一个匿名函数,可以理解为一段可以传递的代码
package jdk8.jdk8;
public class lambdaText01 {
public static void main(String[] args) {
//开启一个新线程
//这里用lambda实现的
new Thread(()->{
System.out.println("Lambda新线程表达式..." + Thread.currentThread().getName());
}).start();
System.out.println("主线程的代码:" + Thread.currentThread().getName());
}
}
Lambda表达式的优点:简化了匿名内部类的使用,语法更加简单。
匿名内部类语法冗余,体验了lambda表达式后,发现lambda表达式是简化匿名内部类的一种方式。
Lambda省去了面向对象的一些条条框框(如:访问类型,函数类型…等),Lambda的标准格式由三部分组成:
(参数类型 参数名称)-> {
代码体;
}
格式说明:
练习无参无返回值的Lambda
首先定义了一个接口:
package jdk8.jdk8.service;
@FunctionalInterface
public interface Userservice {
void show();
}
随后创建一主方法运用:
package jdk8.jdk8;
import jdk8.jdk8.service.Userservice;
public class lambdaText02 {
public static void main(String[] args) {
//匿名内部类的实现
goShow(new Userservice() {
@Override
public void show() {
System.out.println("show 方法执行成功");
}
});
System.out.println("-------------------------------");
//lambda的实现
goShow(()->{
System.out.println("Lambda show 方法执行成功");
});
}
public static void goShow(Userservice userservice){
userservice.show();
}
}
输出结果:
show 方法执行成功
-------------------------------
Lambda show 方法执行成功
完成一个含有参数的Lambda表达式
首先创建一个Person类:
package jdk8.jdk8;
public class Person {
private String name;
private Integer age;
private Integer height;
public Person() {
}
public Person(String name,Integer age,Integer height){
this.name = name;
this.age = age;
this.height = height;
}
public Integer getAge() {
return age;
}
public Integer getHeight() {
return height;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
}
用Lambda对list集合进行排序
package jdk8.jdk8;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class lambdaText03 {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("徐美琪",19,160));
list.add(new Person("zs",20,170));
list.add(new Person("lisi",30,165));
list.add(new Person("mazi",51,180));
//匿名内部类的实现
/*Collections.sort(list,new Comparator<Person>(){
public int compare(Person o1,Person o2){
return o1.getAge() - o2.getAge();
}
});
for(Person person:list){
System.out.println(person);
}*/
System.out.println("----------------------------");
//Lambda表达式实现
Collections.sort(list,(o1,o2)->{
return o1.getAge() - o2.getAge();
});
for(Person person:list){
System.out.println(person.toString());
}
}
}
输出结果:
Person{name='徐美琪', age=19, height=160}
Person{name='zs', age=20, height=170}
Person{name='lisi', age=30, height=165}
Person{name='mazi', age=51, height=180}
@FunctionalInterface
这是一个标志注解,被该注解修饰的接口,只能声明一个抽象方法(如果被它修饰的接口,存在的不是一个抽象方法,会报错)
匿名内部类的本质是在编译时生成一个Class文件(.class)
Lambda表达式在程序运行时候会形成一个类:
1、在类中新增一个方法,这个方法的方法体就是Lambda表达式中的代码
2、还会形成一个匿名内部类,实现接口,重写抽象方法
3、在接口中重写方法会调用新生成的方法
在lambda表达式的标准写法基础上,可以省略写法的规则为:
1、小括号内从参数类型可以省略
2、如果有且只有一个参数,则小括号可以省略
3、如果大括号内有且只有一个语句,可以同时省略大括号,return关键字以及语句分号。
创建两个接口:
package jdk8.jdk8.service;
public interface StudentService {
String show(String name,Integer Age);
}
package jdk8.jdk8.service;
public interface OrderService {
void show(String name);
}
测试:
package jdk8.jdk8;
import jdk8.jdk8.service.OrderService;
import jdk8.jdk8.service.StudentService;
public class lambdaText04 {
public static void main(String[] args) {
//lambda表达式的完整写法(goStudent)
goStudent((String name,Integer age)->{
return name + age + " 666";
});
System.out.println("-------------------");
//省略写法
goStudent((name,age)->name+age+" 666");
System.out.println("----------------");
//lambda表达式完整写法(OrderService)
goOrder((String name)->{
System.out.println(name);
});
System.out.println("--------------------------");
//省略写法
goOrder(name-> System.out.println(name));
}
public static void goStudent(StudentService studentService){
studentService.show("zhangsan",19);
}
public static void goOrder(OrderService orderService){
orderService.show("lisi");
}
}
输出:
-------------------
----------------
lisi
--------------------------
lisi
Lambda表达式的语法是非常简洁的,但是lambda表达式不说随便使用的,使用有几个条件要特别注意:
1、方法的参数或者局部变量必须为接口中才能使用lambda
2、接口中有且仅有一个抽象方法(@FunctionalInterface)
(这里的一个不包括从Object类里继承的方法,由于Object类是所有类的父类,也就是如果一个接口中有Object方法的话,那么这个接口的实现类也实现它就相当于对Object中的方法进行了重写,这里的指的一个抽象方法是这个意思。
比如:Comparator接口中除了有compare方法还有个,equals方法,但仍然可以对Comparator接口进行lambda表达式的使用)
可以我往上面测试案例中加入了equals方法和hashcode方法都没有报错,说明在此没有把它算做接口的“抽象方法”.
lambda和匿名内部类的对比
1、所需类型不一样
a、匿名内部类的类型可以是类,抽象类或者接口
b、lambda表达式需要的类型只能是接口
2、抽象方法的数量不一样
a、匿名内部类所需的接口中的抽象方法的数量是随意的
b、lambda表达式所需的接口中只能有一个抽象方法
3、实现原理不一样
a、匿名内部类是在编译后形成一个class
b、lambda表达式是在程序运行的时候动态生成class
在JDK8中接口的新增,之前:
Interface 接口名{
静态常量;
抽象方法;
}
之后对接口做了新增,接口中可以有了默认方法和静态方法
Interface 接口名{
静态常量;
抽象方法;
默认方法;
静态方法;
}
在JDK8以前接口中只能由抽象方法和静态常量,会存在以下问题:
如果接口中新增抽象方法,那么实现类都必须要实现这个抽象方法,非常不利于接口的扩展的。
接口中默认方法的语法格式:
interface 接口名{
修饰符 default 返回值类型 方法名{
方法体;
} }
package jdk8.jdk8;
public class Demo01Interface {
public static void main(String[] args) {
A b = new B();
A c = new C();
System.out.println("-----------------");
//调用接口中的默认方法
b.text02();
}
}
interface A{
void test01();
//A中接口的默认方法
public default String text02(){
System.out.println("默认方法已被执行");
return "hello";
}
}
class B implements A{
@Override
public void test01(){
System.out.println("实现A接口text01方法");
}
public String text02(){
System.out.println("B重写了A中默认方法");
return "Bhello";
}
}
class C implements A{
public void test01(){
}
}
接口中的默认方法有两种使用方式
1、实现类对默认方法进行重写
2、实现类直接调用接口中的默认方法
JDK8中为接口新增了静态方法,作用也是为了接口的拓展
interface 接口名{
修饰符 static 返回类型 方法名{
方法体;
}}
1、接口中的静态方法在实现类中不能被重写
2、调用的话只能用接口名来调用:接口名.静态方法名()
package jdk8.jdk8;
public class Demo01Interface {
public static void main(String[] args) {
A b = new B();
A c = new C();
System.out.println("-----------------");
//调用接口中的默认方法
b.text02();
System.out.println("-------------");
A.text03();
}
}
interface A{
void test01();
/**
* 默认方法
* @return
*/
public default String text02(){
System.out.println("默认方法已被执行");
return "hello";
}
/**
* 接口中的静态方法
* @return
*/
public static String text03(){
System.out.println("A中的静态方法执行了");
return "xmq";
}
}
class B implements A{
@Override
public void test01(){
System.out.println("实现A接口text01方法");
}
public String text02(){
System.out.println("B重写了A中默认方法");
return "Bhello";
}
}
class C implements A{
public void test01(){
}
}
1、默认方法通过实例调用,静态方法通过接口名调用
2、默认方法可以被继承,实现类可以直接调用接口默认方法,也可以重写接口默认方法
3、静态方法不能被继承(都叫它类方法了,当然不能被继承了),实现类不能重写接口的静态方法,但继承关系仍然存在,可以使用接口名调用
lambda表达式的使用前提是需要有函数式接口,而lambda表达式使用时候不关心接口名,抽象方法名,只关心抽象方法的参数列表返回类型。因此为了让我们使用lambda表达式更加的方便,在JDK中提供了大量的函数式接口。
package jdk8.jdk8;
public class Demo01Fun {
public static void main(String[] args) {
fun1((arr)->{
int sum = 0;
for(int x:arr)
sum += x;
return sum;
});
}
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);
}
在JDK中帮我们提供的有函数式接口,主要是在java.util.function 包中。
无参有返回值(生产数据)
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
对Supplier进行个测试
package jdk8.jdk8;
import java.util.function.Supplier;
/**
* 函数式接口的使用
*/
public class supplierText {
public static void main(String[] args) {
printSum(()->{
int[] arr = {1,5,9,4,8,6,4};
int sum = 0;
//对数组求和
for(int num:arr)
sum += num;
return sum;
});
}
/**
*
* @param supplier
*/
private static void printSum(Supplier<Integer> supplier){
//无参有返回值
Integer sum = supplier.get();
System.out.println("sum = " + sum);
}
}
有参无返回值(消费数据)使用的时候需要指定一个泛型来定义参数类型
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
使用:将输入的数据统一转换成小写
package jdk8.jdk8;
import java.util.function.Consumer;
public class consumerText {
public static void main(String[] args) {
test(msg->{
System.out.println(msg + "--->转换成小写:" + msg.toLowerCase());
});
}
/**
*
* @param consumer
*/
private static void test(Consumer<String> consumer){
consumer.accept("Hello World!!!");
}
}
默认方法:andThen
如果一个方法的参数和返回值全部都是Consumer类型,那么就可以实现效果,消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合,而这个方法就是Consumer接口中的default方法andThen,andThen方法返回的是一个Consumer对象,然后再调用accept就可以实现先后操作的效果了。
下面举例:
package jdk8.jdk8;
import java.util.function.Consumer;
public class consumerText {
public static void main(String[] args) {
/*test(msg->{
System.out.println(msg + "--->转换成小写:" + msg.toLowerCase());
});*/
test02(msg1->{
System.out.println(msg1 + "--->转换成小写:" + msg1.toLowerCase());
},msg2->{
System.out.println(msg2 + "--->转换成大写:" + msg2.toUpperCase());
});
}
/**
*
* @param consumer
*/
private static void test(Consumer<String> consumer){
consumer.accept("Hello World!!!");
}
private static void test02(Consumer<String> consumer1,Consumer<String> consumer2){
String str = "Hello World";
//consumer1.accept(str);//转小写
//consumer2.accept(str);//转大写
consumer1.andThen(consumer2).accept(str);
}
}
有参有返回值,Function接口是根据一个类型的数据得到另一个数据类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。
@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 jdk8.jdk8;
import java.util.function.Function;
public class functionText {
public static void main(String[] args) {
test(msg->{
return Integer.valueOf(msg);
});
}
/**
* 传递一个字符串,返回该字符串数字
* @param function
*/
private 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 jdk8.jdk8;
import java.util.function.Function;
public class functionText {
public static void main(String[] args) {
test(msg->{
return Integer.valueOf(msg);
},msg2->{
return msg2 * 10;
});
}
/**
*可以理解先由function1操作,再由function2操作
* @param function1
* @param function2
*/
private static void test(Function<String,Integer> function1,Function<Integer,Integer> function2){
// Integer res1 = function1.apply("666");
// Integer res2 = function2.apply(res1);
// System.out.println("res2 = " + res2);
Integer res = function1.andThen(function2).apply("666");
System.out.println("res = " + res);
}
}
默认的compose方法顺序和andThen相反,而静态方法identity则是,输入什么参数就返回什么参数。
有参有返回结果(且返回类型为boolean型)
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
操作:对传入的字符串长度进行个判断
package jdk8.jdk8;
import java.util.function.Predicate;
public class predicateText {
public static void main(String[] args) {
test(msg->{
return msg.length()<6?false:true;
},"Hello World");
}
/**
*
* @param predicate
* @param msg
*/
private static void test(Predicate<String> predicate, String msg){
boolean flag = predicate.test(msg);
System.out.println("flag = " + flag);
}
}
在使用lambda表达式的时候,也会出现代码冗余的情况。
package jdk8.jdk8.function;
import java.util.function.Consumer;
public class methodRef01 {
public static void main(String[] args) {
toSum(arr->{
int sum = 0;
for(int num:arr)
sum += num;
System.out.println("sum = " + sum);
});
}
/**
* 对数组进行求和
* @param arr
*/
private static void totalSum(Integer[] arr){
int sum = 0;
for(int num:arr)
sum += num;
System.out.println("sum = " + sum);
}
/**
* 对数据进行”消费“
* @param consumer
*/
private static void toSum(Consumer<Integer[]> consumer){
Integer[] arr = {3,6,6,5,9,7,6,2,3,45};
consumer.accept(arr);
}
}
因为在lambda表达式中要执行的代码和我们另一个方法中的代码是一样的,这时就没有必要重写一份逻辑了,这时我们就可以“引用”重复代码。
package jdk8.jdk8.function;
import java.util.function.Consumer;
public class methodRef01 {
public static void main(String[] args) {
//:: 方法引用 也是JDK8新的语法
toSum(methodRef01::totalSum);
}
/**
* 对数组进行求和
* @param arr
*/
private static void totalSum(Integer[] arr){
int sum = 0;
for(int num:arr)
sum += num;
System.out.println("sum = " + sum);
}
/**
* 对数据进行”消费“
* @param consumer
*/
private static void toSum(Consumer<Integer[]> consumer){
Integer[] arr = {3,6,6,5,9,7,6,2,3,45};
consumer.accept(arr);
}
}
符号表示: ::
符号说明:双冒号为方法引用运算符,而它所在的表达式被称为方法引用
应用场景:如果lambda表达式所要实现的方案,已经有其他方法存在相同的方案,那么则可以使用方法引用。
常见的引用方式:
方法引用在JDK8中使用相当灵活,有以下几种方式:
1、InstanceName::methodName 对象::方法名
2、ClassName::staticMethodName 类名::静态方法
3、ClassName::methodName 类名::普通方法
4、ClassName::new 类名::new 调用的构造器
5、TypeName[]::new String[]::new 调用数组的构造器
package jdk8.jdk8.function;
import java.util.Date;
import java.util.function.Supplier;
public class methodRef02 {
public static void main(String[] args) {
Date now = new Date();
Supplier<Long> supplier = ()-> now.getTime();
System.out.println("时间: " + supplier.get());
//再试试通过方法引用的方式进行处理
Supplier<Long> supplier1 = now::getTime;
System.out.println("时间: " + supplier1.get());
}
}
输出结果:
时间: 1666766083734
时间: 1666766083734
方法引用注意事项:
1、被引用的方法,参数要和接口中的抽象方法的参数一样
2、当接口抽象方法有返回值时被引用的方法也必须有返回值
package jdk8.jdk8.function;
import java.util.function.Supplier;
public class methodRef03 {
public static void main(String[] args) {
//获取当前时间(毫秒为单位)
Supplier<Long> supplier = ()->System.currentTimeMillis();
System.out.println(supplier.get()); //*1
//用方法引用测试(currentTimeMillis)是System类中的静态方法
Supplier<Long> supplier1 = System::currentTimeMillis;
System.out.println(supplier1.get()); //*2
}
}
输出:
1666766539050
1666766539051
//从那个*1位置运行到*2位置用了一毫秒
Java面向对象中,类名只能调用静态方法,类名引用实例方法是要有前提的,实际上是拿第一个参数作为方法的调用者。
package jdk8.jdk8.function;
import jdk8.jdk8.Person;
import java.util.function.Supplier;
public class methodRef04 {
public static void main(String[] args) {
Supplier<Person> supplier = ()->new Person();
System.out.println(supplier.get());
//通过引用方法实现
Supplier<Person> supplier1 = Person::new;
System.out.println(supplier1.get());
}
}
package jdk8.jdk8.function;
import java.util.function.Function;
public class methodRef05 {
public static void main(String[] args) {
Function<Integer,String[]> function = (len)->new String[len];
String[] s = function.apply(3);
System.out.println("数组的长度为:" + s.length);
//使用方法引用的方式
Function<Integer,String[]> function1 = String[]::new;
String[] ss = function1.apply(5);
System.out.println("数组的长度为:" + ss.length);
}
}
方法引用是对lambda表达式符合特定情况下的一种缩写方式,它使得我们的lambda表达式更加的精简,也可以理解为lambda表达式的缩写形式。不过需要注意的是方法引用只能引用已经存在了的方法,解决lambda表达式的冗余问题。
当我们需要对集合中的元素进行操作的时候,除了必须的添加删除获取之外,最典型的操作就是集合的遍历(集合的外部迭代)。
package jdk8.jdk8.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class StreamText01 {
public static void main(String[] args) {
//定义一个List集合
List<String> list = Arrays.asList("xmq","zhangsan","mazi","zhanghanzi");
//1.获取所有姓张的信息
List<String> list1 = new ArrayList<>();
for(String s:list){
if(s.startsWith("zhang"))
list1.add(s);
}
//输出
for(String s: list1)
System.out.println("s = " + s);
System.out.println("----------------------");
//获取长度大于8的
List<String> list2 = new ArrayList<>();
for(String s: list1){
if(s.length()>8)
list2.add(s);
}
//输出
for(String s:list2)
System.out.println("s = " + s);
}
}
上面的代码根据我们不同的需求总是一次次的循环循环。这时我们希望有更加高效的处理方式,这时我们就可以通过JDK8中提供的Stream API来解决这个问题了(Stream的内部迭代)。
Stream有更加优雅的解决方案。
package jdk8.jdk8.stream;
import java.util.Arrays;
import java.util.List;
public class StreamText02 {
public static void main(String[] args) {
//定义一个List集合
List<String> list = Arrays.asList("xmq","zhangsan","mazi","zhanghanzi");
//1.获取所有姓张的信息
//2.获取长度大于8的
//3.输出获取的结果
list.stream()
.filter(s->s.startsWith("zhang"))
.filter(s->s.length()>8)
.forEach(s-> System.out.println(s));
System.out.println("-----------------------------");
//这里可以采用方法引用的方式输出
list.stream()
.filter(s->s.startsWith("zhang"))
.filter(s->s.length()>8)
.forEach(System.out::println);
}
}
上面的Stream API的含义:获取流,过滤长度,逐一打印。代码相比于上面的案例显得更加的简介直观。
注意:Stream和IO流(InputStream/OutputStream)没有任何关系。
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
Stream API能让我们快速完成许多复杂的操作,如筛选,切片,映射,查找,去除重复,统计,匹配和归约。
首先,java.util.Collection接口中加入了default默认方法 stream(),也就是说Collection接口下的所有实现都可以通过stream方法来获取Stream流。
如:
List<String> list = new ArrayList<>();
list.stream();
Set<String> set = new HashSet<>();
set.stream();
.......
但是map接口没有实现Colletion,但是我们可以通过map中的keySet,values,entrySet得到集合然后进行操作。
在实际开发中我们不可避免的会操作数组中的数据,由于数组对象不可能添加默认方法,所以Stream提供了静态方法of。
of的俩种形式:
一、可变参数的形式(可以传多个同类型T,也可以传T数组)
底层也是通过数组创建流的方式:
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
二、传入单个对象,对单个对象进行Stream流
测试:
package jdk8.jdk8.stream;
import java.util.stream.Stream;
public class StreamText03 {
public static void main(String[] args) {
Stream<String> a1 = Stream.of("a1","a2","a3");
String[] arr1 = {"aa","bb","cc"};
Stream<String> sarr1 = Stream.of(arr1);
Integer[] arr2 = {1,2,3};
Stream<Integer> sarr2 = Stream.of(arr2);
sarr2.forEach(System.out::println);
//注意:基本数据类型的数组是不行的;如果是基本数据类型的数组,那么就是整个arr3被当成对象传进去,这和of参数中泛型T有关
int[] arr3 = {1,2,3};
Stream.of(arr3).forEach(System.out::println);
}
}
输出:
1
2
3
[I@4dd8dc3
你可以使用静态方法Arrays.stream从数组创建一个流。它接受一个数组作为参数。例如,你可以将一个原始类型int的数组转换成一个IntStream。
如下所示:
int[] numbers = {2, 3, 5, 7, 11, 13}; int sum =
Arrays.stream(numbers).sum();
Stream流模型的操作很丰富,这里介绍一些常用的API,这些方法可以被分为两种:
1、终结方法:返回值类型不再是Stream类型的方法,不再支持链式调用。这里介绍了 count 和 forEach 俩大终结方法。
2、非终结方法:返回值类型仍然是Stream 类型的方法,支持链式调用。(除了终结方法外,其余方法都是非终结的方法)
3、终端方法我在后面打了爱心号
终端操作都是返回一个boolean(allMatch之类的)、void
(forEach)或Optional对象(findAny等)…
Stream 注意事项(重要)
package jdk8.jdk8.stream;
import java.util.stream.Stream;
public class StreamText04 {
public static void main(String[] args) {
Stream<String> sarr1 = Stream.of("a1","xmq","a2","aa");
sarr1.filter(s->{
System.out.println("------------");
return s.contains("a");
});//没有终结方法,最后没有输出任何东西
}
}
下面添加上终结方法就有输出了:
package jdk8.jdk8.stream;
import java.util.stream.Stream;
public class StreamText04 {
public static void main(String[] args) {
Stream<String> sarr1 = Stream.of("a1","xmq","a2","aa");
sarr1.filter(s->{
System.out.println("------------");
return s.contains("a");
}).forEach(System.out::println);
System.out.println("------------------");
}
}
输出结果:
------------
a1
------------//这里我们也可以发现“xmq”被过滤掉了
------------
a2
------------
aa
------------------
forEach用来遍历流中的数据的:
void forEach(Consumer<? super T> action);
该方法接受一个Consumer接口,会将每一个流元素交给函数 accept 处理
package jdk8.jdk8.stream;
import java.util.stream.Stream;
public class forEachText {
public static void main(String[] args) {
//这里正常写法
Stream.of("xmq","love","leyang").forEach(s->System.out.print(s + " "));
System.out.println();
//这里用了方法引用
Stream.of("xmq","leyang").forEach(System.out::println);
}
}
输出:
xmq love leyang
xmq
leyang
注意:别把这个当成循环用,所以什么continue,什么break别用,这里参数是Consumer ,用匿名类或者lambda实现,根据里面的 void accept方法,我们想结束该遍历可以return。
Stream 流中的count方法用来统计其中元素的个数(返回一个long值,代表元素的个数)
long count();
package jdk8.jdk8.stream;
import java.util.stream.Stream;
public class countText {
public static void main(String[] args) {
long count = Stream.of("shabi","is","you").count();
System.out.println("count = " + count);//count = 3
}
}
filter 方法的作用是用来过滤数据的,返回符合条件的数据
可以通过filter方法将一个流转换成另一个子集流。
Stream filter(Predicate<? super T> predicate);
该接口接受一个Predicate 函数式接口参数
package jdk8.jdk8.stream;
import java.util.ArrayList;
import java.util.List;
public class filterText {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("xmq");
list.add("a1");
list.add("bb");
list.add("aa");
//这里过滤掉开头为字母a的字符串
list.stream().filter(s->!s.startsWith("a")).forEach(System.out::println);//xmq bb
}
}
limit 方法可以对流进行截取处理,支取前 n 个数据
Stream limit(long maxSize);
package jdk8.jdk8.stream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class limitText {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student("xmq",19,87));
list.add(new Student("zs",20,90));
list.add(new Student("mz",21,79));
list.add(new Student("ly",20,87));
//这里对学生成绩进行排序,如果成绩相同按年龄,年龄小的在前,如果年龄还相同,那按名字字典排序
Collections.sort(list,(stu1,stu2)->{
if(stu1.score!=stu2.score)
return stu2.score-stu1.score;
else if(stu1.age!=stu2.age)
return stu1.age - stu2.age;
else
return stu1.name.compareTo(stu2.name);
});
//输出前三名
list.stream().limit(3).forEach(System.out::println);
}
}
class Student{
public String name;//姓名
public int age;//年龄
public int score;//成绩
public Student(String name,int age,int score){
this.name = name;
this.age = age;
this.score = score;
}
public Student() {
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
注意:如果传递的参数为负数,那么就会报错,如果传递的大于集合的本身长度,那么可以理解为不操作(在原集合中含有的元素上进行工作)
这个方法可以和limit 相对立,跳过前面几个元素,选取后面的元素
Stream skip(long n);
操作:
package jdk8.jdk8.stream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class skipText {
public static void main(String[] args) {
List<jdk8.jdk8.stream.Student> list = new ArrayList<>();
list.add(new jdk8.jdk8.stream.Student("xmq",19,87));
list.add(new jdk8.jdk8.stream.Student("zs",20,90));
list.add(new jdk8.jdk8.stream.Student("mz",21,79));
list.add(new jdk8.jdk8.stream.Student("ly",20,87));
//这里对学生成绩进行排序,如果成绩相同按年龄,年龄小的在前,如果年龄还相同,那按名字字典排序
Collections.sort(list,(stu1, stu2)->{
if(stu1.score!=stu2.score)
return stu2.score-stu1.score;
else if(stu1.age!=stu2.age)
return stu1.age - stu2.age;
else
return stu1.name.compareTo(stu2.name);
});
//输出不是前三名的学生
list.stream().skip(3).forEach(System.out::println);//输出mz那个学生
}
}
常用的数据处理工具。
如果我们需要将流中的元素映射到另一个流中,我们可以使用map方法(看参数是 Function 接口我们也是比较好理解的):
Stream map(Function<? super T, ? extends R> mapper);
该方法需要一个Function 函数式接口参数,可以将当前流中的T类型数据转换成另一种R类型的数据。
操作:
package jdk8.jdk8.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Stream;
public class mapText {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String date = in.next();//输入的格式是 年-月-日
String[] dates = date.split("-");
List<Integer> list = new ArrayList<>();
Stream.of(dates).map(Integer::valueOf).forEach(num->list.add(num));//这里用了方法引用的方式,Integer::valueOf
//Stream.of(dates).map(s->Integer.valueOf(s)).forEach(num->list.add(num));
//Stream.of(dates).map(s->Integer.valueOf(s)).forEach(System.out::println);
list.stream().forEach(System.out::println);//这样我们就将年月日三个数字得到了,可以进行操作了
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。