首页 > 代码库 > Lambda表达式学习篇二(流-上)

Lambda表达式学习篇二(流-上)

一、从外部迭代到内部迭代

  使用for循环计算伦敦的艺术家人数:

int count=0;

  for(Artist artist:allArtists){

  if(artist.isFrom("London")){

  count++;

  }

  }

 每次迭代集合类时,都需要写较多样本代码。

  调用iterator,产生一个新的Iterator对象,进而控制整个迭代过程,这就是外部迭代。

int count=0;

Iterator<Artist> iterator=allArtists.iterator();

while(iterator.hasNext()){

  Artist artist=iterator.next();

  if(artist.isFrom("London"))

  {

    count++;

  }

}

 

  再来看内部迭代。

1 long count=allArtists.stream()
2                      .filter(artist->artist.isFrom("London"))
3                      .count();

  stream是函数式编程方式在集合类上进行复杂操作的工具。这里的stream()方法的调用和iterator调用的方法类似,但是该方法返回的是一个接口:Stream,而不是一个对象。

 

二、实现机制

这里对上述内部迭代代码进行分析:

  allArtists.stream().filter(artist->artist.isFrom("London"));这行代码中filter只刻画了Stream,但没有产生新的集合。这种不产生新集合的方法叫作惰性求值方法;而像count这样会产生值得方法叫作及早求值方法。

  

  这样我们添加一两个例子:

allArtists.stream().filter(artist->{ 
  System.out.println(artist.getName()); return artist.isFrom("London"); });
//这里不会出项任何显示结果
long count=allArtists.stream().filter(artist->{
        System.out.println(artist.getName());   
            return artist.isFrom("London");

}).count();

//这里会显示出成员艺术家列表名单

判断很简单:返回值是否为Stream。

整个过程和建造者模式有共通之处。

三、常用的流操作

1、collec(toList()) 方法由Stream里的值生成一个列表,是一个及早求值操作。Stream的of方法使用一组初始值生产新的Steam。

List<String> collected = Stream.of("a","b","c").collect(Collectiors.toList());
assertEquals(Array.asList("a","b","c"),collected);                                                                                                                                                          

  2、map

  map可以将一个流中的值转换为成一个新的流。

  首先看一下一个平时所见的例子:

//for循环将字符串转换为大写
List<String> collected=new ArrayList<>(); for(String string:asList("a","b","hello")){ String uppercaseString=string.toUpperCase(); collected.add(upercaseString); } assertEquals(asList("A","B","HELLO"),colected);

 

//使用map操作将字符串转换为大写形式
List<String> collected=Stream.of("a","b","c").map(string->string.toUpperCase()).collect(toList());
assertEquals(asList("A","B","C"),collected);

 

3、filter

  遍历数据并检查其中的元素时,可尝试使用stream中提供的新方法filter,通map有点类似

使用循环遍历列表,使用条件语句判断

List<string>buginningWithNumbers=new ArrayList<>();
for(String value:asList("a","1abc","abc1"))
{
     if(isDigit(value.charAt(0))
     {  
      buginningWithNumbers.add(value);
    }          
}

 函数式风格:

List<String>beginningWithNumbers=Stream.of("a","1abc","abc1").filter(value->isDigit(value.charAt(0))).collect(toList());

 

4、flatMap

  调用stream方法,将每个列表装换成Stream对象,其余不封由flatMap方法处理。例子如下:

List<Integer>together=Stream.of(asList(1,2),asList(3,4)).flatMap(numbers->numbers.stream()).collect(toList());
assertEquals(asLsit(1,2,3,4),together);

 

5、max和min

  Stream上常用的操作之一是求最大值和求最小值。

使用Stream查找最短曲目:

List<Track>tracks=asList(
    new Track("Love",524),   new Track("For my love",378),
    new Track("For Your Love",451)); Track shortestTrack=track.stream().min(Comparator.comparing(track->track.getLength())).get();
assertEquals(tracks.
get(1),shortestTrack);

 

6、通用模式

  for循环查找专辑中最短的曲目

List<Track> tracks =asList(
      new Track("Dangers",524),
      new Track("Violets for Your Fures",378),
      new Track("Lost",451)  
    );
Track shorttestTrack=track.get(0);
for(Track track:tracks){
   if(track.getLength()<shortestTrack.getLength()){
      shortestTrack=track;    
  }
}    

  另外一种reduce模式

      Object accumulato=initialValue;
      for(Object element : collection)
     {
       accumulator=combine(accumulatror,element);
   }    

 

 7、reduce

   reduce操作可以实现从一个值中生成一个值。在上述例子中用到count、mun和max方法,因为常用被纳入标准库中。这些其实都是reduce操作。

使用reduce求和

int count =Stream.of(1,2,3).reduce(0,(acc,element)->acc+element);
assertEquals(6,count);

 Lambda表达式返回值是最新的acc,是上一轮acc的值和当前元素相加的结果。reducer的类型BinaryOperator;

 

下面展开reduce操作

BinaryOperator<Integer> accumulator=(acc,element)->acc+element;
int count=accumulator.apply(accumulator.apply(accumulator.apply(0,1),2),3)

 

 8、整合操作

  Stream()方法众多,在此我们介绍了Collect,map,filter,flatMap,max,min。在这儿我们总结一个例子来供自己理解。

  第一个要解决的问题是,找出某张专辑上所有乐队的国籍。艺术家列表里既有个人,也有乐队。利用一点领域知识,假定一般乐队名以定冠词The开头。当然不是绝对的,但也差不多。

  需要注意的是,这个问题绝对不是简单地调用几个API就足以解决。这既不是使用map将一组值映射为另一组值,也不是过滤,更不是将一个Stream中的元素最终归约为一个值。首先,可以将这个问题分解为以下步骤。

  1、找出专辑上所有的表演者。

  2、分辨出哪些表演者是乐队。

  3、找出每个乐队的国籍。

  4、将找出的国籍放入一个集合。

  现在,找出每一步对应的Stream API也就相对容易了:

  1、Album类有个getMusicians方法,该方法返回一个Stream对象,包含整张专辑中所有的表演者;

  2、使用filter方法对表演者进行过滤,值保留乐队;

  3、使用map方法将乐队映射为其所属国家;

  4、使用collect(Collectors.toList())方法将国籍放入一个列表。

  

  最后,整合所有的操作,就得到如下代码:

Set<String> origins=album.getMusicians()
               .filter(artist->artist.getName()
               .startsWith("Th"))
               .map(artist->artist.getNationality())
               .collect(toSet());

 

Lambda表达式学习篇二(流-上)