Search code examples
javasortingtreemapalphabetical

Sorting TreeMap alphabetically


I have an object of typer person, which has a name as attribute. I have a treemap which link the person to a distance, which is an integer.

I need to sort the TreeMap in alphabetical order, by looking at the name of each person in it.

How can I do that ? thank you for your answers.


Solution

  • Eric Berry wrote a handy class that compares Strings by human values instead of traditional machine values. Below is a modified version of it along with Object comparator (what I think you are looking for) and its testing class.

    An example on how to use the string comparator:

    Map<String,String> humanSortedMap = new TreeMap<>(new AlphaNumericStringComparator());
    

    An example on how to use the object comparator, but this time using a List instead of a TreeMap:

    Collections.sort(humanSortedList, new AlphaNumericObjectComparator<QuartzJobWrapper>()
                {
                    @Override
                    public int compare(QuartzJobWrapper t1, QuartzJobWrapper t2)
                    {
                        return compareStrings(t1.getName(), t2.getName());
                    }
                });
    

    AlphaNumericStringComparator Source:

      /*
     * Copyright (c) 2007 Eric Berry <elberry@gmail.com>
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     * 
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     * 
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    
    import java.text.DecimalFormatSymbols;
    import java.util.Comparator;
    import java.util.Locale;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    import org.apache.commons.lang3.StringUtils;
    
    /**
     * Compares Strings by human values instead of traditional machine values.
     * 
     * @author elberry
     * @modified Tristan Everitt
     */
    public class AlphaNumericStringComparator implements Comparator<String>
    {
    
        private Pattern alphaNumChunkPattern;
    
        public AlphaNumericStringComparator()
        {
            this(Locale.getDefault());
        }
    
        public AlphaNumericStringComparator(Locale locale)
        {
            DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale);
            char localeDecimalSeparator = dfs.getDecimalSeparator();
            // alphaNumChunkPatter initialized here to get correct decimal separator for locale.
            alphaNumChunkPattern = Pattern.compile("(\\d+\\" + localeDecimalSeparator + "\\d+)|(\\d+)|(\\D+)");
        }
    
        @Override
        public int compare(String s1, String s2)
        {
            int compareValue = 0;
            Matcher s1ChunkMatcher = alphaNumChunkPattern.matcher(s1);
            Matcher s2ChunkMatcher = alphaNumChunkPattern.matcher(s2);
            String s1ChunkValue = null;
            String s2ChunkValue = null;
    
            while (s1ChunkMatcher.find() && s2ChunkMatcher.find() && compareValue == 0)
            {
                s1ChunkValue = s1ChunkMatcher.group();
                s2ChunkValue = s2ChunkMatcher.group();
    
                // teveritt - Remove white space and make lower case to neutralise it
                s1ChunkValue = s1ChunkValue.replaceAll("\\s+", "");
                s2ChunkValue = s2ChunkValue.replaceAll("\\s+", "");
                s1ChunkValue = StringUtils.lowerCase(s1ChunkValue);
                s2ChunkValue = StringUtils.lowerCase(s2ChunkValue);
    
                try
                {
                    // compare double values - ints get converted to doubles. Eg. 100 = 100.0
                    Double s1Double = Double.valueOf(s1ChunkValue);
                    Double s2Double = Double.valueOf(s2ChunkValue);
                    compareValue = s1Double.compareTo(s2Double);
                }
                catch (NumberFormatException e)
                {
                    // not a number, use string comparison.
                    compareValue = s1ChunkValue.compareTo(s2ChunkValue);
                }
                // if they are equal thus far, but one has more left, it should come after the one that doesn't.
                if (compareValue == 0)
                {
                    if (s1ChunkMatcher.hitEnd() && !s2ChunkMatcher.hitEnd())
                    {
                        compareValue = -1;
                    }
                    else if (!s1ChunkMatcher.hitEnd() && s2ChunkMatcher.hitEnd())
                    {
                        compareValue = 1;
                    }
                }
            }
            return compareValue;
        }
    }
    

    AlphaNumericObjectComparator Source:

    /**
     * Compares Objects by human values instead of traditional machine values.
     * 
     * @modified Tristan Everitt
     */
    public class AlphaNumericObjectComparator<T> implements Comparator<T>
    {
    
        private AlphaNumericStringComparator stringComparator;
    
        public AlphaNumericObjectComparator()
        {
            this(Locale.getDefault());
        }
    
        public AlphaNumericObjectComparator(Locale locale)
        {
            this.stringComparator = new AlphaNumericStringComparator(locale);
        }
    
        @Override
        public int compare(T t1, T t2)
        {
            return compareStrings(t1.toString(), t2.toString());
        }
    
        protected int compareStrings(String s1, String s2)
        {
            return stringComparator.compare(s1, s2);
        }
    }
    

    AlphaNumericStringComparatorTester Source:

    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertNotEquals;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    import java.util.Random;
    
    import org.junit.Test;
    
    /**
     *
     * @author Tristan Everitt
     */
    public class AlphaNumericStringComparatorTester
    {
        @Test
        public void testHumanNaturalSort1()
        {
            List<String> randomList = Arrays.asList("z1.doc", "z10.doc", "z100.doc", "z101.doc", "z102.doc", "z11.doc", "z12.doc", "z13.doc", "z14.doc", "z15.doc", "z16.doc", "z17.doc", "z18.doc",
                    "z19.doc", "z2.doc", "z20.doc", "z3.doc", "z4.doc", "z5.doc", "z6.doc", "z7.doc", "z8.doc", "z9.doc", "z1.2.doc", "z1.3.doc");
            Collections.shuffle(randomList, new Random());
    
            List<String> expected = Arrays.asList("z1.doc", "z1.2.doc", "z1.3.doc", "z2.doc", "z3.doc", "z4.doc", "z5.doc", "z6.doc", "z7.doc", "z8.doc", "z9.doc", "z10.doc", "z11.doc", "z12.doc",
                    "z13.doc", "z14.doc", "z15.doc", "z16.doc", "z17.doc", "z18.doc", "z19.doc", "z20.doc", "z100.doc", "z101.doc", "z102.doc");
    
            assertNotEquals(expected, randomList);
            Collections.sort(randomList, new AlphaNumericStringComparator());
            assertEquals(expected, randomList);
        }
    
        @Test
        public void testHumanNaturalSort2()
        {
            List<String> randomList = Arrays.asList("z1.doc", "z10.doc", "z100.doc", "z101.doc", "z102.doc", "z11.doc", "z12.doc", "z13.doc", "z14.doc", "z15.doc", "z16.doc", "z17.doc", "z18.doc",
                    "z19.doc", "z2.doc", "z20.doc", "z3.doc", "z4.doc", "z5.doc", "z6.doc", "z7.doc", "z8.doc", "z9.doc", "z1.2.doc", "z1.3.doc");
            Collections.shuffle(randomList, new Random());
    
            List<String> expected = Arrays.asList("z1.doc", "z1.2.doc", "z1.3.doc", "z2.doc", "z3.doc", "z4.doc", "z5.doc", "z6.doc", "z7.doc", "z8.doc", "z9.doc", "z10.doc", "z11.doc", "z12.doc",
                    "z13.doc", "z14.doc", "z15.doc", "z16.doc", "z17.doc", "z18.doc", "z19.doc", "z20.doc", "z100.doc", "z101.doc", "z102.doc");
    
            assertNotEquals(expected, randomList);
            Collections.sort(randomList, new AlphaNumericStringComparator());
            assertEquals(expected, randomList);
        }
    
        @Test
        public void testHumanNaturalSort3()
        {
            List<String> randomList = Arrays.asList("yr1", "yr10", "yr11", "yr12", "yr13", "yr2", "yr 3", "yr 3.4", "yr 4", "yr5", "yr6", "yr7", "yr8", "yr 9");
            Collections.shuffle(randomList, new Random());
    
            List<String> expected = Arrays.asList("yr1", "yr2", "yr 3", "yr 3.4", "yr 4", "yr5", "yr6", "yr7", "yr8", "yr 9", "yr10", "yr11", "yr12", "yr13");
    
            assertNotEquals(expected, randomList);
            Collections.sort(randomList, new AlphaNumericStringComparator());
            assertEquals(expected, randomList);
        }
    
        @Test
        public void testHumanNaturalSort4()
        {
            List<String> randomList = Arrays.asList("1-2", "1-02", "1-20", "10-20", "fred", "jane", "pic01", "pic2", "pic02", "pic02a", "pic3", "pic4", "pic 4 else", "pic 5", "pic05", "pic 5",
                    "pic 5 something", "pic 6", "pic   7", "pic100", "pic100a", "pic120", "pic121", "pic02000", "tom", "x2-g8", "x2-y7", "x2-y08", "x8-y8");
            Collections.shuffle(randomList, new Random());
    
            List<String> expected = Arrays.asList("1-2", "1-02", "1-20", "10-20", "fred", "jane", "pic01", "pic02", "pic2", "pic02a", "pic3", "pic4", "pic 4 else", "pic 5", "pic05", "pic 5",
                    "pic 5 something", "pic 6", "pic   7", "pic100", "pic100a", "pic120", "pic121", "pic02000", "tom", "x2-g8", "x2-y7", "x2-y08", "x8-y8");
    
            assertNotEquals(expected, randomList);
            Collections.sort(randomList, new AlphaNumericStringComparator());
            assertEquals(expected, randomList);
        }
    
    }