0

I have a list of Member which needs to be null checked handled within java 8 collection.sort method for a Person firstName incase if I have null values. Currently I have Member which implements Comparable interface inorder to compareTo another member's fullName. I am getting an NullPointerException from the TimSort binarySort. Seems to be it's a bug in OpenJDK. Found a solution to the NullPointerException in this question. Any recommended solutions for this problem from the above question with what I have mentioned below.

Main program

import java.util.Collections;
import java.util.List;

public class SortMembersByFirstName {
    public static void main(String[] args) {
        List<Member> members = getMembers();
        List<Member> sorted = setMemberCacheOrderByFirstName(members);
        for (Member mem: sorted) {
            System.out.println(mem.toString());
        }
    }

    private static List<Member> getMembers() {
        Person tim = new Person("Tim", "Southee");
        Member member1 = new Member(tim);
        Person eoin = new Person("Eoin", "Morgan");
        Member member2 = new Member(eoin);
        Person moeenAli = new Person("Moeen", "Ali");
        Member member3 = new Member(moeenAli);
        Person timFirstNameNull = new Person(null, "Tim");
        Member member4 = new Member(timFirstNameNull);
        Person timFirstNameLastNameNull = new Person(null, null);
        Member member5 = new Member(timFirstNameLastNameNull);
        Person nullPerson = null;
        Member member6 = new Member(nullPerson);

        List<Member> members = new ArrayList<>();
        members.add(member1);
        members.add(member2);
        members.add(member3);
        members.add(member4);
        members.add(member5);
        members.add(member6);
        return members;
    }

    /**
     * Order list members by first name
     */
    private static List<Member> setMemberCacheOrderByFirstName(List<Member> members) {
        // TimSort.binarySort NPE
        Collections.sort(members, (m1, m2) ->
            m1.getPerson().getFirstName().compareTo(m2.getPerson().getFirstName())
        );
        return members;
    }
}

Member class

class Member implements Comparable<Member>{
    private Person person;
    public Member(Person person){
        this.person = person;
    }
    public Person getPerson(){
        return this.person;
    }
    public void setPerson(Person person){
        this.person = person;
    }
    public String getFullName(){
        return this.getPerson() != null ? this.getPerson().getFirstName() + " " + this.getPerson().getLastName() : "";
    }
    public int compareTo(Member o) {
        return this.getFullName().compareTo(o.getFullName());
    }

    @Override
    public String toString() {
        return "Member{" +
                "person=" + person +
                '}';
    }
}

Person class

class Person{
    private String firstName;
    private String lastName;
    public String getFirstName(){
        return this.firstName;
    }
    public String getLastName(){
        return this.firstName;
    }
    public void setFirstName(String firstName){
        this.firstName = firstName;
    }

    @Override
    public String toString() {
        return "Person{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }

    public Person(String firstname, String lastname){
        this.firstName = firstname;
        this.lastName= lastname;
    }

}

One solution would be to catch the nullpointerexeceptions thrown. Like this

private static List<Member> setMemberCacheOrderByFirstName(List<Member> members) {
        Collections.sort(members, (m1, m2) -> {
            try {
                return m1.getPerson().getFirstName().compareTo(m2.getPerson().getFirstName());
            }catch (NullPointerException e){
                return 0;
            }
        });
        return members;
    }

Or Manually checks if there is a null value which isn't great.

private static List<Member> setMemberCacheOrderByFirstName(List<Member> members) {
        Collections.sort(members, (m1, m2) -> {
            // sort the list for firstnames
            if (m1.getPerson() != null && m2.getPerson() != null && m1.getPerson().getFirstName() != null
                    && m2.getPerson().getFirstName() != null) {
                return m1.getPerson().getFirstName().compareTo(m2.getPerson().getFirstName());
            } else {
                // Assuming any firstname is null
                if (m1.getPerson().getFirstName() == null || m2.getPerson().getFirstName() == null
                        && (m1.getPerson() != null && m2.getPerson() != null)) {
                    if (m1.getPerson().getFirstName() == null) {
                        m1.getPerson().setFirstName("");
                        return m1.getPerson().getFirstName().compareTo(m2.getPerson().getFirstName());
                    }
                    m2.getPerson().setFirstName("");
                    return m1.getPerson().getFirstName().compareTo(m2.getPerson().getFirstName());
                }
                // Assuming any member or person is null from schedulers
                return "".compareTo("");
            }
        });
        return members;
    }
  • 2
    read about `nullsFirst/nullsLast` – Eugene Jul 03 '21 at 16:57
  • Check answers for this question. https://stackoverflow.com/questions/3671826/how-to-handle-nulls-when-using-java-collection-sort/38530920 – Pramod Jul 03 '21 at 17:41
  • Why don't you just pull out the persons and assign to a variable like person1, person2, etc. and then eliminate then null conditions at the beginning. And for `return "".compareTo("")` just return 0. – WJS Jul 03 '21 at 17:51
  • Eugene thanks for the suggestion @WJS yes I should return 0 in that case. – Murshid Hassen Jul 03 '21 at 18:02

1 Answers1

1

Using Comparator.nullsFirst or Comparator.nullsLast would be the correct choice.

If you need null values at first. You could use this

Collections.sort(members, (m1, m2) -> {
            if(m1.getPerson() == null || m2.getPerson() == null ||
                    m1.getPerson().getFirstName() == null || m2.getPerson().getFirstName() == null){
                return -1;
            }

            if(m1.getPerson() != null && m2.getPerson() != null &&
                    m1.getPerson().getFirstName() != null && m2.getPerson().getFirstName() != null){
                return m1.getPerson().getFirstName().compareTo(m2.getPerson().getFirstName());
            }
            return 0;
        });

If you need null values at last. You could use this

Collections.sort(members, (m1, m2) -> {
            if(m1.getPerson() == null && m2.getPerson() == null &&
                    m1.getPerson().getFirstName() == null && m2.getPerson().getFirstName() == null){
                return -1;
            }

            if(m1.getPerson() != null && m2.getPerson() != null &&
                    m1.getPerson().getFirstName() != null && m2.getPerson().getFirstName() != null){
                return m1.getPerson().getFirstName().compareTo(m2.getPerson().getFirstName());
            }
            return 0;
        });

Optimized approach would be.

Collections.sort(members, (m1, m2) -> {
            if (m1.getPerson() == null || m2.getPerson() == null ||
                    m1.getPerson().getFirstName() == null ||
                    m2.getPerson().getFirstName() == null) {
                return -1;
            } else {
                return m1.getPerson().getFirstName().compareTo(m2.getPerson().getFirstName());
            }
        });