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;
    }
4
  • 2
    read about nullsFirst/nullsLast Commented Jul 3, 2021 at 16:57
  • Check answers for this question. stackoverflow.com/questions/3671826/… Commented Jul 3, 2021 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. Commented Jul 3, 2021 at 17:51
  • Eugene thanks for the suggestion @WJS yes I should return 0 in that case. Commented Jul 3, 2021 at 18:02

1 Answer 1

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());
            }
        });
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.