Basically I have a List
, and I want to sort it by the values of certain key in the map.
The problem is that I do not know
You can generalize comparing numbers by comparing their double values, because they are the largest. If two objects cannot be cast to numbers and the double value cannot be parsed from these objects, then compare their string values:
List<Map<String, Object>> data = Arrays.asList(
Map.of("Field1", 21.2d), // Double
Map.of("Field1", "qqq"), // String
Map.of("Field1", "22.5"), // String
Map.of("Field1", 2), // Integer
Map.of("Field1", 3L), // Long
Map.of("Field1", 23.1f)); // Float
data.sort(Comparator.comparingDouble((Map<String, Object> map) -> {
Object object = map.get("Field1");
if (object instanceof Number) {
return ((Number) object).doubleValue();
} else {
try {
return Double.parseDouble(String.valueOf(object));
} catch (NumberFormatException e) {
return Double.NaN;
}
}
}).thenComparing(map -> String.valueOf(map.get("Field1"))));
data.forEach(System.out::println);
// {Field1=2}
// {Field1=3}
// {Field1=21.2}
// {Field1=22.5}
// {Field1=23.1}
// {Field1=qqq}
See also: Sort 2D List by Column Header
First you will have to define the desired order, especially the place of the string values, or are they all numbers in text form?
Then implement a more complex comparator method, working through your comparison criteria from highest to lowest priority.
Untested:
List<Map<String, Object>> list = new ArrayList<>();
Collections.sort(list, (map1, map2) -> {
Object v1 = map1.get("Field1");
Object v2 = map2.get("Field1");
// --------------------------------------------
// handle null
// --------------------------------------------
if(v1 == null && v2 == null) {
return 0;
}
if(v1 == null && v2 != null) {
return -1 /* null value before non-null value */;
}
if(v1 != null && v2 == null) {
return 1 /* non-null value after null value */;
}
// --------------------------------------------
// handle String
// --------------------------------------------
boolean v1IsString = v1 instanceof String;
boolean v2IsString = v2 instanceof String;
if(v1IsString && v2IsString) {
return ((String) v1).compareToIgnoreCase((String) v2);
}
if(v1IsString && !v2IsString) {
return -1 /* string value before non-string values */;
}
if(!v1IsString && v2IsString) {
return 1 /* non-string value after string value */;
}
// --------------------------------------------
// handle LocalDate
// --------------------------------------------
// ....
// --------------------------------------------
// handle Instant
// --------------------------------------------
// ....
// --------------------------------------------
// handle BigDecimal and BigInteger
// --------------------------------------------
// ....
// --------------------------------------------
// handle Number
// --------------------------------------------
boolean v1IsNumber = v1 instanceof Number;
boolean v2IsNumber = v2 instanceof Number;
if(v1IsNumber && v2IsNumber) {
double d1 = ((Number) v1).doubleValue();
double d2 = ((Number) v2).doubleValue();
return (int) Math.signum(d1 - d2);
}
if(v1IsNumber && !v2IsNumber) {
return -1 /* number value before non-number values */;
}
if(!v1IsNumber && v2IsNumber) {
return 1 /* non-number value after number value */;
}
// etc.
// --------------------------------------------
// finally, treat all unhandled value types as equal
// --------------------------------------------
return 0;
});