5.6 付诸实践

在本节中,你会将迄今学到的关于流的知识付诸实践。我们来看一个不同的领域:执行交易的交易员。你的经理让你为八个查询找到答案。你能做到吗?5.6.2节会给出答案,但你应该自己先尝试一下作为练习。

(1) 找出2011年发生的所有交易,并按交易额排序(从低到高)。

(2) 交易员都在哪些不同的城市工作过?

(3) 查找所有来自于剑桥的交易员,并按姓名排序。

(4) 返回所有交易员的姓名字符串,按字母顺序排序。

(5) 有没有交易员是在米兰工作的?

(6) 打印生活在剑桥的交易员的所有交易额。

(7) 所有交易中,最高的交易额是多少?

(8) 找到交易额最小的交易。

5.6.1 领域:交易员和交易

以下是你要处理的领域,一个TradersTransactions的列表:

  1. Trader raoul = new Trader("Raoul", "Cambridge");
  2. Trader mario = new Trader("Mario","Milan");
  3. Trader alan = new Trader("Alan","Cambridge");
  4. Trader brian = new Trader("Brian","Cambridge");
  5. List<Transaction> transactions = Arrays.asList(
  6. new Transaction(brian, 2011, 300),
  7. new Transaction(raoul, 2012, 1000),
  8. new Transaction(raoul, 2011, 400),
  9. new Transaction(mario, 2012, 710),
  10. new Transaction(mario, 2012, 700),
  11. new Transaction(alan, 2012, 950)
  12. );

TraderTransaction类的定义如下:

  1. public class Trader{
  2. private final String name;
  3. private final String city;
  4. public Trader(String n, String c){
  5. this.name = n;
  6. this.city = c;
  7. }
  8. public String getName(){
  9. return this.name;
  10. }
  11. public String getCity(){
  12. return this.city;
  13. }
  14. public String toString(){
  15. return "Trader:"+this.name + " in " + this.city;
  16. }
  17. }
  18. public class Transaction{
  19. private final Trader trader;
  20. private final int year;
  21. private final int value;
  22. public Transaction(Trader trader, int year, int value){
  23. this.trader = trader;
  24. this.year = year;
  25. this.value = value;
  26. }
  27. public Trader getTrader(){
  28. return this.trader;
  29. }
  30. public int getYear(){
  31. return this.year;
  32. }
  33. public int getValue(){
  34. return this.value;
  35. }
  36. public String toString(){
  37. return "{" + this.trader + ", " +
  38. "year: "+this.year+", " +
  39. "value:" + this.value +"}";
  40. }
  41. }

5.6.2 解答

解答在下面的代码清单中。你可以看看你对迄今所学知识的理解程度如何。干得不错!

代码清单 5-1 找出2011年发生的所有交易,并按交易额排序(从低到高)

  1. List<Transaction> tr2011 =
  2. transactions.stream()
  3. .filter(transaction -> transaction.getYear() == 2011) ←---- filter传递一个谓词来选择2011年的交易
  4. .sorted(comparing(Transaction::getValue)) ←---- 按照交易额进行排序
  5. .collect(toList()); ←---- 将生成的Stream中的所有元素收集到一个List

代码清单 5-2 交易员都在哪些不同的城市工作过

  1. List<String> cities =
  2. transactions.stream()
  3. .map(transaction -> transaction.getTrader().getCity()) ←---- 提取与交易相关的每位交易员的所在城市
  4. .distinct() ←---- 只选择互不相同的城市
  5. .collect(toList());

这里还有一个新招:你可以去掉distinct(),改用toSet(),这样就会把流转换为集合。你在第6章中会了解到更多相关内容。

  1. Set<String> cities =
  2. transactions.stream()
  3. .map(transaction -> transaction.getTrader().getCity())
  4. .collect(toSet());

代码清单 5-3 查找所有来自于剑桥的交易员,并按姓名排序

  1. List<Trader> traders =
  2. transactions.stream()
  3. .map(Transaction::getTrader) ←---- 从交易中提取所有交易员
  4. .filter(trader -> trader.getCity().equals("Cambridge")) ←---- 仅选择位于剑桥的交易员
  5. .distinct() ←---- 确保没有任何重复
  6. .sorted(comparing(Trader::getName)) ←---- 对生成的交易员流按照姓名进行排序
  7. .collect(toList());

代码清单 5-4 返回所有交易员的姓名字符串,按字母顺序排序

  1. String traderStr =
  2. transactions.stream()
  3. .map(transaction -> transaction.getTrader().getName()) ←---- 提取所有交易员姓名,生成一个Strings构成的Stream
  4. .distinct() ←---- 只选择不相同的姓名
  5. .sorted() ←---- 对姓名按字母顺序排序
  6. .reduce("", (n1, n2) -> n1 + n2); ←---- 逐个拼接每个名字,得到一个将所有名字连接起来的String

请注意,此解决方案效率不高(所有字符串都被反复连接,每次迭代的时候都要建立一个新的String对象)。下一章中,你将看到一个更为高效的解决方案,它像下面这样使用joining(其内部会用到StringBuilder):

  1. String traderStr =
  2. transactions.stream()
  3. .map(transaction -> transaction.getTrader().getName())
  4. .distinct()
  5. .sorted()
  6. .collect(joining());

代码清单 5-5 有没有交易员是在米兰工作的

  1. boolean milanBased =
  2. transactions.stream()
  3. .anyMatch(transaction -> transaction.getTrader()
  4. .getCity()
  5. .equals("Milan")); ←---- 把一个谓词传递给anyMatch,检查是否有交易员在米兰工作

代码清单 5-6 打印生活在剑桥的交易员的所有交易额

  1. transactions.stream()
  2. .filter(t -> "Cambridge".equals(t.getTrader().getCity())) ←---- 选择住在剑桥的交易员所进行的交易
  3. .map(Transaction::getValue) ←---- 提取这些交易的交易额
  4. .forEach(System.out::println); ←---- 打印每个值

代码清单 5-7 所有交易中,最高的交易额是多少

  1. Optional<Integer> highestValue =
  2. transactions.stream()
  3. .map(Transaction::getValue) ←---- 提取每项交易的交易额
  4. .reduce(Integer::max); ←---- 计算生成的流中的最大值

代码清单 5-8 找到交易额最小的交易

  1. Optional<Transaction> smallestTransaction =
  2. transactions.stream()
  3. .reduce((t1, t2) ->
  4. t1.getValue() < t2.getValue() ? t1 : t2); ←---- 通过反复比较每个交易的交易额,找出最小的交易

你还可以做得更好。流支持minmax方法,它们可以接受一个Comparator作为参数,指定计算最小或最大值时要比较哪个键值:

  1. Optional<Transaction> smallestTransaction =
  2. transactions.stream()
  3. .min(comparing(Transaction::getValue));