在 Java 8 中使用 Lambda 对 ArrayList 进行排序
Posted
技术标签:
【中文标题】在 Java 8 中使用 Lambda 对 ArrayList 进行排序【英文标题】:Sorting ArrayList with Lambda in Java 8 【发布时间】:2014-07-05 07:11:57 【问题描述】:有人可以向我展示一个简单的示例,如何使用新的 lambda 语法在 Java 8 中按字母顺序对 ArrayList
进行排序。
【问题讨论】:
我建议你阅读这个:docs.oracle.com/javase/tutorial/java/javaOO/… 【参考方案1】:对于字符串,这会起作用
arrayList.sort((p1, p2) -> p1.compareTo(p2));
【讨论】:
更好:arrayList.sort(String::compareTo) 更好:arrayList.sort(Comparator.naturalOrder())
请注意,.sort(String::compareTo) and .sort(Comparator.naturalOrder())
都会将 all 大写字母排在任何小写字母之前。通常你想要的是.sort(String::compareToIgnoreCase)
【参考方案2】:
您只是在对String
s 进行排序吗?如果是这样,您不需要 lambda;毫无意义。你只是这样做
import static java.util.Comparator.*;
list.sort(naturalOrder());
...虽然如果您使用 String
字段对对象进行排序,那么它会更有意义:
list.sort(comparing(Foo::getString));
【讨论】:
【参考方案3】:使用list.sort(String::compareToIgnoreCase)
使用list.sort(String::compareTo)
或list.sort(Comparator.naturalOrder())
会给出不正确(即非字母)的结果。它将在 all 小写字母之前对 any 大写字母进行排序,因此数组 ["aAAA","Zzz", "zzz"]
被排序为 ["Zzz", "aAAA", "zzz"]
【讨论】:
【参考方案4】:假设您有想要按字母顺序排序的名称列表(字符串)。
List<String> result = names.stream().sorted(
Comparator.comparing(n->n.toString())).collect(Collectors.toList());
它工作得很好。
【讨论】:
Comparator
完全不需要。【参考方案5】:
在函数式编程中,您不是使用旧对象来操作它们,而是以这种方式创建新对象:
list.stream().sorted().map(blah-blah).filter(...)...
【讨论】:
这是不正确的,因为现在流不再包含旧元素,而是映射和过滤的版本。考虑一个你想按人名排序的银行账户:如果你按照你的建议去做,那么你从一个银行账户流开始,到一个人名流结束,而你想以一个又是银行账户流。 一般来说,当你用 FP 风格编写程序时,你不需要迭代保存结果。所以,这一切都是一个新列表:list.stream().sorted() 不是对旧列表进行排序,而是创建新列表。 而该列表刚刚变得毫无用处,因为您仅留下了此人的姓名(在我的示例中),并且您不能再将其(直接)引用回他们的银行帐户. 另外,可能是您的困惑(无可指责)的根源是 OP 请求了一种对列表进行排序的方法,但您只返回一个中间流,您还需要将其存储在某些点,您从答案中省略了。通过尝试实现这一点,您自己也可能会看到问题。 @skiwi 不需要指定排序运算符,因为 OP 想要按字典顺序对String
s 进行排序,这是默认行为。【参考方案6】:
Lambda 不应该是目标。在您的情况下,您可以像在 Java 1.2 中一样对其进行排序:
Collections.sort(list); // case sensitive
Collections.sort(list, String.CASE_INSENSITIVE_ORDER); // case insensitive
如果你想用 Java 8 的方式来做:
list.sort(Comparator.naturalOrder()); // case sensitive
list.sort(String.CASE_INSENSITIVE_ORDER); // case insensitive
您也可以使用list.sort(null)
,但我不建议这样做,因为它不是类型安全的。
【讨论】:
【参考方案7】:一个真正通用的解决方案是引入一些 StreamUtil
之类的
public class StreamUtil
private StreamUtil()
@SuppressWarnings( "rawtypes", "unchecked" )
public static <TYPE> Comparator<TYPE> sort(Function<TYPE, ? extends Comparable> getterFunction, boolean descending)
if (descending)
return (o1, o2) -> getterFunction.apply(o2).compareTo(getterFunction.apply(o1));
return (o1, o2) -> getterFunction.apply(o1).compareTo(getterFunction.apply(o2));
调用看起来像
list.stream().sorted(sort(YourClass::getSortProperty, true));
【讨论】:
【参考方案8】:最简洁:
Collections.sort(stringList, String::compareToIgnoreCase);
【讨论】:
【参考方案9】:如果您有一个包含自然排序元素的数组(即String
、int
、double
);那么可以通过以下方式实现:
List<String> myList = new ArrayList<>();
myList.add("A");
myList.add("D");
myList.add("C");
myList.add("B");
myList.sort(Comparator.comparing(s -> s));
myList.forEach(System.out::println);
另一方面,如果您有一个对象数组,并且您想根据某种对象字段进行排序,那么您可以使用:
class User
double score;
// Constructor // Getters // Setters
List<User> users = new ArrayList<>();
users.add(new User(19d));
users.add(new User(67d));
users.add(new User(50d));
users.add(new User(91d));
List<User> sortedUsers = users
.stream()
.sorted(Comparator.comparing(User::getScore))
.collect(Collectors.toList());
sortedUsers.forEach(System.out::println);
如果排序更复杂,那么您必须编写自己的比较器并将其传入。
【讨论】:
【参考方案10】: List<Product> list = new ArrayList<>();
List<String> list1 = new ArrayList<>();
list.add(new Product(1));
list.add(new Product(2));
list.add(new Product(3));
list.add(new Product(10));
Collections.sort(list, Comparator.comparing((Product p) -> p.id));
for (Product p : list)
System.out.println(p.id);
【讨论】:
坚持函数式编程,for (Product p : list) System.out.println(p.id); 可以写成 list.stream().forEach(System.out::println);【参考方案11】:包含自定义比较器的语法非常简单快捷,例如这里被排序的 ArrayList 包含 JSONObjects,我将根据名为“order”的属性对其进行排序:
arrayList.sort((JSONObject o1, JSONObject o2)->o1.getInt("order")-o2.getInt("order"));
我认为这是一种非常简洁明了的语法,易于阅读并且比非 lambda 语法更简洁。
【讨论】:
以上是关于在 Java 8 中使用 Lambda 对 ArrayList 进行排序的主要内容,如果未能解决你的问题,请参考以下文章
[Java 8] Lambda表达式对递归的优化(上) - 使用尾递归 .
Java 8新特性:学习如何使用Lambda表达式,一看必懂