HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

A method that generates a Comparator<String> using lambda expression

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
generatesexpressionmethodthatusinglambdastringcomparator

Problem

Given below is an exercise from the book *Java SE 8 for the Really Impatient( by Cay S. Horstmann:


Write a method that generates a Comparator that can be normal or
reversed, case-sensitive or case-insensitive, space-sensitive or
space-insensitive, or any combination thereof. Your method should
return a lambda expression.

Is there a better (efficient, elegant) way to do it than below?

public static final int COMPARE_REVERSED = 0x01;
public static final int COMPARE_CASE_INSENSITIVE = 0x02;
public static final int COMPARE_SPACE_INSENSITIVE = 0x04;

public static Comparator getComparator(int flags) {

    return (s1, s2) -> {
        if (has(flags, COMPARE_REVERSED)) {
            String temp = s1;
            s1 = s2;
            s2 = temp;
        }
        if (has(flags, COMPARE_SPACE_INSENSITIVE)) {
            s1 = s1.replaceAll("\\s", "");
            s2 = s2.replaceAll("\\s", "");
        }
        if (has(flags, COMPARE_CASE_INSENSITIVE)) {
            return s1.compareToIgnoreCase(s2);
        }
        return s1.compareTo(s2);
    };
}

public static boolean has(int flags, int FLAG) {
    return (flags & FLAG) != 0;
}

Solution

This seems as efficient as it gets, to me.

In terms of elegance, Joshua Bloch (in Effective Java) recommends to stop using bit fields, use EnumSet instead.

The first step is to change your bit fields to an enum:

public enum Mode {
    COMPARE_REVERSED,
    COMPARE_CASE_INSENSITIVE,
    COMPARE_SPACE_INSENSITIVE
}


Next, change your method to take a Set instead of an int:

public static Comparator getComparator(Set flags) {
    return (s1, s2) -> {
        if (flags.contains(Mode.COMPARE_REVERSED)) {
            String temp = s1;
            s1 = s2;
            s2 = temp;
        }
        if (flags.contains(Mode.COMPARE_SPACE_INSENSITIVE)) {
            s1 = s1.replaceAll("\\s", "");
            s2 = s2.replaceAll("\\s", "");
        }
        if (flags.contains(Mode.COMPARE_CASE_INSENSITIVE)) {
            return s1.compareToIgnoreCase(s2);
        }
        return s1.compareTo(s2);
    };
}


You could call this method with any Set, but it will be best to call with an EnumSet, for example:

getComparator(EnumSet.of(Mode.COMPARE_CASE_INSENSITIVE, Mode.COMPARE_SPACE_INSENSITIVE));


Why is this better?

  • You don't need to worry about setting the bit fields correctly to be mutually exclusive



  • You don't need the has method anymore, now you can use simple Set.contains



  • You don't need the ... | ... bit operations anymore, EnumSet.of(...) is simple and clean



In short (quoted from the book):


No more bit twiddling, EnumSet does all the hard work for you.

I would also add a convenience method for the case of no flags:

public static Comparator getComparator() {
    return getComparator(Collections.emptySet());
}


And of course, a bunch of unit tests, for example:

@Test
public void testNormalComparator() {
    Comparator comparator = ComparatorGenerator.getComparator();
    assertNotEquals(0, comparator.compare("hello", "Hello"));
    assertEquals(0, comparator.compare("hello", "hello"));
}

@Test
public void testCaseInsensitiveComparator() {
    Comparator comparator = ComparatorGenerator.getComparator(EnumSet.of(ComparatorGenerator.Mode.COMPARE_CASE_INSENSITIVE));
    assertNotEquals(0, comparator.compare("hello", "xHello"));
    assertEquals(0, comparator.compare("hello", "Hello"));
    assertEquals(0, comparator.compare("hello", "hello"));
}

@Test
public void testReversedComparator() {
    Comparator comparator1 = ComparatorGenerator.getComparator();
    assertEquals(-1, comparator1.compare("hello1", "hello2"));
    Comparator comparator2 = ComparatorGenerator.getComparator(EnumSet.of(ComparatorGenerator.Mode.COMPARE_REVERSED));
    assertEquals(1, comparator2.compare("hello1", "hello2"));
}

@Test
public void testCaseAndSpaceInsensitiveComparator() {
    Comparator comparator = ComparatorGenerator.getComparator(EnumSet.of(ComparatorGenerator.Mode.COMPARE_CASE_INSENSITIVE, ComparatorGenerator.Mode.COMPARE_SPACE_INSENSITIVE));
    assertNotEquals(0, comparator.compare("hello there", "x hello there"));
    assertEquals(0, comparator.compare("HELLO there", "h e llo there"));
}

Code Snippets

public enum Mode {
    COMPARE_REVERSED,
    COMPARE_CASE_INSENSITIVE,
    COMPARE_SPACE_INSENSITIVE
}
public static Comparator<String> getComparator(Set<Mode> flags) {
    return (s1, s2) -> {
        if (flags.contains(Mode.COMPARE_REVERSED)) {
            String temp = s1;
            s1 = s2;
            s2 = temp;
        }
        if (flags.contains(Mode.COMPARE_SPACE_INSENSITIVE)) {
            s1 = s1.replaceAll("\\s", "");
            s2 = s2.replaceAll("\\s", "");
        }
        if (flags.contains(Mode.COMPARE_CASE_INSENSITIVE)) {
            return s1.compareToIgnoreCase(s2);
        }
        return s1.compareTo(s2);
    };
}
getComparator(EnumSet.of(Mode.COMPARE_CASE_INSENSITIVE, Mode.COMPARE_SPACE_INSENSITIVE));
public static Comparator<String> getComparator() {
    return getComparator(Collections.<Mode>emptySet());
}
@Test
public void testNormalComparator() {
    Comparator<String> comparator = ComparatorGenerator.getComparator();
    assertNotEquals(0, comparator.compare("hello", "Hello"));
    assertEquals(0, comparator.compare("hello", "hello"));
}

@Test
public void testCaseInsensitiveComparator() {
    Comparator<String> comparator = ComparatorGenerator.getComparator(EnumSet.of(ComparatorGenerator.Mode.COMPARE_CASE_INSENSITIVE));
    assertNotEquals(0, comparator.compare("hello", "xHello"));
    assertEquals(0, comparator.compare("hello", "Hello"));
    assertEquals(0, comparator.compare("hello", "hello"));
}

@Test
public void testReversedComparator() {
    Comparator<String> comparator1 = ComparatorGenerator.getComparator();
    assertEquals(-1, comparator1.compare("hello1", "hello2"));
    Comparator<String> comparator2 = ComparatorGenerator.getComparator(EnumSet.of(ComparatorGenerator.Mode.COMPARE_REVERSED));
    assertEquals(1, comparator2.compare("hello1", "hello2"));
}

@Test
public void testCaseAndSpaceInsensitiveComparator() {
    Comparator<String> comparator = ComparatorGenerator.getComparator(EnumSet.of(ComparatorGenerator.Mode.COMPARE_CASE_INSENSITIVE, ComparatorGenerator.Mode.COMPARE_SPACE_INSENSITIVE));
    assertNotEquals(0, comparator.compare("hello there", "x hello there"));
    assertEquals(0, comparator.compare("HELLO there", "h e llo there"));
}

Context

StackExchange Code Review Q#62400, answer score: 9

Revisions (0)

No revisions yet.