So, actually you try to compare rows by their contents, that is, each row can be represented as an array of strings, where some strings should be compared as numbers.
So, custom comparator needs to be implemented like this according to the mentioned requirements:
- numbers should be compared as numbers
- strings should be compared alphabetically
- numbers go before strings (therefore less than strings)
private static int customCompare(String[] s1, String[] s2) {
int res = 0;
for (int i = 0, n = Math.min(s1.length, s2.length); res == 0 && i < n; i++) {
Comparable e1 = convert(s1[i]);
Comparable e2 = convert(s2[i]);
if (e1 instanceof Double && e2 instanceof Double) {
res = Double.compare((Double)e1, (Double)e2);
} else if (e1 instanceof String && e2 instanceof String) {
res = e1.compareTo(e2);
} else {
res = e1 instanceof Double ? -1 : 1;
}
}
return res != 0 ? res : Integer.compare(s1.length, s2.length);
}
// utility method to check for numeric format and convert to Double if needed
private static Comparable convert(String s) {
if (null == s || !s.matches("[-+]?(\\d+|\\d*\\.\\d+)")) {
return s;
}
return Double.valueOf(s);
}
Test:
String[] data = {
"-2.2\t2\t3\t4",
"2.2\t12345q\t69\t-afq",
"2.2\t12345q\t69\t-asdf",
"-22\t1234234\tasdfasf\tasdgas",
"-22\t-3\t4",
"",
"-1.1",
" ",
"qqqq\tq1.1",
"qqqq\t0.1",
"qqqq\t-1.1",
"-22\t11\tabc"
};
Arrays.stream(data)
.map(s -> s.split("\t")) // Stream<String[]> rows
.sorted(MyClass::customCompare) // reference to customCompare method
.map(arr -> String.join("\t", arr))
.forEach(System.out::println);
Output:
-22 -3 4
-22 11 abc
-22 1234234 asdfasf asdgas
-2.2 2 3 4
-1.1
2.2 12345q 69 -afq
2.2 12345q 69 -asdf
qqqq -1.1
qqqq 0.1
qqqq q1.1