java8 函数式编程实战

1.Lambda表达式的5种格式

1.1首先看一个代码片段

list.stream().anyMatch(person -> person.getAge() == 30);

list.stream().anyMatch(new Predicate() {
      @Override
      public boolean test(Person person) {
            return person.getAge() == 30;
      }
});

以上两段代码是等价的

 

1.2下面5种格式表达的都是一个意思,很关键,必须全部掌握

Person::getAge
          
person -> {return person.getAge();}
       
(person) -> {return person.getAge();}
      
(person) -> person.getAge()         
      
person -> person.getAge()

表示当前参数接收一个匿名内部类,类中的方法接收一个Person类型的参数,方法的逻辑是调用person的getAge方法

2.Stream流

collect:用于把Stream转换为List,set,map等待操作,示例:stream.collect(Collectors.toList) 参考示例test1(),test2()
   
filter:用于过滤集合元素,比原生代码速度快的多,参考示例 test1(),test2()

           去重示例:

main(){
    list.stream().filter(distinctByKey(Person::getName)).collect(Collectors.toList());
}

public static  Predicate distinctByKey(Function keyExtractor) {
	Map map = new ConcurrentHashMap<>();
	return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

以上示例如果看不太明白,其实等价于:原理就是使用HaspMap的putIfAbsent方法,如果元素不存在,才put,并返回null,否则返回map中存在的元素,使用该原理可以判断当前元素是否重复,即(v != null)表示重复,如果重复就由stream.filter进行过滤

public static void main(String[] args) {
		List list = Lists.newArrayList(new Person(22, "Strawberry", 10010),new Person(22, "Strawberry", 10010),new Person(23, "Marry", 10010));
		
		Function personNameFunction = new Function() {
			@Override
			public String apply(Person t) {
				return t.getName();
			}
		};
						
		list = list.stream().filter(distinctByKey(personNameFunction)).collect(Collectors.toList());
		System.out.println(list);				
	}
	
	public static Predicate distinctByKey(Function personNameFunction) {
		final Map map = new ConcurrentHashMap<>();
		Predicate predicate = new Predicate() {
			@Override
			public boolean test(Person t) {
				Object key = personNameFunction.apply(t);
				Boolean v = map.putIfAbsent(key, Boolean.TRUE);
				return v == null;	//如果v == null说明map中在putIfAbsent之前不存在该元素,否则就存在,即会被过滤掉
			}
		};
		return predicate;
	}


   
sorted:用于对集合中的元素比较排序,效率远远高于传统方式,参考示例 test3()
   
peek:用于对stream中的元素迭代做某些操作,注意peek后要返回一个新的list,peek才有效与foreach区别开,参考示例 test10()
   
map:用于操作集合列表元素,并且返回另一个元素类型的集合列表
          list元素为基本类型时,使用stream.map操作比原生代码快
          list元素为引用类型时,使用stream.map操作比原生代码慢
          参考示例test4(),test5()
          Collectors.toMap 这种方式list转为map依然比原生操作慢


flatMap:用于扁平化流,把list列表中的数组打平成元素    
          参考示例:test6()
              collectFlatMap中的数据格式:[test, 1, 2, 3, 4, 5, 6, 7, 8, 9]
           collectMap中的数据格式:[[test, 1],[test, 2],[test, 3],[test, 4],[test, 5],[test, 6],[test, 7],[test, 8],[test, 9]]

         注意要distinct()保证去重

         性能上还是不如原生操作
  
match:是否匹配 参考示例:test12()
       allMatch:全部都匹配返回true
       anyMatch:只要有一个匹配返回true,false全部都不匹配
       noneMatch: 全部都不匹配
   
reduce:可以进行filter,max,min等等操作,参考test()12

skip(int n):丢弃Stream前n个元素,拿到剩下的元素

limit(int n):获取Stream前n个元素

利用以上两个方法可以做到分页效果:

stream().skip((pageNum - 1) * pageSize).limit(pageSize)

3.代码示例

package cn.qu.function;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;

public class FunctionTest {

	public static void main(String[] args) {
		test1();
	}
	
	private static Random random;
	private static List list;
	
	static void init(Integer n){
		random = new Random();
		list = new ArrayList<>();
		for(int i = n; i < 2*n - 1; i ++){
			list.add(new Person().setId(i).setDeviceId("A1-01-" + (random.nextInt(2) + 1)).setAge(random.nextInt(30) + 1).setName("A"));
		}	
	}
	
	//使用stream.reduce求person列表中personId最小的person
	public static void test13() {			
		init(101);	
		Optional reduce = list.stream().reduce((person1,person2) -> person1.getId() < person2.getId() ? person1 : person2);
		System.out.println(reduce.get());
	}
	
	//匹配当前person列表中是否有年龄为30岁的人
	public static void test12() {
		init(101);		
		boolean anyMatch = list.stream().anyMatch(person -> person.getAge() == 30);
		System.out.println("person列表中是否有30岁的人:" + anyMatch);
	}
	
	//求person列表中最大值,平均值,最小值
	public static void test11() {
		init(101);
		Map> collect = null;
		
		//测试Collectors.maxBy 分组求最大值
		collect = list.stream().collect(Collectors.groupingBy(Person::getDeviceId,Collectors.maxBy(Comparator.comparing(Person::getId))));
		System.out.println("分组求最大值:");
		System.out.println(collect);
		
		//测试Collectors.minBy 分组求最小值
		collect = list.stream().collect(Collectors.groupingBy(Person::getDeviceId,Collectors.minBy(Comparator.comparing(Person::getId))));
		System.out.println("分组求最小值:");
		System.out.println(collect);
		
		//测试Collectors.averagingInt 分组求平均值
		Map collect3 = list.stream().collect(Collectors.groupingBy(Person::getDeviceId,Collectors.averagingInt(Person::getAge)));
		collect3.forEach((key,avg) -> {
			avg = Math.round(avg*100)*0.01;
			collect3.put(key, avg);
		});
		System.out.println("分组求平均值:");
		System.out.println(collect3);
		
		Map collect2 = null;
		
		//测试Collectors.counting() 分组统计
		collect2 = list.stream().collect(Collectors.groupingBy(Person::getDeviceId, Collectors.counting()));
		System.out.println("分组统计:");
		System.out.println(collect2);
		
		//测试Collectors.counting() 分组求和
		collect2 = list.stream().collect(Collectors.groupingBy(Person::getDeviceId, Collectors.summingLong(Person::getId)));
		System.out.println("分组求和:");
		System.out.println(collect2);
	}
	
	// 用于对stream中的元素迭代做某些操作
	public static void test10() {
		init(101);
		
		List collect = list.stream().peek(person -> person.setName("B")).collect(Collectors.toList());
		System.out.println(collect);
	}

	//核心概念就是对person列表按person.id属性去重
	public static void test9() {
		Random random = new Random();
		List list = new ArrayList<>();
		for(int i = 1000001; i < 2000001; i ++){
			list.add(new Person().setId(random.nextInt(10000) + 10000).setDeviceId("A1-01-" + (random.nextInt(24) + 1)).setAge(10).setName("A"));
		}
		
		long start = System.currentTimeMillis();
		//按设备ID分组
		Map> collect = list.stream().collect(Collectors.groupingBy(Person::getDeviceId));
		list.clear();
		//对每一组的person列表按id去重复
		collect.forEach((key,persons) ->{
			//去重后得到一个新的list
			ArrayList result = persons.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getId))), ArrayList::new));
			//合并
			list.addAll(result);
		});
		/*collect.forEach(new BiConsumer>() {
			@Override
			public void accept(String t, List u) {
				ArrayList list = u.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getId))), ArrayList::new));
				collect.put(t, list);
			}
		});*/
		
		//按id分组统计list中人数
		list.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.counting()));
		long end = System.currentTimeMillis();
		System.out.println("按id分组统计list中人数耗时:" + (end - start));

	}

	// 按年龄分组统计person列表各个年龄段的人数
	public static void test8() {
		init(101);
		
		Map collect = list.stream()
				.collect(Collectors.groupingBy(Person::getAge, Collectors.counting()));
		System.out.println(collect);
	}

	// 使用串行流方式对person列表按年龄进行分组,分组后拿到一个年龄为key的HashMap
	public static void test7() {
		init(101);

		Map> collect = list.stream()
				.collect(Collectors.groupingBy(new Function() {
					@Override
					public Integer apply(Person t) {
						return t.getAge();
					}
				}));
		System.out.println(collect);
	}

	/*
	 * stream.flatMap的作用: 使用stream.map把list列表中的字符串进行分割后得到的是一个String[]类型的list列表
	 * 使用stream.flatMap可以把String[]扁平化得到的是一个String类型的list列表
	 */
	public static void test6() {

		List list = new ArrayList<>();
		for (int i = 1; i < 10; i++) {
			list.add("test-" + i);
		}
		/*
		 * List collect = list.stream().map((str) ->
		 * str.split("-")).flatMap(new Function>() {
		 * 
		 * @Override public Stream apply(String[] array) {
		 * return Arrays.stream(array); }
		 * }).distinct().collect(Collectors.toList());
		 */
		List collect = list.stream().map((str) -> str.split("-")).flatMap(Arrays::stream).distinct()
				.collect(Collectors.toList());
		System.out.println(collect);

	}

	// 使用传统方式和串行流方式修改list列表中的字符串,串行流方式效率高的多
	public static void test5() {
		System.out.println("test4():");
		List list = new ArrayList<>();
		for (int i = 1000001; i < 2000001; i++) {
			list.add("test-" + i);
		}

		long start = System.currentTimeMillis();
		List collect = list.stream().map((string) -> string.toLowerCase()).collect(Collectors.toList());
		long end = System.currentTimeMillis();
		System.out.println("collect.size:" + collect.size() + "  耗时毫秒:" + (end - start));

		long start2 = System.currentTimeMillis();
		List strs = new ArrayList<>();
		for (String string : list) {
			strs.add(string.toUpperCase());
		}
		long end2 = System.currentTimeMillis();
		System.out.println("strs.size:" + strs.size() + "  耗时毫秒:" + (end2 - start2));

	}

	// 使用传统方式和串行流方式把列表中的person更换成personVo,传统方式较快,串行流方式较慢
	public static void test4() {
		System.out.println("test3():");
		init(1000001);

		long start = System.currentTimeMillis();
		List collect = list.stream()
				.map((person) -> new PersonVo().setId(person.getId()).setAge(person.getAge()).setName(person.getName()))
				.collect(Collectors.toList());
		long end = System.currentTimeMillis();
		System.out.println("stream流方式collect.size:" + collect.size() + "  耗时毫秒:" + (end - start));

		long start2 = System.currentTimeMillis();
		List personVos = new ArrayList<>();
		for (Person person : list) {
			personVos.add(new PersonVo().setId(person.getId()).setAge(person.getAge()).setName(person.getName()));
		}
		long end2 = System.currentTimeMillis();
		System.out.println("传统方式personVos.size:" + personVos.size() + "  耗时毫秒:" + (end2 - start2));

	}

	// 使用传统选择排序和stream流对list元素排序,stream流方式快的多
	public static void test3() {
		init(1000001);

		long start = System.currentTimeMillis();
		list.stream().sorted(Comparator.comparing(Person::getAge).thenComparing(Person::getId))
				.collect(Collectors.toList());
		long end = System.currentTimeMillis();
		System.out.println("stream串行流排序耗时:" + (end - start));

		long start2 = System.currentTimeMillis();
		Person[] persons = list.toArray(new Person[list.size()]);
		for (int i = 0; i < persons.length; i++) {
			for (int j = i + 1; j < persons.length; j++) {
				Person temp = null;
				if (persons[i].getAge() < persons[j].getAge()) {
					temp = persons[i];
					persons[i] = persons[j];
					persons[j] = temp;
				}
			}
		}
		long end2 = System.currentTimeMillis();
		System.out.println("传统选择排序耗时:" + (end2 - start2));

	}

	// 使用传统方式和串并行流方式过滤出age<15的person列表, 列表数量越大stream比传统方式越快,注意越test1的区别,test1只是统计人数,test2是过滤出元素
	public static void test2() {
		int age = 15;
		init(1000001);

		long start = System.currentTimeMillis();
		// 传统方式
		Iterator iterator = list.iterator();
		while (iterator.hasNext()) {
			if (iterator.next().getAge() >= age) {
				iterator.remove();
			}
		}

		long end = System.currentTimeMillis();
		System.out.println("传统方式统计年龄小于15岁的人数:" + list.size() + "  统计耗时毫秒:" + (end - start));

		long start2 = System.currentTimeMillis();
		List collect = list.stream().filter(person -> person.getAge() < age).collect(Collectors.toList());
		// List collect = list.parallelStream().filter(person ->
		// person.getAge() < age).collect(Collectors.toList());
		long end2 = System.currentTimeMillis();
		System.out.println("stream流方式统计年龄小于15岁的人数:" + collect.size() + "  统计耗时毫秒:" + (end2 - start2));

	}

	// 使用传统方式和串行流方式过滤出age<15的人数, 传统方式效率高一些
	public static void test1() {
		int age = 15;
		init(1000001);
		
		long start = System.currentTimeMillis();
		int count = 0;
		for (int i = 0; i < list.size(); i++) {
			if (list.get(i).getAge() < age) {
				count++;
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("年龄小于15岁的人数:" + count + "  统计耗时毫秒:" + (end - start));
		long start2 = System.currentTimeMillis();
		// long count2 = list.parallelStream().filter(person -> person.getAge()
		// < age).count();
		long count2 = list.stream().filter(person -> person.getAge() < age).count();
		long end2 = System.currentTimeMillis();
		System.out.println("年龄小于15岁的人数:" + count2 + "  统计耗时毫秒:" + (end2 - start2));

	}

}

4.person和personVo

package cn.qu.function;

public class Person {

	private Integer id;
	private Integer age;
	private String name;
	private String deviceId;
	public Integer getId() {
		return id;
	}
	public Person setId(Integer id) {
		this.id = id;
		return this;
	}
	public Integer getAge() {
		return age;
	}
	public Person setAge(Integer age) {
		this.age = age;
		return this;
	}
	public String getName() {
		return name;
	}
	public Person setName(String name) {
		this.name = name;
		return this;
	}
	
	public String getDeviceId() {
		return deviceId;
	}
	public Person setDeviceId(String deviceId) {
		this.deviceId = deviceId;
		return this;
	}
	@Override
	public String toString() {
		return "Person [id=" + id + ", age=" + age + ", name=" + name + ", deviceId=" + deviceId + "]";
	}

}

//PersonVo extends Person

 

你可能感兴趣的:(java,函数式编程,lambda)