java8新特性
在前面已经见过一些东西了,但是:挖得有坑儿
1、lambda表达式
- lambda表达式是jdk1.8引入的全新语法特性
- 它支持的是:只有单个抽象方法的函数式接口。什么意思?
- 就是说:这个接口下面只有一个抽象方法是真正属于自己的( 像那种从Object类里面继承过来的不算 ———— 所以这样严格来讲:这个接口里面就只有一个抽象方法是真正属于自己的 )
- 什么时候会用这个东西? ———— 总的一句话是:传参的时候是一个函数式接口就会用( 这句话包含了很多东西,这里不理解没事儿,学完了就懂了 )
- 1、简化匿名内部类 ( 学完这个lambda表达式之后,回过来采用 1、接口 ——> 实现类( new实现类对象,调接口中的方法 )、2、把接口的实现类弄成内部类( 成员、局部、匿名 )、3、弄成lambda表达式的样子,这样一步一步简化,推导出lambd表达式,最后一对比就突然发现lambda表达式好简单了( 注意:接口中那个抽象方法是单个参数、多个参数( lambda加不加()小括号 )、无返回值、有返回值( lambda写不写返回值类型 )、业务代码为一句、多句( lambda要不要加花括号{} 的情况 ) ) ———— 这句话很重要,记住这句话,然后弄完我后面说的这些之后,再回过来实践一下我说的这句话,会收获很多,只要把这句话搞懂了,lambda表达式就彻底懂了
- 以前玩儿匿名内部类的时候不是弄过一点吗,回顾一下
-
package cn.xieGongZi.reviewInnerClass; import java.util.TreeSet; // 回顾一下以前玩匿名内部类
public class Demo { public static void main(String[] args) { // 以前在treeSet中研究过它的底层排序原理,所以再来玩儿一下
TreeSet<Integer> treeSet = new TreeSet<>(); treeSet.add(20);
treeSet.add(30);
treeSet.add(1);
treeSet.add(9);
treeSet.add(10);
treeSet.add(18); System.out.println( treeSet ); // 这是升序排列,那我想降序排列呢? }
}来嘛:改变排序规则
-
package cn.xieGongZi.reviewInnerClass; import java.util.Comparator;
import java.util.TreeSet; // 回顾一下以前玩的匿名内部类
public class Demo { public static void main( String[] args ) { // 来吧:改变排序规则 ———— 重写comparator接口( 匿名内部类 )
TreeSet<Integer> treeSet = new TreeSet<>( new Comparator<Integer>() {
@Override
public int compare( Integer o1, Integer o2 ) {
// 注意观察这里 ( 这就是传递的参数、及参数个数 )
// 记得用这个写法 和 lambda需要传参时的写法作对比
return o2 - o1; // 改变比较规则
}
}); treeSet.add(20);
treeSet.add(30);
treeSet.add(1);
treeSet.add(9);
treeSet.add(10);
treeSet.add(18); System.out.println( treeSet ); }
}这样虽然是把排序规则改变了,但是不麻烦吗?
- 所以:lambda表达式来了————再来回顾一下lambda的表达式是怎么样的?
- 【对象名】 ( 参数 )-> { 集合体( 业务代码 )} ———— 那再来玩儿一下
-
package cn.xieGongZi.reviewInnerClass; import java.util.TreeSet; public class UseLambda { public static void main(String[] args) { // 来吧:改变排序规则 ———— 使用lambda表达式
TreeSet<Integer> treeSet = new TreeSet<>( ( o1,o2 )-> {
/*
1、为什么这里写两个o1,o2的参数,然后加个{}
这个表达式就可以去执行相应的东西
即:这个表达式怎么知道:老衲重写的是Comparator接口?
2、另外一个问题:看起来这个o1,o2貌似没有值吧
它不是参数吗,在这里看起来没赋值啊,所以最后怎么可以得出想要的结果?
*/
return o2 - o1; // 改变比较规则————即:业务代码
} ); // 记得用这里的写法 和 前面匿名内部类那里的写法作对比 ———— 会发现lambda表达式如此的简单
// 记得尝试模拟一个参数、多个参数、有返回值、无返回值的写法 ( 当然:不是用这个、而是自己设计 )
treeSet.add(20);
treeSet.add(30);
treeSet.add(1);
treeSet.add(9);
treeSet.add(10);
treeSet.add(18); System.out.println(treeSet);
}
}效果图如下:
-
- 为了解决解决上述代码中的两个问题,来看一下如下代码( 顺便引申出函数式接口是什么意思 )
package cn.xieGongZi.functionInterface; public class Test { public static void main(String[] args) { // 开始玩儿
test test = new test();
test.doAdd( new TestFunctionInterface() {
/*
这中间的这一堆就是一个对象嘛 ———— 一个TestFunctionInterface对象
这里面是用了这个对象 去 调用了TestFunctionInterface中的add()方法
所以结果来了:lambda表达式中有一个对象名,这个对象名就是这里的TestFunctionInterface对象
传递的参数有没有值:有,为什么?
就是调用doAdd()方法的时候不得需要一个TestFunctionInterface对象吗,虽然使用lambda可以不用对象名
但是:参数是必写的 ( 因为add( int a ) 需要一个int类型的a ,如果add()没有参数就可以不写 )
然后这个对象就把这个参数a携带进去了,那这个对象把参数携带去了哪里?
把这个值赋给了重写的add( int a )方法里面需要的这个int a参数了嘛 因此:总结一句话就是lambda表达式就是指:
把一段代码( 如:t.add( int a ) —— 这里面可能需要携带参数,也可能不需要、甚至可能是一个、多个
需不需要携带参数,看那个接口中的抽象方法需不需要 )
传递给了某一个函数
最后让这个函数( 就是lambda做的事情 )去执行相关的操作( 这个操作是怎么样的,就是自己写的业务代码 )
要真正理解lambda表达式,关键就是这里的回调,就这里会绕一下
*/
@Override
public void add( int a ) {
// 正常是这么玩儿的————重写这个方法
System.out.println("老衲自东土大唐而来....." + a );
}
}); /*
这里面doAdd()中是不是需要传一个TestFunctionInterface对象
这个对象指向的那个类型,就是他喵说的函数式接口( 这个接口只有一个真正属于自己的抽象方法 )
在java中是不是函数式接口,会有一个注解来进行说明@FunctionalInterface
这个就是一个声明性的东西而已
就相当于是给这个接口做了一个标记,标记它是一个函数式接口
*/
}
} // 定义一个接口
interface TestFunctionInterface{ // 这里面只有这一个方法是真正属于这个接口的
void add( int a );
} class test{ public void doAdd( TestFunctionInterface t ){
/*
这个t就相当于是我们使用lambda时的那个对象名( 用lambda时可写可不写,看情况来做 )
*/
t.add( int a ); // t.add()就相当于是lambda做的事情
// 这里用TestFunctionInterface对象t 回调了 TestFunctionInterface中的抽象方法
}
}
-
再继续研究:函数式接口有哪些类型( 即:分为哪几种 )
-
1、消费型( Consumer) ——— 又懵逼,这是啥子鬼玩意儿? ———— 看源码涩
-
把源码提取出来分析一下
-
@FunctionalInterface
public interface Consumer<T> { void accept(T t); // default Consumer<T> andThen(Consumer<? super T> after) { // 看不懂这里的话,那这一段先不看,这样是不是就很明白了
// Objects.requireNonNull(after); // 这他喵的这接口里面不就只有accept()这一个抽象方法吗
// return (T t) -> { accept(t); after.accept(t); }; // 这个单词已经在网络编程中见过了涩———— accept 接收
}
}通过看这个源码,其实什么是消费型函数式接口已经很明显了( 这里有一个注意点:别看这个方法是在做什么,而是应该关注:这个方法表达的逻辑是什么? )
-
accept 接收,那在现实生活中,爪子是接收? ———— 拿个东西给别人,别人要接收吧
-
所以:void accept( T t )所表达的意思已经非常非常明显了 ———— 如果:还有点懵,那么改动一下 void accept( Object obj ) ————这里只是为了好理解所以用了Object,严格来讲:这个参数类型是由传进来的那个泛型决定的( 所以:注意是支持泛型的啊 )
-
表明:
- 1、传递一个参数给接口,这个接口中的方法( 即:自己要写的业务代码 ),执行完之后,不需要返回值 ( 即:void )
- 2、传递进去的参数可以是任意类型
-
表明:
-
因此:总结————lambda表达式可以执行的 消费者类型的函数式接口是指什么?
- 使用方法时,这个方法的参数是一个接口类型的 ( 注:这个接口要保证只有一个抽象方法,不然lambda识别不了 ,是多个抽象方法的话,它不知道去找哪一个抽象方法)
- 然后传递进去一个 / 多个 / 无参给你( lambda去执行的那个函数 ,即:接口中的那个抽象方法 ),lambda执行完之后不需要给我返回值 ( 即:中间写的那部分业务代码 ) ————这种接口类型的参数问题就可以使用lambda表达式来简化代码
-
所以:void accept( T t )所表达的意思已经非常非常明显了 ———— 如果:还有点懵,那么改动一下 void accept( Object obj ) ————这里只是为了好理解所以用了Object,严格来讲:这个参数类型是由传进来的那个泛型决定的( 所以:注意是支持泛型的啊 )
-
accept 接收,那在现实生活中,爪子是接收? ———— 拿个东西给别人,别人要接收吧
-
-
-
1、消费型( Consumer) ——— 又懵逼,这是啥子鬼玩意儿? ———— 看源码涩
-
再继续研究:函数式接口有哪些类型( 即:分为哪几种 )
- 2、供给型( Supplier ),直接上源码
-
@FunctionalInterface
public interface Supplier<T> { /**
* Gets a result.
*
* @return a result
*/
T get();
}得出结论:
- 传递的接口参数对应的接口中抽象方法是无参数、有返回值类型
- 实例:就是前面对TreeSet集合改写排序规则那个例子就是,后面的实例不想写了
-
3、断言型( Predicate ),上源码————这个接口别懵了,因为它里面还有其他的抽象方法( 但是真正属于这个接口的只有一个 )
-
@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);
}得出结论:
- 给接口中的抽象方法传递一个任意类型的参数( 自己看情况决定是什么类型 ),然后返回一个boolean值
-
-
3、断言型( Predicate ),上源码————这个接口别懵了,因为它里面还有其他的抽象方法( 但是真正属于这个接口的只有一个 )
-
4、函数型( 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); // R是返回值,t是参数
}得出结论:
- 给接口中的抽象方法传递一个参数、需要一个任意类型的返回值( 自己控制类型 )
-
-
4、函数型( Function ),也是直接上源码
-
lambda表达式的一些精要
-
lambda表达式的一些精要
-
lambda中的方法引用
-
List<String> list = Arrays.asList("张三", "李四", "王五", "赵六"); // lambda用法
// list.forEach( t-> System.out.println(t) ); // 这是正常玩的,另外forEach语句不说明,就是遍历的一个方法( 自行百度 ) // 方法引用
list.forEach( System.out::println ); // 这是指:遍历list这个集合,然后打印出每一个值( 这里的System.out::println就是方法引用
// ::就是引用的意思
-
-
lambda中的方法引用
2、Stream流,不是IO里面的那个流,这里是数据操作流( 也就是一个流程的流 )
-
这个流程是个什么样的?
-
1、来玩儿数据源( 即:创建流 )
package cn.xieGongZi.Stream; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream; public class Demo { public static void main(String[] args) { // 创建流
// 1、通过集合创建流( 最常用,这个东西应用的最多的地方也就是集合中 )
// 先搞一个数据嘛————不过搞复杂一点儿( 因此:再来一个员工类 Employee ) // 开始准备填充数据
//准备数据
List<Employee> list = new ArrayList<>();
list.add(new Employee("刘德华", "歌手", "男", 1200.0));
list.add(new Employee("张旭阳", "演员", "男", 1400.0));
list.add(new Employee("小沈阳", "演员", "男", 1300.0));
list.add(new Employee("王宝强", "演员", "男", 1900.0));
list.add(new Employee("姚明", "歌手", "男", 2200.0));
list.add(new Employee("张杰", "主持人", "男", 3100.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0)); // 创建流————1、集合创建
Stream<Employee> stream1 = list.stream();
System.out.println( stream1 ); // java.util.stream.ReferencePipeline$Head@4554617c,就是一个对象,看不出啥
// 创建流嘛,看不出什么东西正常,也比较无聊 // 2、利用Arrays工具类创建
// Arrays.stream(); // 但是:这个需要传递一个任意类型的数组
Stream<Object> stream2 = Arrays.stream( list.toArray() );
System.out.println( stream2 ); // java.util.stream.ReferencePipeline$Head@74a14482 // 3、利用stream接口来创建
Stream<List<Employee>> stream3 = Stream.of(list);// of这里面是一个可变参数 / T类型的参数( 泛型 )
// 所以类型就是依赖前面集合中创建的时候用的泛型类型
System.out.println( stream3 ); // java.util.stream.ReferencePipeline$Head@1540e19d 都是一些对象,看不出啥子东西,玩起也无聊 }
}
package cn.xieGongZi.Stream; // 员工类
public class Employee { private String name;
private String job;
private String sex;
private Double salary; public Employee(String name, String job, String sex, Double salary) {
this.name = name;
this.job = job;
this.sex = sex;
this.salary = salary;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getJob() {
return job;
} public void setJob(String job) {
this.job = job;
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex;
} public Double getSalary() {
return salary;
} public void setSalary(Double salary) {
this.salary = salary;
} @Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
", sex='" + sex + '\'' +
", salary=" + salary +
'}';
}
}
-
先提前知道一个点:即:流程图中的最后一环 —— 结束流程( 这里先了解一个forEach,其他的后面进行说明 )
-
让这个点提前了解的原因是:
-
这个流程创建了流之后,需要最后一步:结束流程,不然数据无法显示
// 终止流程 ———— 这个有结束流程 并 获取最终结果的功能 ———— 因此:先来了解一个终止方法
// forEach()————这个方法就是遍历的意思。它需要一个消费型函数式接口
stream1.forEach( (e)-> System.out.println(e) ); // 这里的这个e就是Employee类型的对象e
// 再次解释一下这个流程
// 首先:我们需要把一个类型的对象 当做参数 丢进去( 即:玩的匿名内部类new哪里————不就是一个对象吗 )
// 然后:这个对象携带的参数( 信息 ) 就交给lambda表达式的某一个函数,让它去执行( 即:这里的forEach )
// 而我们只需要做的事情就是:提供一种类型的对象参数,然后提供这个对象参数进到lambda要执行的函数中的业务代码是什么就行了( 即:这里的输出语句 )
-
这个流程创建了流之后,需要最后一步:结束流程,不然数据无法显示
-
让这个点提前了解的原因是:
-
先提前知道一个点:即:流程图中的最后一环 —— 结束流程( 这里先了解一个forEach,其他的后面进行说明 )
- 来正式开始玩儿这个流中的中间重要流程
-
1、filter过滤 ————— 过滤是什么意思?———— 就是 筛选出我想要的信息涩
package cn.xieGongZi.Stream.playFilter; import cn.xieGongZi.Stream.Employee; import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream; public class Test { public static void main(String[] args) { //准备数据
List<Employee> list = new ArrayList<>();
list.add(new Employee("刘德华", "歌手", "男", 1200.0));
list.add(new Employee("张旭阳", "演员", "男", 1400.0));
list.add(new Employee("小沈阳", "演员", "男", 1300.0));
list.add(new Employee("王宝强", "演员", "男", 1900.0));
list.add(new Employee("姚明", "歌手", "男", 2200.0));
list.add(new Employee("张杰", "主持人", "男", 3100.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0)); // 创建流
Stream<Employee> stream1 = list.stream(); // 开始玩儿filter
// 1、假如我只想要list中的演员信息呢?
System.out.println("获取list中的演员");
stream1.filter( employee -> employee.getJob().equals("演员") ).forEach( System.out::println ); // 形参只有employee这一个,所以()直接省略
// employee.getJob().equals("演员") 这个就是业务逻辑代码涩
// 可以对照着 我们不知道这个lambda表达式,正常写这种问题的代码,这样理解更深
System.out.println();
System.out.println("正常代码写法");
for (Employee employee : list) { if ( employee.getJob().equals("演员") ){
System.out.println( employee );
}
} // 2、如果我想获得职业是演员,但是同时保证还是女的呢?
System.out.println();
System.out.println("获取list中的女演员");
Stream<Employee> stream2 = list.stream(); // 这里重写开流,是因为前面那个已经利用forEach把流关闭了
stream2.filter( employee -> employee.getJob().equals("演员") && employee.getSex().equals("女") ).forEach( System.out::println );
// 这个过滤也就这么玩的,就不继续玩儿了 }
}-
效果图如下:
-
2、limit限制 ———— 什么意思? ———— 就是我只想显示特定的几条数据嘛
package cn.xieGongZi.Stream.playLimit; import cn.xieGongZi.Stream.Employee; import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream; public class Demo { public static void main(String[] args) { // 准备数据
List<Employee> list = new ArrayList<>();
list.add(new Employee("刘德华", "歌手", "男", 1200.0));
list.add(new Employee("张旭阳", "演员", "男", 1400.0));
list.add(new Employee("小沈阳", "演员", "男", 1300.0));
list.add(new Employee("王宝强", "演员", "男", 1900.0));
list.add(new Employee("姚明", "歌手", "男", 2200.0));
list.add(new Employee("张杰", "主持人", "男", 3100.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0)); // 1、假如我只想要显示前5条数据呢?
System.out.println("Stream流使用limit和lambda表达式进行操作");
Stream<Employee> stream1 = list.stream(); stream1.limit(5).forEach( System.out::println ); System.out.println(); // 对比正常流程
System.out.println("这是用正常流程进行遍历前5条数据");
for (int i = 0; i < 5; i++) { System.out.println( list.get(i) );
} // 2、那如果我只想要4、5条数据呢? ———— 用一个方法skip(),在多线程中已经介绍过这个方法了,作用也是一样的
System.out.println();
System.out.println("利用skip和lambda表达式获取stream流中指定那条 / 几条的信息");
Stream<Employee> stream2 = list.stream();
stream2.skip(3).limit(2).forEach(employee -> System.out.println( employee ) ); }
}-
效果图如下:
-
-
2、limit限制 ———— 什么意思? ———— 就是我只想显示特定的几条数据嘛
-
3、distinct 去重 ———— 去除重复的数据
- 这个默认是根据Object类中的equals()来进行比较( 而这种默认比较的是值,所以只适合用值来去重,其他的东西需要去重的话,则:需要重写equals()方法,也就会伴随着重写hashCode,因此:这个不多做说明
-
3、distinct 去重 ———— 去除重复的数据
-
4、map投影 ———— 获取指定流中的某一部分组成的流
package cn.xieGongZi.Stream.playMap; import cn.xieGongZi.Stream.Employee; import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream; public class Demo { public static void main(String[] args) { //准备数据
List<Employee> list = new ArrayList<>();
list.add(new Employee("刘德华", "歌手", "男", 1200.0));
list.add(new Employee("张旭阳", "演员", "男", 1400.0));
list.add(new Employee("小沈阳", "演员", "男", 1300.0));
list.add(new Employee("王宝强", "演员", "男", 1900.0));
list.add(new Employee("姚明", "歌手", "男", 2200.0));
list.add(new Employee("张杰", "主持人", "男", 3100.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0)); // 1、如果我只想要list中的所有的Job的信息呢?
System.out.println("通过lambda和map映射流中的一部分内容");
Stream<Employee> stream = list.stream();
stream.map( employee -> employee.getJob() ).forEach( employee->System.out.println(employee) );
}
}-
效果图如下:
-
4、map投影 ———— 获取指定流中的某一部分组成的流
-
5、Sorted排序
-
可以得出:排序规则和treeSet一样的( 实现CompareTo接口 / 现写现用 ),所以,其实不太想进行说明
package cn.xieGongZi.Stream.playSorted; import cn.xieGongZi.Stream.Employee; import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream; public class Demo { public static void main(String[] args) { //准备数据
List<Employee> list = new ArrayList<>();
list.add(new Employee("刘德华", "歌手", "男", 1200.0));
list.add(new Employee("张旭阳", "演员", "男", 1400.0));
list.add(new Employee("小沈阳", "演员", "男", 1300.0));
list.add(new Employee("王宝强", "演员", "男", 1900.0));
list.add(new Employee("姚明", "歌手", "男", 2200.0));
list.add(new Employee("张杰", "主持人", "男", 3100.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0)); Stream<Employee> stream = list.stream();
stream.sorted( ( employee1 , employee2)->
employee1.getSalary() > employee2.getSalary() ? 1:-1).forEach(
employee -> System.out.println(employee) );
}
}-
效果图如下:
-
-
5、Sorted排序
-
2、玩儿流程中的最后一项:终止操作
- 1、forEach 前面已经玩过了,所以不玩了
-
2、玩儿流程中的最后一项:终止操作
-
2、min 和 max ———— 获取流中最小 / 最小的那个对象信息 ———— 这个东西就和多线程中的那个异步结果Future一样
-
package cn.xieGongZi.Stream.close.min; import cn.xieGongZi.Stream.Employee; import java.util.ArrayList;
import java.util.List; public class Demo { public static void main(String[] args) { //准备数据
List<Employee> list = new ArrayList<>();
list.add(new Employee("刘德华", "歌手", "男", 1200.0));
list.add(new Employee("张旭阳", "演员", "男", 1400.0));
list.add(new Employee("小沈阳", "演员", "男", 1300.0));
list.add(new Employee("王宝强", "演员", "男", 1900.0));
list.add(new Employee("姚明", "歌手", "男", 2200.0));
list.add(new Employee("张杰", "主持人", "男", 3100.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0));
// 这个规则也是弄一个Comparator,所以还可以对照treeSet的排序原理来理解
Employee employee = list.stream().min((employee1, employee2) -> employee1.getSalary() > employee2.getSalary() ? 1 : -1).get();
System.out.println(employee);
System.out.println();
Employee employee3 = list.stream().max((employee1, employee2) -> employee1.getSalary() > employee2.getSalary() ? 1 : -1).get();
System.out.println(employee3);
}
}
-
- 3、count ———— 获取流中的信息对象有多少个
-
2、min 和 max ———— 获取流中最小 / 最小的那个对象信息 ———— 这个东西就和多线程中的那个异步结果Future一样
-
package cn.xieGongZi.Stream.close.count; import cn.xieGongZi.Stream.Employee; import java.util.ArrayList;
import java.util.List; public class Demo { public static void main(String[] args) { //准备数据
List<Employee> list = new ArrayList<>();
list.add(new Employee("刘德华", "歌手", "男", 1200.0));
list.add(new Employee("张旭阳", "演员", "男", 1400.0));
list.add(new Employee("小沈阳", "演员", "男", 1300.0));
list.add(new Employee("王宝强", "演员", "男", 1900.0));
list.add(new Employee("姚明", "歌手", "男", 2200.0));
list.add(new Employee("张杰", "主持人", "男", 3100.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0));
System.out.println( list.stream().count() );
System.out.println(); // 还可以配合着玩儿
System.out.println( list.stream().filter( employee -> employee.getSex().equals("男") ).count() );
}
}效果图如下:
-
-
4、collect ———— 收集 ———— 这个玩意儿需要一个容器把结果装起来( 因此需要用到Collectors工具类 )
package cn.xieGongZi.Stream.close.collect; import cn.xieGongZi.Stream.Employee; import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors; public class Demo { public static void main(String[] args) { //准备数据
List<Employee> list = new ArrayList<>();
list.add(new Employee("刘德华", "歌手", "男", 1200.0));
list.add(new Employee("张旭阳", "演员", "男", 1400.0));
list.add(new Employee("小沈阳", "演员", "男", 1300.0));
list.add(new Employee("王宝强", "演员", "男", 1900.0));
list.add(new Employee("姚明", "歌手", "男", 2200.0));
list.add(new Employee("张杰", "主持人", "男", 3100.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0)); // 1、低级使用
System.out.println("低级使用");
List<Employee> collect = list.stream().collect( Collectors.toList() );
System.out.println( collect );
System.out.println(); // 2、配合其他的中间操作使用
System.out.println("灵活使用");
List<Employee> resultCollect = list.stream().filter( employee -> employee.getSex().equals("女") ).collect( Collectors.toList() );
System.out.println( resultCollect );
}
}效果图如下:
这个东西补充一个点:利用Conllects工具类在这里还可以实现分组 ——— group
-
而这个的思想就是利用了map集合的思想———— key-value双列存储,但是更复杂( 来个图看一下 )
-
实例:
-
package cn.xieGongZi.Stream.close.collect.group; import cn.xieGongZi.Stream.Employee; import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; public class Play { public static void main(String[] args) { //准备数据
List<Employee> list = new ArrayList<>();
list.add(new Employee("刘德华", "歌手", "男", 1200.0));
list.add(new Employee("张旭阳", "演员", "男", 1400.0));
list.add(new Employee("小沈阳", "演员", "男", 1300.0));
list.add(new Employee("王宝强", "演员", "男", 1900.0));
list.add(new Employee("姚明", "歌手", "男", 2200.0));
list.add(new Employee("张杰", "主持人", "男", 3100.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0));
list.add(new Employee("范冰冰", "演员", "女", 4800.0)); Map< String, List<Employee> > group = list.stream().collect( Collectors.groupingBy(employee -> employee.getJob() ) );
// 这里泛型的泛型 ———— 套娃 ———— 即:整个内容是存在一个map中的
// 而这个map中的key就是job,value就是每一个职业对应有哪些人
// String就是key 职业的类型,list集合中就是存的这个职业对应的是那些人,类型嘛,就是Employee类型咯 for ( String key : group.keySet() ) { System.out.println( key + " = " + group.get(key) ); }
}
}效果图如下:
-
-
-
-
-
4、collect ———— 收集 ———— 这个玩意儿需要一个容器把结果装起来( 因此需要用到Collectors工具类 )
至此,java8的新特性就整完了,其他的特性也有,只是那些懒得说明了,因为最常用的是这些,其他那些有基础知识之后,需要用时一看就会了
另外:现在已经是java10几了,所以严格来讲,这些根本不叫新特性 ^ _ ^
javaSE高级篇5 — java8新特性详解———更新完毕的更多相关文章
-
java8新特性详解(转)
原文链接. 前言: Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级.在Java Code Geeks上已经有很多介绍Java 8新特性的文章,例如Playing with ...
-
Java学习之==>;Java8 新特性详解
一.简介 Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级.Java 8是 Java 自 Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库. ...
-
javaSE高级篇4 — 反射机制( 含类加载器 ) — 更新完毕
反射机制 1.反射机制是什么?----英文单词是:reflect.在java.lang包下---这才是java最牛逼的技术 首先提前知道一句话----在java中,有了对象,于是有了类,那么有了类之后 ...
-
点击--》java9 新特性 详解
引言: 点击-->java9 新特性 详解 点击-->java8 新特性 详解 正题: 1.局部变量var 将前端思想var关键字引入java后段,自动检测所属于类型,一种情况除外,不能为 ...
-
java10 新特性 详解
引言: 点击-->java9 新特性 详解 点击-->java8 新特性 详解 正题: 1.局部变量var 将前端思想var关键字引入java后段,自动检测所属于类型,一种情况除外,不能为 ...
-
Java8 Stream新特性详解及实战
Java8 Stream新特性详解及实战 背景介绍 在阅读Spring Boot源代码时,发现Java 8的新特性已经被广泛使用,如果再不学习Java8的新特性并灵活应用,你可能真的要out了.为此, ...
-
Java基础学习总结(33)——Java8 十大新特性详解
Java8 十大新特性详解 本教程将Java8的新特新逐一列出,并将使用简单的代码示例来指导你如何使用默认接口方法,lambda表达式,方法引用以及多重Annotation,之后你将会学到最新的API ...
-
Java9 新特性 详解
作者:木九天 < Java9 新特性 详解 > Java9 新特性 详解 摘要: 1.目录结构 2.repl工具 jShell命令 3.模块化 4.多版本兼容jar包 5.接口方 ...
-
ES6,ES2105核心功能一览,js新特性详解
ES6,ES2105核心功能一览,js新特性详解 过去几年 JavaScript 发生了很大的变化.ES6(ECMAScript 6.ES2105)是 JavaScript 语言的新标准,2015 年 ...
随机推荐
-
51Nod 1766 树上的最远点对
Description 一棵树,询问两个端点编号分别在在 \([a,b]\) 和 \([c,d]\) 两个区间中的最长链. Sol 线段树+ST表. 树上最长链可以合并,只需要合并两个区间最长链的两个 ...
-
☀【CSS3】box-sizing
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8& ...
-
javascript基础学习(二)
javascript的数据类型 学习要点: typeof操作符 五种简单数据类型:Undefined.String.Number.Null.Boolean 引用数据类型:数组和对象 一.typeof操 ...
-
Linux企业级项目实践之网络爬虫(30)——通过查阅RFC文档扩充更加复杂的功能
HTTP是一种很简单的请求.响应式协议,客户端发送一个请求.服务器返回一个响应.HTTP 1.1 版本规范由 RFC2616 定义.了解了 HTTP请求.响应消息在TCP数据流中的格式,很容易使用纯 ...
-
webpack+vue打包之后输出配置文件修改接口文件
用vue-cli构建的项目通常是采用前后端分离的开发模式,也就是前端与后台完全分离,此时就需要将后台接口地址打包进项目中,but,难道我们只是改个接口地址也要重新打包吗?当然不行了,那就太麻烦了,怎么 ...
-
C#基础系列——语法
1.C#是这样开始的: 函数入口:static void Main(String [] args){} 2.Hello World 例子 using system;--------导入命名空间,里 ...
-
nodeJs实现微信小程序的图片上传
今天我来介绍一下nodejs如何实现保存微信小程序传过来的图片及其返回 首先wx.uploadFile绝大部分时候是配合wx.chooseImage一起出现的,毕竟选择好了图片,再统一上传是实现用户图 ...
-
安卓程序代写 网上程序代写[原]Android项目中string.xml占位符
开发中经常遇到这样的情况 , 在string.xml中用到以下占位符 <string name="delete_success">删除<xliff:g id=&q ...
-
c++ imooc自学计划
一.视频学习相关的课程列表: C++远征之起航篇http://www.imooc.com/learn/342: C++远征之离港篇http://www.imooc.com/learn/381: C++ ...
-
js_在原有的日期上添加天数输出添加后的日期
开始编码工作也有段时间了,想想没有留下点什么,有点遗憾.学到的一些经验,写写,分享一下.也给自己整理一下. 今天分享一下,在原有的日期上添加天数输出添加后的日期.开始做的时候,简单的思路是,直接用ne ...