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 <[email protected]>
*
* 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);
}
}
solved Sorting TreeMap alphabetically