For example, I have two Arrays converted into ArrayList which is firstName and lastName. I want to sort these two lists using the first names, the last name will follow through the first names.
Expected output:
firstNameList = {Andrew, Johnson, William}
lastNameList = {Wiggins, Beru, Dasovich};
My Initial Program:
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
String [] firstName = {William, Johnson, Andrew};
String [] lastName = {Dasovich, Beru, Wiggins};
//Will convert arrays above into list.
List <String> firstNameList= new ArrayList<String>();
List <String> lastNameList= new ArrayList<String>();
//Conversion
Collections.addAll(firstNameList, firstName);
Collections.addAll(lastNameList, lastName);
As I have stated in my comment, I would recommend using a Person
- POJO to bind firstName
and lastName
in a semantic way:
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());
}
}
To convert two String[]
s firstNames
and lastNames
into a List<Person>
, one can provide a method:
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));
}
A remark on this method: Here, we use collect(Collectors.toCollection(...))
instead of collect(Collectors.toList())
to have some control with respect to list mutability since we are going to sort the list.
From here on there are two general routes: Either one makes Person
comparable by public class Person implements Comparable<Person>
or one writes a Comparator<Person>
. We will discuss both possibilities.
The goal is to sort Person
-objects. The primary criteria for sorting is the first name of the person. If two persons have equal first names, then they should be ordered by their last names. Both first- and last name are String
-objects and should be ordered in lexicographical order, which is String
's natural order.
Comparable<Person>
on Person
The logic to implement the comparison is straight-forward:
firstName
s of two persons using equals(...)
.lastName
s using compareTo(...)
and return the result.firstName
s with compareTo(...)
and return the result.The corresponding method would then look like this:
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());
}
...
}
While not strictly necessary, it is recommended that the natural ordering of a class (ie the Comparable
-implementation) is consistent with its equals(...)
-implementation. Since this is not the case right now, I would recommend overriding equals(...)
and 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());
}
...
}
The following code demonstrates how to create and order a List<Person>
from two String[]
:
final List<Person> persons = constructPersons(
new String[]{"Clair", "Alice", "Bob", "Alice"},
new String[]{"Clear", "Wonder", "Builder", "Ace"}
);
Collections.sort(persons);
System.out.println(persons);
Comparator<Person>
A traditional implementation of a comparator realizing the sort comparison given in the challenge-section may look like this:
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());
}
}
A example call may look like this:
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);
With Java 8, the construction of a Comparator
has been simplified through the Comparator.comparing
-API . To define a Comparator
realizing the order given in section Challenge with the Comparator.comparing
-API, we only need one line of code:
Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
The following code demonstrates how this Comparator
is used to sort a 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);
A MRE is available on Ideone .
I would question the initial design decision to split up first- and last names into two separate arrays. I opted to not include the method List<Person> constructPersons(String[] firstNames, String[] lastNames)
in class Person
since this is just adapter-code. It should be contained in some mapper, but is not a functionality that is existential for Person
.
You can do this by merging the two arrays into one stream of Names, with first and last name, sort that stream and then recreate the two lists.
String[] firstName = {"William", "Johnson", "Andrew"};
String[] lastName = {"Dasovich", "Beru", "Wiggins"};
final var sortedNames = IntStream.range(0, firstName.length)
.mapToObj(i -> new Name(firstName[i], lastName[i]))
.sorted(Comparator.comparing(n -> n.firstName))
.collect(Collectors.toList());
final var sortedFirstNames = sortedNames.stream()
.map(n -> n.firstName)
.collect(Collectors.toList());
final var sortedLastNames = sortedNames.stream()
.map(n -> n.lastName)
.collect(Collectors.toList());
As highlighted by comments , your problem is you are using two different lists for names and surnames so the ordering process for the two types of data are totally unrelated. A possible solution is creation of a new class Person
including two fields name
and surname
and implementing Comparable
interface like below:
public class Person implements Comparable<Person> {
public String firstName;
public String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return "Person [firstName=" + firstName + ", lastName=" + lastName + "]";
}
@Override
public int compareTo(Person o) {
return this.firstName.compareTo(o.firstName);
}
public static void main(String[] args) {
Person[] persons = { new Person("William", "Dasovich"),
new Person("Johnson", "Beru"),
new Person("Andrew", "Wiggins") };
Collections.sort(Arrays.asList(persons));
for (Person person : persons) {
System.out.println(person);
}
}
}
The Collections.sort
method provides the order of the Person
array by firstName
.
Because the firstName
and lastName
are connected to each other, you should create a class to model them as such. Let's call this class Person
:
class Person {
private final String firstName;
private final String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
// Add toString, equals and hashCode as well.
}
Now, create a list of persons instead:
List<Person> persons = Arrays.asList(
new Person("Andrew", "Wiggins"),
new Person("Johnson", "Beru"),
new Person("William", "Dasovich"));
Now, to sort it, you can use the sorted
method on a stream with a comparator. This will create a new List<Person>
which will be sorted. The Comparator.comparing
function will let you pick which property of the Person
class that you want to sort on. Something like this:
List<Person> sortedPersons = persons.stream()
.sorted(Comparator.comparing(Person::getFirstName))
.collect(Collectors.toList());
A TreeSet could do it:
(using a Person class as suggested by Turing85)
import java.util.Set;
import java.util.TreeSet;
public class PersonTest {
private static class Person implements Comparable<Person> {
private final String firstName;
private final String lastName;
public Person(final String firstName, final String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public int compareTo(final Person otherPerson) {
return this.firstName.compareTo(otherPerson.firstName);
}
@Override
public String toString() {
return this.firstName + " " + this.lastName;
}
}
public static void main(final String[] args) {
final Set<Person> people = new TreeSet<>();
/**/ people.add(new Person("William", "Dasovich"));
/**/ people.add(new Person("Johnson", "Beru"));
/**/ people.add(new Person("Andrew", "Wiggins"));
people.forEach(System.out::println);
}
}
but Streams & a somewhat simpler Person class might do too:
import java.util.stream.Stream;
public class PersonTest {
private static class Person {
private final String firstName;
private final String lastName;
public Person(final String firstName, final String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return this.firstName + " " + this.lastName;
}
}
public static void main(final String[] args) {
Stream.of(
new Person("William", "Dasovich"),
new Person("Johnson", "Beru" ),
new Person("Andrew", "Wiggins" ) )
.sorted ((p1,p2) -> p1.firstName.compareTo(p2.firstName))
.peek (System.out::println)
.sorted ((p1,p2) -> p1.lastName .compareTo(p2.lastName))
.forEach(System.out::println);
}
}
String[] firstName = {"William", "Johnson", "Andrew"};
String[] lastName = {"Dasovich", "Beru", "Wiggins"};
// combine the 2 arrays and add the full name to an Array List
// here using a special character to combine, so we can use the same to split them later
// Eg. "William # Dasovich"
List<String> combinedList = new ArrayList<String>();
String combineChar = " # ";
for (int i = 0; i < firstName.length; i++) {
combinedList.add(firstName[i] + combineChar + lastName[i]);
}
// Sort the list
Collections.sort(combinedList);
// create 2 empty lists
List<String> firstNameList = new ArrayList<String>();
List<String> lastNameList = new ArrayList<String>();
// iterate the combined array and split the sorted names to two lists
for (String s : combinedList) {
String[] arr = s.split(combineChar);
firstNameList.add(arr[0]);
lastNameList.add(arr[1]);
}
System.out.println(firstNameList);
System.out.println(lastNameList);
If you don't want to create DTO to keep the first names and last names together, you can use a kind of functional way based on java streams :
String[] firstName = {"William", "Johnson", "Andrew"};
String[] lastName = {"Dasovich", "Beru", "Wiggins"};
//Will convert arrays above into list.
List<String> firstNameList = new ArrayList<String>();
List<String> lastNameList = new ArrayList<String>();
//Conversion
Collections.addAll(firstNameList, firstName);
Collections.addAll(lastNameList, lastName);
List<String> collect = firstNameList
.stream()
.map(name -> {
List<String> couple = List.of(name, lastNameList.get(0));
lastNameList.remove(0);
return couple;
})
.sorted(Comparator.comparing(l -> l.get(0)))
.flatMap(Collection::stream)
.collect(Collectors.toList());
String[] firstNames = {William, Johnson, Andrew};
String[] lastNames = {Dasovich, Beru, Wiggins};
//Will convert arrays above into list.
List<String> firstNameList = new ArrayList<String>();
List<String> lastNameList = new ArrayList<String>();
Map<String, String> lastNameByFirstName = new HashMap<>();
for (int i = 0; i < firstNames.length; i++) {
lastNameByFirstName.put(firstNames[i], lastNames[i]);
}
//Conversion
Collections.addAll(firstNameList, firstNames);
Collections.sort(firstNameList);
for (String firstName : firstNameList) {
lastNameList.add(lastNameByFirstName.get(firstName));
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.