JavaSE-Lambda表达式-03-201804

3 篇文章 0 订阅
1 篇文章 0 订阅

JavaSE-Lambda表达式-03-201804

Stream(流)

1.背景

    自从lambda表达式成为Java语言的一部分之后,Java集合(Collections)API就面临着大幅变化。

    尽管我们可以从头实现一个新的集合框架(比如“Collection II”),但取代现有的集合框架是一项非常艰难的工作,因为集合接口渗透了 Java 生态系统的每个角落,将它们一一换成新类库需要相当长的时间。因此,我们决定采取演化的策略(而非推倒重来)以改进集合 API:

  • 为现有的接口(例如 CollectionList 和 Stream)增加扩展方法;
  • 在类库中增加新的 (stream,即 java.util.stream.Stream)抽象以便进行聚集(aggregation)操作;
  • 改造现有的类型使之可以提供流视图(stream view);
  • 改造现有的类型使之可以容易的使用新的编程模式,这样用户就不必抛弃使用以久的类库,例如 ArrayList 和 HashMap(当然这并不是说集合 API 会常驻永存,毕竟集合 API 在设计之初并没有考虑到 lambda 表达式。我们可能会在未来的 JDK 中添加一个更现代的集合类库)。
  • 提供更加易用的并行(Parallelism)库。尽管 Java 平台已经对并行和并发提供了强有力的支持,然而开发者在实际工作(将串行代码并行化)中仍然会碰到很多问题。我们把编程的重点从具体执行细节(how computation should be formed)转移到抽象执行步骤(what computation should be perfomed)。除此之外,我们还需要在将并行变的 容易(easier)和将并行变的 不可见(invisible)之间做出抉择,我们选择了一个折中的路线:提供 显式(explicit)但 非侵入(unobstrusive)的并行。(如果把并行变的透明,那么很可能会引入不确定性(nondeterminism)以及各种数据竞争(data race)问题)。
2.内部迭代与外部迭代(internal or external iteration)

    集合类库主要依赖于外部迭代,Collection 实现 Iterable 接口,从而使得用户可以依次遍历集合的元素。

                List<School> list=new ArrayList<>();
		list.add(new School("Havard",1));
		list.add(new School("Cambridge",2));
		list.add(new School("Oxford",3));
		list.add(new School("Stanford",4));
		list.add(new School("MIT",5));
		for(School s:list) {                       //外部迭代
			System.out.println(s);
		}
		list.forEach(s->System.out.println(s));    //内部迭代

    for-each循环调用list的iterator()方法进行依次遍历。外部循环的代码非常直接,但它有如下问题:

  • Java 的 for 循环是串行的,而且必须按照集合中元素的顺序进行依次处理;
  • 集合框架无法对控制流进行优化,例如通过排序、并行、短路(short-circuiting)求值以及惰性求值改善性能。
       有时 for-each 循环的这些特性(串行,依次)是我们所期待的,但它对改善性能造成了阻碍。我们可以使用内部迭代(internal iteration)替代外部迭代,用户把对迭代的控制权交给类库,并向类库传递迭代时所需执行的代码。

    内部迭代的forEach()方法实际也是采取for-each循环遍历,但我们无法控制迭代的具体流程。用户把对操作的控制权交还给类库,从而允许类库进行各种各样的优化(例如乱序执行、惰性求值和并行等等)。总的来说,内部迭代使得外部迭代中不可能实现的优化成为可能。

    外部迭代同时承担了做什么(打印每个条目)和怎么做(得到 Iterator 实例然后依次遍历)两项职责,而内部迭代只负责做什么,而把怎么做留给类库。

3.Stream(流)

    a.概念:A sequence of elements supporting sequential and parallel aggregate operations.

    b.目的:提供一种在比集合更高维度上指定计算的数据视图.(stream view)。
     c.概况:定义于 java.util.stream(这个包里有若干流类型:Stream<T> 代表对象引用流,此外还有一系列特定(specialization)流,比如 IntStream 代表整形数字流)。流的操作可以被组合成 流水线(Pipeline)。
    d.流与集合的区别:

  • 流不存储元素,这些元素可能存在于底层的集合中,或是按需生成;
  • 流操作不会修改数据源,比如filter()不会从原始流中移除元素,而是生成一个不包含已过滤元素的新流;
  • 流操作尽可能使惰性执行的(lazy evaluation)。即直至需要其结果时,才会被执行。(??)

    f.流的创建步骤:

        i.创建一个流

        ii.指定将初始流转换成其他流的中间操作

        iii.终止这个流,得到结果。此动作将强制流执行之前的惰性操作。终止之后该流将无法被调用。

4.流的创建

    a.将集合转换成一个流:使用Collection接口的stream()方法

        Stream<Employee> stream=list.stream();

    b.将数组转换成一个流:主要有两种方法-Stream.of(T... values)和Arrays.stream(T[] arr,int start,int end)

//将数组转换成一个流使用public static<T> Stream<T> of(T... values)
		String[] euCities={"Munchen","Paris","London","Oslo","Manchester"};
		String[] asiaCities={"Tokyo","Seoul","HK","Mumbai"};
		Stream.of(euCities).filter(s->s.length()>5).forEach(System.out::println);
		Stream.of(euCities,asiaCities).forEach(s->System.out.println(Arrays.toString(s)));
		System.out.println("-----------------------------");
		//Arrays.stream(T[] array,int startInclusive,int endExclusive)--取下标为[0,3)的数组元素成流
		Arrays.stream(euCities).filter(s->s.contains("o")).forEach(System.out::println);
		Arrays.stream(asiaCities, 0, 3).forEach(System.out::println);

    c.创建一个初始为空的流

        Stream<String> s2=Stream.empty();

    d.创建一个无限流(延迟计算/惰性)

        i.  public static<T> Stream<T> generate(Supplier<T> s)

        返回无限顺序无序流,其中每个元素由提供的Supplier生成。 这适合于产生恒定流,随机元素流等。

            ①生成一个无限fabonacci流,使用limit(n)取前n项

Stream.generate(new Fabonacii()).limit(10).forEach(System.out::println);

class Fabonacii implements Supplier<Long>{
	long a=0;
	long b=1;
	@Override
	public Long get() {
		// TODO Auto-generated method stub
		long sum=a+b;
		a=b;
		b=sum;
		return a;
	}
}

           ②利用级数展开 π/4=1-1/3+1/5-1/7... ,计算π(莱布尼茨级数)

Stream.generate(new piTest()).skip(100).limit(10).forEach(System.out::println);
class piTest implements Supplier<Double>{
	double sgn=-1;
	double deno=-1;
	double sum=0;
	@Override
	public Double get() {
		// TODO Auto-generated method stub
		sgn*=-1;
		deno+=2;
		sum+=sgn/deno;
		return sum*4;
	}
}

           ③ 使用泰勒级数展开计算arctanx

                /**
		 * 使用泰勒级数展开 计算arctanx
		 * 在0点泰勒展开 arctanx=∑(-1)^n*x^(2n+1)/(2n+1) ,n∈[0,+∞)
		 */
//    arctan1=π/4		 
Stream.generate(new Arctan(1.0)).skip(1000).limit(10).forEach(System.out::println);

class Arctan implements Supplier<Double>{
	double sgn=-1;
	double deli=-1;
	double res=0;
	Double x;
	
	public Arctan(Double x) {
		this.x=x;
	}
	
	@Override
	public Double get() {
		// TODO Auto-generated method stub
		sgn*=-1;
		deli+=2;
		x*=x*x;
		res+=sgn*x/deli;
		return res;
	}
}

           ④马青公式  π/4=4arctan1/5+arctan1/239快速逼近π;  

 /**
		 * 使用马青公式  π/4=4arctan1/5+arctan1/239快速逼近π; 将arctanx=∑(-1)^n*x^(2n+1)/(2n+1) ,n∈[0,+∞)代入
		 * 这里使用BigDecimal提高精度[double有效数字15位]
		 */    
                Stream<BigDecimal> machin=Stream.generate(new Machin()).limit(10);
		//由于lambda表达式中引入的外部变量是等效常量,无法表示索引,这里使用数组的方法折中处理
		int[] index= {0};
		machin.forEach(t->{
			int idx=++index[0];
			char[] s=t.toString().toCharArray();
			char[] pi=String.valueOf(Math.PI).toCharArray();
			for(int i=0;i<s.length;i++) {
				if(s[i]!=pi[i]) {
					System.out.println(t.toString()+"  "+idx+".精确到小数点后第"+(i-2)+"位");
					break;
				}
			}
		});

class Machin implements Supplier<BigDecimal>{
	final BigDecimal t1=BigDecimal.valueOf(25);
	final BigDecimal t2=BigDecimal.valueOf(57121);	//239*239
	BigDecimal s1=BigDecimal.valueOf(5);
	BigDecimal s2=BigDecimal.valueOf(239);
	final BigDecimal li=BigDecimal.valueOf(4);
	BigDecimal sgn=BigDecimal.valueOf(-1);
	BigDecimal deli=BigDecimal.valueOf(-1);
	BigDecimal res=BigDecimal.valueOf(0.0);
	
	@Override
	public BigDecimal get() {
		// TODO Auto-generated method stub
		sgn=sgn.multiply(BigDecimal.valueOf(-1));
		deli=deli.add(BigDecimal.valueOf(2));
		s1=s1.divide(t1,20,RoundingMode.HALF_EVEN);        //保留小数点后20位
		s2=s2.divide(t2,20,RoundingMode.HALF_EVEN);
		BigDecimal temp=s1.multiply(li).subtract(s2);
		res=res.add(sgn.divide(deli,20,RoundingMode.HALF_EVEN).multiply(temp).multiply(li)); 
		return res;                                      //两个小数点后保留20位的数相乘,最后保留40位
	}	
}

        结果: 第10次就已精确到小数点后14位

3.1832635983263598326400000000000000000000  1.精确到小数点后第1位
3.1405970293260603143070933323566696618500  2.精确到小数点后第2位
3.1416210293250344250510933323566696618500  3.精确到小数点后第3位
3.1415917721821772950225224889280982330220  4.精确到小数点后第5位
3.1415926824043995172447447020480982330220  5.精确到小数点后第7位
3.1415926526153086081538356114368982330220  6.精确到小数点后第8位
3.1415926536235547619999894576310742330220  7.精确到小数点后第9位
3.1415926535886022286666561242959932730220  8.精确到小数点后第11位
3.1415926535898358474901855360606498106220  9.精确到小数点后第12位
3.1415926535897916969217644834290730707820  10.精确到小数点后第14位

    ii. public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)

       返回有序无限连续Stream由函数的迭代应用产生f到初始元素seed ,产生Streamseedf(seed)f(f(seed))

        ①创建一个自然数流

		//使用iterate生成一个带初始值的无限迭代流--自然数
		Stream.iterate(0,i->i+1).limit(10).forEach(System.out::println);
        ②3n+1猜想:随意选一个整数,如果它是偶数,那么将它除以2;如果它是奇数,那么将它乘以3再加1。最终将得到1
Stream.iterate(99,i->i%2==0?i/2:3*i+1).takeWhile(i->i!=1).forEach(System.out::println);

    e.使用Pattern类的splitAsStream方法创建一个流

        根据给定的输入序列创建一个流,该流与该模式匹配。

        public Stream<String> splitAsStream(final CharSequence input)

		String s="dfwefewfwefweffqwweffrgrgweweeqwweerrf";
		Pattern p=Pattern.compile("e");
		p.splitAsStream(s).filter(i->i!="").forEach(i->System.out.print(i+","));

    f.使用Files类的静态方法

       ① public static Stream<String> lines(Path path, Charset cs)

            从文件中读取所有行作为 Stream 。 

       ② public static Stream<Path> walk(Path start, FileVisitOption... options)

            返回一个Stream ,它通过走在一个给定的起始文件的根文件树上, Path 。 文件树被深度优先地遍历,流中的元素是Path对象,如resolving所示 ,相对路径为start

		Path path=Paths.get("D:/java");
		try(Stream<Path> paths=Files.walk(path, 1)){
			paths.forEach(pa->System.out.println(pa.getFileName()));
		}catch(IOException e) {
			e.printStackTrace();
		}
    g.IntStream类的静态方法range
		//返回有序IntStream:从[s1,s2]递增元素
		IntStream.rangeClosed(0,6).forEach(System.out::println);
		//返回有序IntStream:从[s1,s2)递增元素
		IntStream.range(0,6).forEach(System.out::println);

    h.使用StreamSupport类的静态方法与Spliterator的trySplit方法创建

		Spliterator<School> sp=list.spliterator();
		Spliterator<School> spl=sp.trySplit();
		Spliterator<School> spl2=spl.trySplit();
		StreamSupport.stream(sp, false).forEach(System.out::println);
		System.out.println("-------------------------");
		StreamSupport.stream(spl, false).forEach(System.out::println);
		System.out.println("-------------------------");
		StreamSupport.stream(spl2, false).forEach(System.out::println);

参考:http://zh.lucida.me/blog/java-8-lambdas-inside-out-library-features/

           https://www.liaoxuefeng.com/article/001411309538536a1455df20d284b81a7bfa2f91db0f223000

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值