[英]Simplifying Lambda expressions on stream in Java
我正在嘗試自學如何在Java中使用lambda表達式而不是常規的外部迭代類型解決方案。 我做了一個簡短的程序,我使用整數流並為它們分配等級字母。 我沒有使用典型的“switch”語句,而是使用了這些lambda表達式。 有什么辦法可以簡化嗎? 似乎有更好的方法來做到這一點。 它有效,但看起來並不整潔。
grades.stream()
.mapToInt(Integer::intValue)
.filter(x -> x >= 90)
.forEach(x -> System.out.println(x + " -> A"));
grades.stream()
.mapToInt(Integer::intValue)
.filter(x -> x >= 80 && x <= 89)
.forEach(x -> System.out.println(x + " -> B"));
grades.stream()
.mapToInt(Integer::intValue)
.filter(x -> x >= 70 && x <= 79)
.forEach(x -> System.out.println(x + " -> C"));
grades.stream()
.mapToInt(Integer::intValue)
.filter(x -> x >= 60 && x <= 69)
.forEach(x -> System.out.println(x + " -> D"));
grades.stream()
.mapToInt(Integer::intValue)
.filter(x -> x >= 50 && x <= 59)
.forEach(x -> System.out.println(x + " -> F"));
grades
是一個ArrayList。
有什么辦法可以將邏輯全部合並到一個流語句中嗎? 我試過讓他們跟隨彼此,但不斷得到語法錯誤。 我錯過了什么? 我試着查看IntStream和Stream的文檔,但是找不到我想要的東西。
只需創建一個單獨的函數來映射成績字母並在單個流中調用它:
static char gradeLetter(int grade) {
if (grade >= 90) return 'A';
else if (grade >= 80) return 'B';
else if (grade >= 70) return 'C';
else if (grade >= 60) return 'D';
else return 'F';
}
grades.stream()
.mapToInt(Integer::intValue)
.filter(x -> x >= 50)
.forEach(x -> System.out.println(x + " -> " + gradeLetter(x)));
如果您特別想按年級排序結果,可以在流的開頭添加排序:
.sorted(Comparator.comparing(x -> gradeLetter(x)))
如果您願意使用第三方庫,此解決方案將適用於Java Streams和Eclipse Collections 。
List<Integer> grades = List.of(0, 40, 51, 61, 71, 81, 91, 100);
grades.stream()
.mapToInt(Integer::intValue)
.mapToObj(new IntCaseFunction<>(x -> x + " -> F")
.addCase(x -> x >= 90, x -> x + " -> A")
.addCase(x -> x >= 80 && x <= 89, x -> x + " -> B")
.addCase(x -> x >= 70 && x <= 79, x -> x + " -> C")
.addCase(x -> x >= 60 && x <= 69, x -> x + " -> D")
.addCase(x -> x >= 50 && x <= 59, x -> x + " -> F"))
.forEach(System.out::println);
這輸出如下:
0 -> F
40 -> F
51 -> F
61 -> D
71 -> C
81 -> B
91 -> A
100 -> A
我使用Java 9工廠方法來處理 Eclipse Collections中的List和IntCaseFunction 。 如果沒有其他情況匹配,我傳遞給構造函數的lambda將是默認情況。
數字范圍也可以由IntInterval的實例表示,可以使用contains作為方法引用來測試。 以下解決方案將在Java 10中使用局部變量類型推斷。
var mapGradeToLetter = new IntCaseFunction<>(x -> x + " -> F")
.addCase(x -> x >= 90, x -> x + " -> A")
.addCase(IntInterval.fromTo(80, 89)::contains, x -> x + " -> B")
.addCase(IntInterval.fromTo(70, 79)::contains, x -> x + " -> C")
.addCase(IntInterval.fromTo(60, 69)::contains, x -> x + " -> D")
.addCase(IntInterval.fromTo(50, 59)::contains, x -> x + " -> F");
var grades = List.of(0, 40, 51, 61, 71, 81, 91, 100);
grades.stream()
.mapToInt(Integer::intValue)
.mapToObj(mapGradeToLetter)
.forEach(System.out::println);
如果我刪除所有Predicates
和Functions
並刪除IntCaseFunction
並使用三元運算符替換為方法引用調用實例方法,則以下內容將起作用。
@Test
public void grades()
{
var grades = List.of(0, 40, 51, 61, 71, 81, 91, 100);
grades.stream()
.mapToInt(Integer::intValue)
.mapToObj(this::gradeToLetter)
.forEach(System.out::println);
}
private IntObjectPair<String> gradeToLetter(int grade)
{
var letterGrade =
grade >= 90 ? "A" :
grade >= 80 ? "B" :
grade >= 70 ? "C" :
grade >= 60 ? "D" : "F";
return PrimitiveTuples.pair(grade, letterGrade);
}
我在這里使用了對的toString()
實現,因此以下將是輸出。
0:F
40:F
51:F
61:D
71:C
81:B
91:A
100:A
可以使用對中包含的數字等級和字母等級在forEach中自定義輸出。
注意 :我是Eclipse Collections的提交者。
這是一個沒有開關/ if的解決方案,否則。
它具有各種條件和動作之間的映射(打印邏輯)
Map<Predicate<Integer>, Consumer<Integer>> actions = new LinkedHashMap<>();
//Beware of nulls in your list - It can result in a NullPointerException when unboxing
actions.put(x -> x >= 90, x -> System.out.println(x + " -> A"));
actions.put(x -> x >= 80 && x <= 89, x -> System.out.println(x + " -> B"));
actions.put(x -> x >= 70 && x <= 79, x -> System.out.println(x + " -> C"));
actions.put(x -> x >= 60 && x <= 69, x -> System.out.println(x + " -> D"));
actions.put(x -> x >= 50 && x <= 59, x -> System.out.println(x + " -> F"));
grades.forEach(x -> actions.entrySet().stream()
.filter(entry -> entry.getKey().apply(x))
.findFirst()
.ifPresent(entry -> entry.getValue().accept(x)));
注意:
簡介:我更喜歡使用switch / if,else來堅持原始代碼。
您可能希望將標記轉換為等級(int - > String)並循環遍歷arraylist。
使用流作為循環數據的機制和翻譯邏輯的方法
這是一個簡單的例子
private static void getGradeByMarks(Integer marks) {
if(marks > 100 || marks < 0) {
System.err.println("Invalid value " + marks);
return;
}
// whatever the logic for your mapping is
switch(marks/10) {
case 9:
System.out.println(marks + " --> " + "A");
break;
case 8:
System.out.println(marks + " --> " + "B");
break;
case 7:
System.out.println(marks + " --> " + "C");
break;
case 6:
System.out.println(marks + " --> " + "D");
break;
default:
System.out.println(marks + " --> " + "F");
break;
}
}
public static void main(String[] args) {
List<Integer> grades = Arrays.asList(90,45,56,12,54,88,-6);
grades.forEach(GradesStream::getGradeByMarks);
}
您可以在forEach
語句中添加這些條件
grades.stream()
.mapToInt(Integer::intValue)
.forEach(x -> {
if (x >= 90)
System.out.println(x + " -> A");
else if (x >= 80)
System.out.println(x + " -> B");
else if (x >= 70)
System.out.println(x + " -> C");
......
// other conditions
});
沒有辦法將“分叉”流分成多個流,如果這就是你所追求的。 如果您想要在流操作中編寫所有邏輯,那么您當前編寫它的方式是最好的。
我認為這里最簡單的方法就是編寫正常生成字符串的邏輯。 如果您想要了解它,可以使用mapToObj
,如下所示:
grades.stream()
.mapToInt(Integer::intValue)
.mapToObj(x -> {
if (x >= 90) {
return "A";
}
//etc
})
.forEach(System.out::println);
以下代碼生成與原始代碼完全相同的輸出,即以相同的順序。
grades.stream()
.filter(x -> x >= 50)
.collect(Collectors.groupingBy(x -> x<60? 'F': 'E'+5-Math.min(9, x/10)))
.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.flatMap(e -> e.getValue().stream().map(i -> String.format("%d -> %c", i, e.getKey())))
.forEach(System.out::println);
如果您不需要該訂單,則可以省略分組和排序步驟:
grades.stream()
.filter(x -> x >= 50)
.map(x -> String.format("%d -> %c", x, x<60? 'F': 'E'+5-Math.min(9, x/10)))
.forEach(System.out::println);
要么
grades.stream()
.filter(x -> x >= 50)
.forEach(x -> System.out.printf("%d -> %c%n", x, x<60? 'F': 'E'+5-Math.min(9, x/10)));
要么
grades.forEach(x -> {
if(x >= 50) System.out.printf("%d -> %c%n", x, x<60? 'F': 'E'+5-Math.min(9, x/10));
});
for (int x : grades)
{
if (x >= 90)
System.out.println(x + " -> A");
else if (x >= 80)
System.out.println(x + " -> B");
else if (x >= 70)
System.out.println(x + " -> C");
else if (x >= 60)
System.out.println(x + " -> D");
else if (x >= 50)
System.out.println(x + " -> F");
}
更容易閱讀和運行更快。 PS。 那么“E”級呢?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.