域
正如我在评论中所说,我建议使用Person-POJO 以语义方式绑定firstName 和lastName:
class Person {
public static final String PERSON_TO_STRING_FORMAT = "{f: %s, l: %s}";
private final String firstName;
private final String lastName;
private Person(final String firstName, final String lastName) {
this.firstName = Objects.requireNonNull(firstName);
this.lastName = Objects.requireNonNull(lastName);
}
public static Person of(final String firstName, final String lastName) {
return new Person(firstName, lastName);
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
@Override
public String toString() {
return String.format(PERSON_TO_STRING_FORMAT, getFirstName(), getLastName());
}
}
要将两个String[]sfirstNames和lastNames转换成List<Person>,可以提供一种方法:
public static List<Person> constructPersons(
final String[] firstNames,
final String[] lastNames) {
if (firstNames.length != lastNames.length) {
throw new IllegalArgumentException("firstNames and lastNames must have same length");
}
return IntStream.range(0, firstNames.length)
.mapToObj(index -> Person.of(firstNames[index], lastNames[index]))
.collect(Collectors.toCollection(ArrayList::new));
}
关于此方法的说明:这里,我们使用collect(Collectors.toCollection(...)) 而不是collect(Collectors.toList()) 来控制列表的可变性,因为我们要对列表进行排序。
从这里开始,有两条一般路线:一种是Personcomparable by public class Person implements Comparable<Person>,一种是写Comparator<Person>。我们将讨论这两种可能性。
挑战
目标是对Person-objects 进行排序。排序的主要标准是人的名字。如果两个人的名字相同,则应按姓氏排序。名字和姓氏都是String-objects,应该按照字典顺序排序,这是String的自然顺序。
解决方案 1:在 Person 上实现 Comparable<Person>
实现比较的逻辑很简单:
- 使用
equals(...)比较两个人的firstNames。
- 如果相等,则使用
compareTo(...) 比较 lastNames 并返回结果。
- 否则,将
firstNames 与compareTo(...) 进行比较并返回结果。
相应的方法将如下所示:
public class Person implements Comparable<Person> {
...
@Override
public final int compareTo(final Person that) {
if (Objects.equals(getFirstName(), that.getFirstName())) {
return getLastName().compareTo(that.getLastName());
}
return getFirstName().compareTo(that.getFirstName());
}
...
}
虽然不是绝对必要的,但建议类的自然顺序(即Comparable-实现)与其equals(...)-实现一致。由于现在不是这种情况,我建议覆盖 equals(...) 和 hashCode():
public class Person implements Comparable<Person> {
...
@Override
public final boolean equals(Object thatObject) {
if (this == thatObject) {
return true;
}
if (thatObject == null || getClass() != thatObject.getClass()) {
return false;
}
final Person that = (Person) thatObject;
return Objects.equals(getFirstName(), that.getFirstName()) &&
Objects.equals(getLastName(), that.getLastName());
}
@Override
public final int hashCode() {
return Objects.hash(getFirstName(), getLastName());
}
...
}
以下代码演示了如何从两个String[] 创建和订购List<Person>:
final List<Person> persons = constructPersons(
new String[]{"Clair", "Alice", "Bob", "Alice"},
new String[]{"Clear", "Wonder", "Builder", "Ace"}
);
Collections.sort(persons);
System.out.println(persons);
解决方案 2:实现 Comparator<Person>
实现挑战部分中给出的排序比较的比较器的传统实现可能如下所示:
class PersonByFirstNameThenByLastNameComparator implements Comparator<Person> {
public static final PersonByFirstNameThenByLastNameComparator INSTANCE =
new PersonByFirstNameThenByLastNameComparator();
private PersonByFirstNameThenByLastNameComparator() {}
@Override
public int compare(final Person lhs, final Person rhs) {
if (Objects.equals(lhs.getFirstName(), rhs.getFirstName())) {
return lhs.getLastName().compareTo(rhs.getLastName());
}
return lhs.getFirstName().compareTo(rhs.getFirstName());
}
}
示例调用可能如下所示:
final List<Person> persons = constructPersons(
new String[]{"Clair", "Alice", "Bob", "Alice"},
new String[]{"Clear", "Wonder", "Builder", "Ace"}
);
persons.sort(PersonByFirstNameThenByLastNameComparator.INSTANCE);
System.out.println(persons);
在 Java 8 中,Comparator 的构造已通过 Comparator.comparing-API 进行了简化。要使用Comparator.comparing-API 定义一个实现挑战部分给出的顺序的Comparator,我们只需要一行代码:
Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
以下代码演示如何使用此Comparator 对List<Person> 进行排序:
final List<Person> persons = constructPersons(
new String[]{"Clair", "Alice", "Bob", "Alice"},
new String[]{"Clear", "Wonder", "Builder", "Ace"}
);
persons.sort(Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName));
System.out.println(persons);
结束语
MRE 可在Ideone 获得。
我会质疑将名字和姓氏分成两个单独数组的初始设计决定。我选择不在类Person 中包含方法List<Person> constructPersons(String[] firstNames, String[] lastNames),因为这只是适配器代码。它应该包含在某个映射器中,但不是Person 存在的功能。