package com.dong.java8;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.partitioningBy;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
 * 关于Java8中流的定义,操作的方法汇总
 * 
 * @author Liudong
 */
public class testStream {
	static List<Dish> menu = Arrays.asList(
			new Dish("pork",false,800,Dish.Type.MEAT),
			new Dish("beef",false,700,Dish.Type.MEAT),
			new Dish("chichken",false,400,Dish.Type.MEAT),
			new Dish("french fries",true,530,Dish.Type.OTHER),
			new Dish("rice",true,350,Dish.Type.OTHER),
			new Dish("season fruit",true,120,Dish.Type.OTHER),
			new Dish("pizza",true,550,Dish.Type.OTHER),
			new Dish("prawns",false,300,Dish.Type.FISH),
			new Dish("salmon",false,450,Dish.Type.FISH)
			);
	public static void useSteam() {
		List<String> ThreeHighCaloricDishNames = menu.stream()
										 .filter( d -> d.getCalories() > 300) //Java编译器会从上下文中推断出用什么函数式接口来配合lambda表达式,即可以推断出合适的lambda签名
										 .map( d -> d.getName())
										 .limit(3)
										 .collect(toList());
		//stream():得到一个流
		//filter,map,limit对流的操作,返回另一个流
		//collect,返回一个结果
		
		//lambda使用的条件是函数式接口,而filter接受一个Predicted函数接口,Predicted函数式接口已经由系统帮我们实现,
		//这里的d.getCalories()是行为的实现过程中,我们使用的一个外部方法,和函数式接口无关;这里的d.getCalories()>300只是行为而已;
		//不可混淆以为Dish需要提供一个函数接口。。。;
		System.out.println(ThreeHighCaloricDishNames);
	}
	//limit, map
	public static void midleOperator() {
		List<String> names =
				menu.stream()
				.filter(d -> {
					System.out.println("filtering " + d.getName());
					return d.getCalories()>300;
				})
				.map(d -> {
					System.out.println("mapping " + d.getName());
					return d.getName();
				})
				.limit(3)
				.collect(toList());
		System.out.println(names);
	}
	//distinct
	public static void distincttest() {
		List<Integer> numbers = Arrays.asList(1,2,1,3,3,1,2,5,8,200);
		numbers.stream()
			   .filter( i -> i % 2 == 0)
			   .distinct()
			   .forEach(System.out::println);

	}
	//映射:map,map方法会接受一个函数作为参数,这个函数会被映射到每个元素上,并将其映射为一个新的元素;
	public static  void toMapping() {
		// TODO Auto-generated method stub
		List<String> dishName = menu.stream()
				.map(Dish::getName)
				.collect(toList());
	}
	public static void toMultipMapping() {
		List<String> words =Arrays.asList("hello","world");
		//使用map,返回的流是一个整体,即Stream<String[]>类型的,里面的元素为  0号元素为 hello,1号元素为world;
		List<String[]> Characters = words.stream()
				.map(word -> word.split(""))
				.distinct()
				.collect(toList());
		
		System.out.println(Characters);
		
		//使用flatMap,将数组映射成流的内容;
		List<String> uniqueCharacters = words.stream()
				.map(word -> word.split(""))
				.flatMap(Arrays::stream)
				.distinct()
				.collect(Collectors.toList());
		
		System.out.println(uniqueCharacters);	
	}
	//anyMatch方法返回一个boolean,检查行为至少匹配一个元素,是终端操作 ;
	//allMatch方法返回一个boolean,检查行为是否全部满足要求,是终端操作;
	//noneMath方法返回一个boolean,检查行为是否没有一个元素u,是终端操作;
	public static void anyMatch() {
		// TODO Auto-generated method stub
		if(menu.stream().anyMatch(Dish::isVegetarian)) {
			System.out.println("The menu is vegetarian friendsly");
		}
		if(menu.stream().allMatch(d -> d.getCalories() < 500)) {
			System.out.println("the all menu is lg 1000");
		}
		if(menu.stream().noneMatch(d -> d.getCalories() >=1000)) {
			System.out.println("no  match");
		}
	}
	//寻找第一个元素
	public static void findElement() {
		// TODO Auto-generated method stub
		List<Integer> number = Arrays.asList(1,2,3,4,5);
		Optional<Integer> firstSquareDivisibleByThree = 
				number.stream()
				.map(x -> x* x)
				.filter(x -> x % 3 == 0)
				.findFirst();
		System.out.println(firstSquareDivisibleByThree);
	}
	//归约: reduce操作将流流中所有元素反复结合起来,得到一个值;
	public static  void toSum() {
		// TODO Auto-generated method stub
		List<Integer> number = Arrays.asList(1,2,3,4,5);
		int  sum = number.stream().reduce(0, (a,b) -> a + b);
		System.out.println(sum);
	}
	//数值流
	//mapToInt会从没到菜中提取热量,并返回一个IntStream,然后可以调用IntStream接口中定义的sum方法;
	public static void valueStream() {
		int calories = menu.stream()
				.mapToInt(Dish::getCalories)
				.sum();
		
		//将数值流在转换会对象流
		IntStream inStream = menu.stream().mapToInt(Dish::getCalories);
		Stream<Integer> stream = inStream.boxed();
		
		System.out.println(calories);
	}
	//构建流
	public void constructStream() {
		//由值创建流
		Stream<String> stream = Stream.of("java " ,"c++","python","c","vb");
		stream.map(String::toUpperCase).forEach(System.out::println);
		//由数组创建流
		int [] numbers = {1,2,3,9,10};
		int sum = Arrays.stream(numbers).sum();
		//由文件生成流
		long uniqueWords = 0;
		try(Stream<String> lines = Files.lines(Paths.get("data.txt"),Charset.defaultCharset())){
			uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
					.distinct()
					.count();
		}catch(IOException e) {	
		}
		//无限流
		//iterate方法接受一个初始值,还有一个依次应用在每个产生的新值上的Lambda,
		Stream.iterate(0, n -> n+2)
		      .limit(10)
		      .forEach(System.out::println);
		//generate方法,接受一个Supplier<T> 类型的Lambda提供的新值;
		Stream.generate(Math::random)
		      .limit(10)
		      .forEach(System.out::println);
	}
	public enum CaloricLevel {DIET,NORMAL,FAT};
	//分组
	public static void toGroupingBy() {
		//一级分类
		Map<CaloricLevel,List<Dish>> dishesByCaloricLevel = menu.stream().collect(
				groupingBy(dish->{
					if(dish.getCalories() <= 400) return CaloricLevel.DIET;
					else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
					else return CaloricLevel.FAT;
				}));
		System.out.println(dishesByCaloricLevel);
		//多级分类
		Map<Dish.Type,Map<CaloricLevel,List<Dish>>> dishesByTypeCaloricLevel = 
				menu.stream().collect(
					groupingBy(Dish::getType,
							groupingBy(dish -> {
								if(dish.getCalories() <= 400) return CaloricLevel.DIET;
								else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
								else return CaloricLevel.FAT;
							})));
		System.out.println(dishesByTypeCaloricLevel);
	}
	//分类
	public static void classcify() {
		//分区:是分组的特殊情况,有一个谓词最为分类函数,分区函数返回一个布尔值,意味着得到的分组Map的键类型是boolean;最多可以分为两组
		Map<Boolean,List<Dish>> partitionedMenu = 
				menu.stream().collect(partitioningBy(Dish::isVegetarian));
		System.out.println(partitionedMenu);
	}
	public static void main(String[] args) {
		//testStream.useSteam();
		//testStream.midleOperator();
		//testStream.distincttest();
		//toMultipMapping();
		//anyMatch();
		//findElement();
		//toSum();
		//valueStream();
		//toGroupingBy();
		//classcify();
	}
	
}

package com.dong.java8;

public class Dish {
	
	private final String name;
	private final boolean vegetarian;
	private final int calories;
	private final Type type;
		
	public Dish(String name, boolean vegetarian, int calories, Type type) {
		super();
		this.name = name;
		this.vegetarian = vegetarian;
		this.calories = calories;
		this.type = type;
	}
	
	public enum Type{MEAT,FISH,OTHER};
	
	public String getName() {
		return name;
	}
	public boolean isVegetarian() {
		return vegetarian;
	}
	public int getCalories() {
		return calories;
	}
	public Type getType() {
		return type;
	};
	
	
	
}

代码参考:《Java8实战》中的片段 作者:Raoul-Gabriedl Urma Mario Fusco Alan Mycroft  ,,由衷感谢此书作者为我们提供学习内容