JavaSE | 10-Stream流与方法引用

Stream流的思想和获取Stream流

  • 比喻: Stream 是流水线,不存数据
  • 作用: 结合Lambda表达式,简化集合、数组操作
类型获取Stream流
List.stream()
Map.entrySet().stream()
数组Arrays.stream(数组)
Set.stream()
零散数据(同种类型)Stream.of(数据)
	//List-------------------------//
	ArrayList<String> list = new ArrayList<>();  
	Collections.addAll(list, "Java", "Python", "C++", "JavaScript", "Ruby");  
	list.stream()  
	    .filter(s -> s.length() > 4)  
	    .forEach(System.out::println);  
	    
	//Map--------------------------//
	HashMap<String, Integer> map = new HashMap<>();  
	map.put("aaa", 111);  
	map.put("bbb", 222);  
	map.put("ccc", 333);  
	  
	map.entrySet().stream()  
	        .filter(e -> e.getValue() == 333)  
	        .forEach(e -> System.out.println(e.getKey() + " = " + e.getValue()));  
	        
	map.keySet().stream()  
        .filter(k -> map.get(k) == 333)  
        .forEach(k -> System.out.println(k + " = " + map.get(k)));  
              
	//数组---------------------------//
	int[] arr = {1,2,3,4,5,6,7,8,9,10};  
	Arrays.stream(arr)  
	        .filter(n-> n==1)  
	        .forEach(System.out::println);
	        
	//零散数据-----------------------//
	Stream.of(1,2,3,4,5,6,7,8,9,10)  
        .filter(n-> n==1)  
        .forEach(System.out::println);

Stream.of的注意事项

  • 数组必须是引用数据类型!!!
String[] sArr = {"a", "b"};
Stream.of(sArr); // 得到流 [a, b],长度 2

int[] iArr = {1, 2};
Stream.of(iArr); // 得到流 [[1, 2]],长度 1!它把整个数组当成了一个元素

中间方法和终结方法

中间方法

  • 返回值还是 Stream(可以继续 .filter().map() 下去)
常用方法功能备注
filter过滤接收 Predicate,返回 true 的留下。修改Stream中的数据,不会影响原来的数据
map类型转换把一种对象换成另一种(如把 Student 转成 String 姓名).map(s -> Integer.parseInt(s.split("-")[1]))
distinct去重依赖对象的 hashCodeequals
limit(n)截取前 n 个`获取前几个元素(短路)
skip(n)跳过前 n 个经常和 limit 组合做分页
sorted排序自然排序或传 Comparator
concat合并将a和b两个流合并成一个集 合。Stream.concat(list1.stram(),list2.stream())

终结方法

  • 返回值不是 Stream(变成 voidlongListOptional 等),一旦调用,流彻底关闭
常用方法功能备注
forEach遍历打印或消费数据.forEach()
count统计个数返回 long.count
collect收集转集合最重要! 把流转回 List, Set, Map.collect(Collectors.toList())
.collect(Collectors.toSet())
toArray收集转数组收集流中的数据,放到数组中.toArray(n -> String[n])
.toArray(String[]::new)
reduce聚合把所有数据累加/合成一个结果
anyMatch匹配只要有一个满足条件就返回 true

collect收集到Map中

	/*
		toMap():参数一表示键的生成规则
				 参数二表示值的生成规则
		
		Function:泛型一表示流中每一个数据的类型
				  泛型二表示Map集合中 键/值 的数据类型
				  
		apply形参:依次表示流里面的每一个数据
			 方法体:生成 键/值 的代码
			 返回值:已经生成的 键/值
	*/
数据格式为 name - gender - age
	Map<String, Integer> map = 
		list.stream()
			.filter(s -> "男".equals(s.spilt("-")[1]))
			.collect(Collectors.toMap(new Function<流的类型,键的类型>(){
					@Override
					public String apply(String s){
						return s.split("-")[0];//name
						//return null;
					}
				},new Function<流的类型,值的类型>(){
						@Override
						public Integer apply(String s){
							return Integer.parseInt(s.split("-")[2]);//age
							//return null;
						}
					}));
	//----------lambda-----------//				
	Map<String, Integer> map = list.stream()  
        .filter(s -> "男".equals(s.split("-")[1]))  
        .collect(Collectors.toMap(s->s.split("-")[0],
					s-> Integer.parseInt(s.split("-")[2])));  
	

[!WARNING] 如果要收集到Map集合当中,键不能重复


方法引用

方法引用前提:

  1. 接口对路:必须是函数式接口。

  2. 坑位对齐:参数、返回值必须和接口方法一模一样。

  3. 现成逻辑:逻辑已存在(被引用方法满足当前需求),且 Lambda 内部只有这一行调用。

类型格式例子 (Lambda -> 方法引用)
静态方法引用类名::静态方法s -> Integer.parseInt(s) $\rightarrow$ Integer::parseInt
实例方法引用对象名::成员方法s -> System.out.println(s) $\rightarrow$ System.out::println
特定类型实例方法类名::成员方法(s1, s2) -> s1.compareTo(s2) $\rightarrow$ String::compareTo
构造方法引用类名::newname -> new Student(name) $\rightarrow$ Student::new

引用成员方法的多种情况

  • 其他类: 其它类对象::方法名
  • 本类: this::方法名
  • 父类: super::方法名

[!WARNING] 当本类,父类的引用处为静态方法时,不可用this/super,应当先创建本类对象

public class Daily {  
    public static void main(String[] args) {  
        ArrayList<String> list = new ArrayList();  
        Collections.addAll(list, "张三", "李四", "王五", "张六一", "孙而七");  
        
	    //当方法在本类中,方法为static,创建本类对象
        list.stream().filter(new Daily()::stringJudge)  
                .forEach(System.out::println);      
    }  
	public boolean stringJudge(String s) {...} 
}
class Parent {  
    public boolean stringJudge(String s) {  
        return s.startsWith("张");  
    }  
}  
  
class Daily extends Parent {  
    @Override  
    public boolean stringJudge(String s) {  
        return s.startsWith("张") && s.length() == 3;  
    }  
  
    public void test(List<String> list) {  
        // 调子类重写后的方法  
	    list.stream().filter(this::stringJudge)
		    .forEach(System.out::println);  
  
        // 强制调父类版本  
	    list.stream().filter(super::stringJudge)
		    .forEach(System.out::println);  
    }  
}

构造方法的引用


	ArrayList<String> list = new ArrayList();  
	Collections.addAll(list, "张三,13", "李四,46", "王五,15");  
	List<Student> list1 = 
		list.stream()  
			//这里必须在Student中新建一个构造方法
			.map(Student::new)  
			.collect(Collectors.toList());
    /*在Student中
	*    public Student(String s){  
	*	    this.name = s.split(",")[0];  
	*	    this.age = Integer.parseInt(s.split(",")[1]);
	*	 }
    */

其他调用方式

类名引用成员方法 类名::成员方法

独有的规则

被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致

抽象方法形参的详解: 第一个参数: 表示被引用方法的调用者,决定了可以引用那些类中的方法 在Stream流当中,第一个参数一般都表示流里面的每一个数据 假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法

第二个参数到最后一个参数: 跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要时无参的成员方法

例子:String::substring(int beginIndex)

  • 这个方法括号里只有 1 个 参数。

  • 但它是一个成员方法,需要 1 个 调用者。

  • 所以这个方法引用,要求 Lambda 必须提供 2 个 参数:

    • 第一个参数:必须是 String(调用者)。

    • 第二个参数:必须是 int(对应 beginIndex)。

  • Lambda 原型(String s, int i) -> s.substring(i)

总结: “类名::成员方法”之所以看起来参数少了一个,是因为第一个参数被拿去“点”那个方法了。

	ArrayList<String> list = new ArrayList();  
	Collections.addAll(list, "aaa", "bbb", "ccc", "ddd");  
	list.stream()  
			//拿着流里面的每一个数据,去调用String类中的方法
	        .map(String::toUpperCase) //s -> s.toUpperCase()
	        .forEach(System.out::println);

引用数组的构造方法 数据类型[]::new

	Integer[] array = list.stream()
		.toArray(Integer[]::new);//v -> new Integer[v]

[!IMPORTANT] 数组的类型,需要跟流中数据一致

常犯错误

Stream 中的参数分配法则: 流里的数据总得有个去处。

  1. 要么当参数:喂给静态方法或构造器(如 Student::new)。

  2. 要么当主人:自己调用成员方法(如 Student::getName)。

报错的原因:通常是因为你既提供了一个主人(new Student()),又想让流里的数据当参数,结果方法不收参数,数据就“没地方放”了。

今日访问 ... 次 | 今日访客 ... 人 | 本页阅读 ...
小站已萌萌哒运行了 0 0 0
已累计耕耘 16 篇博文 · 共 42.69k 个字
总访问量 ...