重学Java 8新特性 | 第8讲——趁热打铁,快来练练这三道有关Stream API的练习题

Posted 李阿昀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重学Java 8新特性 | 第8讲——趁热打铁,快来练练这三道有关Stream API的练习题相关的知识,希望对你有一定的参考价值。

在上一讲中,我们学了那么的Stream API,是不是应该要用一下啊,不然学它干嘛,所以,本讲我们就趁热打铁赶紧来用一下学过的Stream API,看你会不会用学过的Stream API来解决下面这三道练习题。

练习题一

题目是这样的:给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?即给定[1, 2, 3, 4, 5],应该返回[1, 4, 9, 16, 25]

这道练习题解决起来还是蛮简单的,关键是要用一下map方法,相信大家应该都知道这点,下面我直接给出代码清单。

package com.meimeixia.exer;

import org.junit.Test;

import java.util.Arrays;

/**
 * @author liayun
 * @create 2022-02-12 23:26
 */
public class TestStreamAPI 

    @Test
    public void test1() 
    	Integer[] nums = new Integer[]1, 2, 3, 4, 5;

        Arrays.stream(nums)
                .map((x) -> x * x)
                .forEach(System.out::println);
    


练习题二

题目是这样的:怎样用map和reduce这俩方法数一数流中有多少个元素(例如Employee)呢?

其实,要想数一数流中有多少个元素,很多种方式都可以来解决,但是这道题明确规定了我们只能使用map和reduce这俩方法来解决,所以我们就只能顺从了。

package com.meimeixia.exer;

import com.meimeixia.java8.Employee;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * @author liayun
 * @create 2022-02-16 18:57
 */
public class TestStreamAPI1 

    List<Employee> employees = Arrays.asList(
            new Employee(100, "张三", 18, 9999.99, Employee.Status.FREE),
            new Employee(101, "李四", 38, 5555.99, Employee.Status.BUSY),
            new Employee(102, "王五", 50, 6666.66, Employee.Status.VOCATION),
            new Employee(103, "赵六", 16, 3333.33, Employee.Status.FREE),
            new Employee(103, "赵六", 16, 3333.33, Employee.Status.FREE),
            new Employee(103, "赵六", 16, 3333.33, Employee.Status.FREE),
            new Employee(104, "田七", 8, 7777.77, Employee.Status.BUSY)
    );

    @Test
    public void test() 
        Optional<Integer> count = employees.stream()
                .map((e) -> 1)
                .reduce(Integer::sum); // 这是根据方法引用的第二种格式(即类::静态方法名)来书写成这样的哟!
        System.out.println(count.get());
    


注意,以上Employee实体类的代码,大家可以参考上一讲中的Employee实体类来写。

练习题三

题目是这样的:有如下三个类,它们分别是,

  • 交易员类:

    package com.meimeixia.exer;
    
    /**
    * 交易员类
    * @author liayun
    * @create 2022-02-16 19:17
    */
    public class Trader 
    
        private String name;
        private String city;
    
        public Trader() 
            super();
            // TODO Auto-generated constructor stub
        
    
        public Trader(String name, String city) 
            super();
            this.name = name;
            this.city = city;
        
    
        public String getName() 
            return name;
        
    
        public void setName(String name) 
            this.name = name;
        
    
        public String getCity() 
            return city;
        
    
        public void setCity(String city) 
            this.city = city;
        
    
        @Override
        public String toString() 
            return "Trader [name=" + name + ", city=" + city + "]";
        
    
    
    
  • 交易类:

    package com.meimeixia.exer;
    
    /**
    * 交易类
    * @author liayun
    * @create 2022-02-16 19:18
    */
    public class Transaction 
    
        private Trader trader;
        private int year;
        private int value;
    
        public Transaction() 
            super();
            // TODO Auto-generated constructor stub
        
    
        public Transaction(Trader trader, int year, int value) 
            super();
            this.trader = trader;
            this.year = year;
            this.value = value;
        
    
        public Trader getTrader() 
            return trader;
        
    
        public void setTrader(Trader trader) 
            this.trader = trader;
        
    
        public int getYear() 
            return year;
        
    
        public void setYear(int year) 
            this.year = year;
        
    
        public int getValue() 
            return value;
        
    
        public void setValue(int value) 
            this.value = value;
        
    
        @Override
        public String toString() 
            return "Transaction [trader=" + trader + ", year=" + year + ", value=" + value + "]";
        
    
    
    
  • 单元测试类:

    package com.meimeixia.exer;
    
    import org.junit.Before;
    
    import java.util.Arrays;
    import java.util.List;
    
    /**
    * @author liayun
    * @create 2022-02-16 19:19
    */
    public class TestTransaction 
    
        List<Transaction> transactions = null;
    
        @Before
        public void before() 
            Trader raoul = new Trader("Raoul", "Cambridge");
            Trader mario = new Trader("Mario", "Milan");
            Trader alan = new Trader("Alan", "Cambridge");
            Trader brian = new Trader("Brain", "Cambridge");
    
            transactions = Arrays.asList(
                    new Transaction(brian, 2011, 300),
                    new Transaction(raoul, 2012, 1000),
                    new Transaction(raoul, 2011, 400),
                    new Transaction(mario, 2012, 710),
                    new Transaction(mario, 2012, 700),
                    new Transaction(alan, 2012, 950)
            );
        
    
        //1. 找出2011年发生的所有交易,并按交易额排序(从低到高)。
    
        //2. 交易员都在哪些不同的城市工作过?
    
        //3. 查找所有来自剑桥的交易员,并按姓名排序。
    
        //4. 返回所有交易员的姓名字符串,并按字母顺序排序。
    
        //5. 有没有交易员是在米兰工作的?
    
        //6. 打印生活在剑桥的交易员的所有交易额。
    
        //7. 所有交易中,最高的交易额是多少。
    
        //8. 找到交易额最小的交易。
    
    
    

请你解决如下8个问题。

  1. 找出2011年发生的所有交易,并按交易额排序(从低到高)。
  2. 交易员都在哪些不同的城市工作过?
  3. 查找所有来自剑桥的交易员,并按姓名排序。
  4. 返回所有交易员的姓名字符串,并按字母顺序排序。
  5. 有没有交易员是在米兰工作的?
  6. 打印生活在剑桥的交易员的所有交易额。
  7. 所有交易中,最高的交易额是多少。
  8. 找到交易额最小的交易。

先来看第一个小问题,如果想要找出2011年发生的所有交易,并按交易额排序(从低到高),那么该怎么办呢?这题还是很简单的,下面我直接给出代码清单,不会的童鞋可以参考一下。

//1. 找出2011年发生的所有交易,并按交易额排序(从低到高)。
@Test
public void test1() 
    transactions.stream()
            .filter((t) -> t.getYear() == 2011)
            .sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))
            .forEach(System.out::println);

再来看第二个小问题,如果想要找出交易员都在哪些不同的城市工作过,那么是不是就应该像下面这样做啊!

//2. 交易员都在哪些不同的城市工作过?
@Test
public void test2() 
    transactions.stream()
            .map((t) -> t.getTrader().getCity())
            .distinct() // 去重
            .forEach(System.out::println);

再来看第三个小问题,如果想要查找出所有来自剑桥的交易员,并按姓名排序,那么是不是就应该像下面这样做啊!

//3. 查找所有来自剑桥的交易员,并按姓名排序。
@Test
public void test3() 
    transactions.stream()
            .filter((t) -> t.getTrader().getCity().equals("Cambridge"))
            .map(Transaction::getTrader)
            .sorted((t1, t2) -> t1.getName().compareTo(t2.getName()))
            .distinct()
            .forEach(System.out::println);

再来看第四个小问题,这个问题稍微的有那么点争议,就看你是从什么角度去理解这个问题了,下面我会从各种角度出发来解决该问题。

可能大家第一时间想到的代码是下面这样的。

//4. 返回所有交易员的姓名字符串,并按字母顺序排序。
@Test
public void test4() 
    transactions.stream()
            .map((t) -> t.getTrader().getName())
            .sorted()
            .forEach(System.out::println);

右键运行以上test4方法,你确实是可以在控制台中看到按字母顺序排序的所有交易员的姓名,如下图所示。

但是,有童鞋可能不是像上面那样理解的,他理解的是先把所有的交易员的姓名按照字母顺序进行排序,然后再连成一个字符串,即先排后拼。

//4. 返回所有交易员的姓名字符串,并按字母顺序排序。
@Test
public void test4() 
    transactions.stream()
            .map((t) -> t.getTrader().getName())
            .sorted()
            .forEach(System.out::println);

    System.out.println("----------------------------------");

    // 但是,有童鞋可能不是像上面那样理解的,他理解的是先把所有的交易员的姓名按照字母顺序进行排序,然后再连成一个字符串,即先排后拼。
    String str = transactions.stream()
            .map((t) -> t.getTrader().getName())
            .sorted()
            .reduce("", String::concat);// 先排后拼。注意,这是根据方法引用的第三种格式(即类::实例方法名)来书写成String::concat这样的哟!
    System.out.println(str);

右键运行以上test4方法,你便可以在控制台中看到先排后拼的所有交易员的姓名了。

当然,也有的童鞋是这样理解的:先将姓名字符串里面的一个一个字符给提取出来,然后再拼成一个字符串,接着按照字母顺序来进行排序,即先拼后排。如果真是这样理解的话,那么代码就应该像下面这样写了。

//4. 返回所有交易员的姓名字符串,并按字母顺序排序。
@Test
public void test4() 
    transactions.stream()
            .map((t) -> t.getTrader().getName())
            .sorted()
            .forEach(System.out::println);

    System.out.println("----------------------------------");

    // 但是,有童鞋可能不是像上面那样理解的,他理解的是先把所有的交易员的姓名按照字母顺序进行排序,然后再连成一个字符串,即先排后拼。
    String str = transactions.stream()
            .map((t) -> t.getTrader().getName())
            .sorted()
            .reduce("", String::concat);// 先排后拼。注意,这是根据方法引用的第三种格式(即类::实例方法名)来书写成String::concat这样的哟!
    System.out.println(str);

    System.out.println("----------------------------------");

    // 也有的童鞋是这样理解的:先将姓名字符串里面的一个一个字符给提取出来,然后再拼成一个字符串,接着按照字母顺序来进行排序,即先拼后排。
    transactions.stream()
            .map((t) -> t.getTrader().getName())
            .flatMap(TestTransaction::filterCharacter) // 将姓名字符串里面的一个一个字符给提取出来,并变成一个个字符串
            .sorted()
            .forEach(System.out::print);

其中,filterCharacter方法的代码如下所示。

public static Stream<String> filterCharacter(String str) 
    List<String> list = new ArrayList<String>();
    for (Character ch : str.toCharArray()) 
        list.add(ch.toString());
    
    return list.stream();

右键运行以上test4方法,你便可以在控制台中看到这样一串字符串了。

从上面可以清楚地看到,虽然是按照字母顺序来进行排序了,但是排的并不好,因为字母有大小写。实际上,字符在按照字母顺序进行排序时,底层是按照ASCII码来排的,即大写字母在前,小写字母在后。

这时,如果我们想忽略字母大小写来进行排序,那么又该怎么办呢?很简单,像下面这样做就行了!

//4. 返回所有交易员的姓名字符串,并按字母顺序排序。
@Test
public void test4() 
    transactions.stream()
            .map((t) -> t.getTrader().getName())
            .sorted()
            .forEach(System.out::println);

    System.out.println("----------------------------------");

    // 但是,有童鞋可能不是像上面那样理解的,他理解的是先把所有的交易员的姓名按照字母顺序进行排序,然后再连成一个字符串,即先排后拼。
    String str = transactions.stream()
            .map((t) -> t.getTrader().getName())
            .sorted()
            .reduce("", String::concat);// 先排后拼。注意,这是根据方法引用的第三种格式(即类::实例方法名)来书写成String::concat这样的哟!
    System.out.println(str);

    System.out.println("----------------------------------");

    // 也有的童鞋是这样理解的:先将姓名字符串里面的一个一个字符给提取出来,然后再拼成一个字符串,接着按照字母顺序来进行排序,即先拼后排。
    transactions.stream()
            .map((t) -> t重学Java 8新特性 | 第2讲——Java 8新特性简介

重学Java 8新特性 | 第2讲——Java 8新特性简介

重学Java 8新特性 | 第2讲——Java 8新特性简介

重学Java 8新特性 | 第5讲——函数式接口

重学Java 8新特性 | 第5讲——函数式接口

重学Java 8新特性 | 第1讲——我们为什么要学习Java 8新特性?