假设我有以下课程:
class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// getters and setters
}
假设我有一个Person
对象的列表,并且我想在列表中找到一个名字为'John'的Person对象。
我能想到的最短的代码是:
personList.stream()
.filter(person -> person.getFirstName().equals("John"))
.collect(Collectors.toList())
.get(0);
如您所见,它并不那么短。您能想到一个更短的方法吗?
修改:
有人建议使用findFirst()
。在这种情况下,代码将为:
personList.stream()
.filter(person -> person.getFirstName().equals("John"))
.findFirst()
.get()
只短一点。
答案 0 :(得分:2)
您可以为此使用Stream#findAny
。它返回一个Optional<Person>
,如果没有命中,则为空。
Optional<Person> person = personStream.filter(p -> p.getFirstName().equals("John")).findAny();
返回描述流中某些元素的
Optional
,如果流为空,则返回空Optional
。
也有findFirst
,但是findAny
少了两个字符,可能实现起来更快。
如果将过滤器移到专用方法中,代码会变得更短:
public static boolean isJohn(Person p) {
return "John".equals(p.getFirstName());
}
然后获取代码
Optional<Person> person = personStream.filter(MyClass::isJohn).findAny();
如果将过滤器移到专用谓词中,它甚至会更短:
Predicate<Person> isJohn = p -> "John".equals(p.getFirstName());
然后我们得到
Optional<Person> person = personStream.filter(isJohn).findAny();
出于简短代码的考虑而牺牲了可读性(从不这样做),我们可以替换变量名称并获得:
Optional<Person> p = s.filter(j).findAny();
建议将"John"
与人名进行比较,而不是相反。这样,它null
是安全的。您还可以使用Objects#equals
获得null
的安全性。即
// Not null-safe
p.getFirstName().equals("John");
// Null safe
"John".equals(p.getFirstName());
Objects.equals("John", p.getFirstName());
在未证明通话不会崩溃的情况下,请勿在{{1}}上致电get()
。不遵循这一点会破坏Optional
的目的。相反,最好使用Optional
方法。或者至少使用orElseXXX
保护访问。
答案 1 :(得分:2)
仅使用标准库,请使用其他答案提供的内容。它相对简短,清晰,标准,每个人都会理解。它迫使您明确处理不存在该元素的情况,这是一件好事。唯一的“不必要”样板是stream()
调用,它也具有其微妙的语义原理。
Person john = personList.stream()
.filter(person -> person.getFirstName().equals("John"))
.findFirst()
.get();
如果可以使用库,则可以使用更短(虽然不一定更好)的解决方案。使用Google Guava's Iterables.find()
:
Person john = Iterables.find(personList, person -> person.getFirstName().equals("John"));
(请注意,如果找不到该元素,则会引发异常。请考虑使用其他具有默认值的find()
方法或tryFind()
方法。)
许多其他库(例如Apache Commons Collections:IterableUtils.find()
。
在列表中搜索特定元素是线性或O(n)操作。如果要重复执行此操作,很明显应该使用其他数据结构。从人名到人的地图也许会更有用?
Map<String, Person> personsByFirstName = personList.stream()
.collect(toMap(Person::getFirstName, Function.identity()));
Person john = personsByFirstName.get("John");
(请注意,每个名字只能保留一个人。可能不是您想要的,是吗?)
答案 2 :(得分:1)
缩窄不一定更好。使用更高效或更易读/可维护的代码。
这是更短的代码,但在许多方面却更糟:
personList.removeIf(p -> !"John".equals(p.getFirstName()));
Stream.findFirst
通过在找到第一个元素后立即停止流来提高效率:
personList.stream()
.filter(person -> person.getFirstName.equals("John"))
.findFirst() //<-- This will stop your stream when the first match is found
.get();
但是,当您需要避免在空的get
上调用Optional
时,您必须使其变长。
这些只是两个示例,表明您可以通过不惜一切代价使代码简短来降低代码质量。
这是一个权衡,取决于开发人员(一切都是如此)。我认为,在效率/可读性和代码简洁性之间进行选择很容易。
答案 3 :(得分:0)
仅使用Stream.findFirst(),但不要以最短的方式思考。而是总是创建最容易理解的代码(在本例中为findFirst)。而且我认为代码中的2或3个字符并不重要。
readFile1, readFile2, readFile3, ......., readFile50
答案 4 :(得分:0)
我个人建议查看apache commons库,该库提供了许多有用的实用程序。 http://commons.apache.org/
Collections-package(http://commons.apache.org/proper/commons-collections/)具有IterableUtils
-utility类,该类可让您执行以下操作:
Person john = IterableUtils.find(personList, person -> person.getFirstName().equals("John"));
除此之外,它还提供了许多其他有用的东西。或更多示例:
Predicate<Person> isJohn = p -> "John".equals(p.getFirstName());
Person john = IterableUtils.find(personList, isJohn);