传统方式
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("张三三");
list.add("张四四");
list.add("李四四");
ArrayList<String> newlist = new ArrayList<>();
for(String s: list){
if(s.startsWith("张")&&s.length()>2){
listWithZhang.add(s);
}
}
for(String s: newlist){
System.out.println(s);
}
}
Stream流
JDK1.8后出现,关注做什么,而不是怎么做
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("张三三");
list.add("张四四");
list.add("李四四");
list.stream().filter((name)->name.startsWith("张"))
.filter((name)->name.length()>2)
.forEach((name)->{
System.out.println(name);
});
}
拼接流式模型:建立一个生产线,按照生产线来生产商品。
当使用一个流的时候,通常包括三个基本步骤:
每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。
java.util.stream.Stream
(这并不是一个函数式接口。)
获取一个流有以下几种常用的方式:
public static void main(String[] args) {
//集合调用stream方法可以获得
ArrayList<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//数组可以用Stream.of获得
Stream<Integer> stream6 = Stream.of(1, 2, 3, 4, 5, 6);
Integer[] arr = {
1, 2, 3, 5};
Stream<Integer> steam7 = Stream.of(arr);
}
流的特点:Stream流属于管道流,只能使用一次,第一个流使用完毕就会关闭,这个流就不可以再调用其他方法了。
流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:
forEach方法:forEach的参数是Consumer
//forEach的参数的Consumer
//Consumer接口是一个消费型的函数式接口,可以传递lambda表达式消费数据
public static void main(String[] args) {
Stream<String> stream = Stream.of("张三", "李四", "王五");
stream.forEach(name-> System.out.println(name));
}
filter方法:filter的参数是Predicate,用于将一个流转换成另一个子集流
//forEach的参数是该接口接收一个Predicate
//函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。
//test方法将会产生一个boolean值结果,代表指定的条件是否满足。
//如果结果为true,那么Stream流的filter 方法
//将会留用元素;如果结果为false,那么filter 方法将会舍弃元素。
public static void main(String[] args) {
Stream<String> stream = Stream.of("张三", "张四","李四", "王五");
Stream<String> stream1 = stream.filter((name) -> {
return name.startsWith("张");
});
stream1.forEach(name-> System.out.println(name));
}
map方法:
如果需要将流中的元素映射到另一个流中,可以用map。可以将T类型的流转换为R类型的流。java.util.stream.Function 函数式接口,其中唯一的抽象方法为:apply。这可以将一种T类型转换成为R类型,而这种转换的动作,就称为“映射”。
将integer类型转换为string类型
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<String> stream2 = stream.map((Integer i) -> {
return String.valueOf(i);
});
stream2.forEach((s)-> System.out.println(s));
}
count方法
用于统计Stream流中的元素个数。正如旧集合Collection 当中的size 方法一样,流提供count 方法来数一数其中的元素个数。count方法返回值是long类型的整数,是终结方法,,之后不能再继续调用其他方法。
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
long count = stream.count();
System.out.println(count);
}
limit方法:可以对流进行截取。参数是long类型的整数。属于延迟方法,可以继续调用其他方法。如果集合当前长度大于参数则进行截取;否则不进行操作,即还是原流。
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
stream.limit(3).forEach((num)-> System.out.println(num));
}
skip方法:可以跳过前几个元素,获取一个截取之后的新流。参数超过元素个数,返回空流。
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
stream.skip(3).forEach((num)-> System.out.println(num));
}
concat方法:组合两个流为一个流。concat是静态方法,通过接口名调用。
public static void main(String[] args) {
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
Stream<String> stream2 = Stream.of("a","b","c","d");
Stream.concat(stream1,stream2).forEach((s)-> System.out.println(s));
}
现在有两个ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
第一个队伍只要名字为3个字的成员姓名;
第一个队伍筛选之后只要前3个人;
第二个队伍只要姓张的成员姓名;
第二个队伍筛选之后不要前2个人;
将两个队伍合并为一个队伍;
根据姓名创建Person 对象;
打印整个队伍的Person对象信息。
Stream方式
public static void main(String[] args) {
ArrayList<Person> list1 = new ArrayList<>();
list1.add(new Person("李白"));
list1.add(new Person("杜甫"));
list1.add(new Person("李清照"));
list1.add(new Person("王勃"));
list1.add(new Person("刘禹锡"));
list1.add(new Person("辛弃疾"));
list1.add(new Person("龚自珍"));
ArrayList<Person> list2 = new ArrayList<>();
list2.add(new Person("李四"));
list2.add(new Person("王五"));
list2.add(new Person("张一"));
list2.add(new Person("张二"));
list2.add(new Person("张三"));
Stream<Person> newlist1 = list1.stream().filter((person) -> {
return person.getName().length() == 3;
}).limit(3);
Stream<Person> newlist2 = list2.stream().filter((person) -> {
return person.getName().startsWith("张");
}).skip(2);
Stream.concat(newlist1, newlist2).forEach((person)-> System.out.println(person));
}
双冒号:: 为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。
例如上方代码中的最后一句可以等价为:
Stream.concat(newlist1, newlist2).forEach((person)-> System.out.println(person));
Stream.concat(newlist1, newlist2).forEach(System.out::println);
自定义一个接口
public interface Printable {
void print(String s);
}
自定义一个类及其成员方法
public class MethodRerObject {
public void printUpperCaseString(String str){
System.out.println(str.toUpperCase());
}
}
测试
public class Test {
//通过对象名引用成员方法
//前提:对象名存在,成员方法存在
public static void printString(Printable p){
p.print("Hello");
}
public static void main(String[] args) {
//用lambda
printString((s)->{
MethodRerObject obj = new MethodRerObject();
obj.printUpperCaseString(s);
});
//方法引用优化
//对象和成员方法都存在
MethodRerObject obj = new MethodRerObject();
printString(obj::printUpperCaseString);
}
}
定义一个函数式接口
@FunctionalInterface
public interface Calcable {
int calAbs(int num);
}
定义一个方法传递接口和整数
public class Test {
public static int method(int number, Calcable c){
return c.calAbs(number);
}
public static void main(String[] args) {
//调用method方法
int result = method(-10, (num) -> Math.abs(num));
System.out.println(result);
//使用方法引用优化 Math存在 abs的静态方法也存在
int result2 = method(-10, Math::abs);
System.out.println(result2);
}
}
定义函数式接口
public interface Greatable {
void great();
}
定义父类
public class Human {
public void sayHello(){
System.out.println("Hello, i am human");
}
}
定义子类
public class Man extends Human{
@Override
public void sayHello(){
System.out.println("Hello, i am man");
}
public void great(Greatable g){
g.great();
}
public void show(){
great(()->{
Human h = new Human();
h.sayHello();
});
//通过父类调用
great(()->{
super.sayHello();
});
//通过父类引用
great(super::sayHello);
}
public static void main(String[] args) {
new Man().show();
}
}
public interface Richable {
void buy();
}
public class Husband {
public void buyHouse(){
System.out.println("买房子");
}
public void marry(Richable r){
r.buy();
}
public void soHappy(){
//this和buyHouse都是以及存在的 可以直接用this来引用本类方法
//marry(()->this.buyHouse());
marry(this::buyHouse);
}
public static void main(String[] args) {
new Husband().soHappy();
}
}
自定义一个Person类,自定义一个创建Person的接口
public interface PersonBuilder {
Person buildPerson(String name);
}
public class Test {
public static void printName(String name, PersonBuilder pb){
Person person = pb.buildPerson(name);
System.out.println(person.getName());
}
public static void main(String[] args) {
//调用method方法
printName("张三",(name)->new Person(name));
//使用方法引用优化 Person的构造方法已知 创建对象new已知
printName("李四", Person::new);
}
}
@FunctionalInterface
public interface ArrayBuilder {
int[] buiderArray(int length);
}
public class Test {
public static int[] createArray(int len, ArrayBuilder ab){
return ab.buiderArray(len);
}
public static void main(String[] args) {
//调用method方法
int[] array = createArray(10, (len) ->new int[len]);
System.out.println(array.length);
//使用方法引用优化lambda 已知创建的是int类型的数组 数组的长度已知
int[] array1 = createArray(10, int[]::new);
System.out.println(array1.length);
}
}