使用 Rinetime 提供的属性对列表进行动态排序
Sorting a List dynamically using attributes provided at Rinetime
有一个 Map,其键类型为 String
,值由对象列表表示,如下所示:
Map<String, List<ScoreAverage>> averagesMap
ScoreAverage
记录:
public record ScoreAverage(
@JsonProperty("average") double average,
@JsonProperty("name") String name
) {}
map保存数据如下:
{
"averagesMap":{
"A":[
{
"average":4.0,
"name":"Accounting"
},
{
"average":4.0,
"name":"company-wide"
},
{
"average":4.0,
"name":"Engineering"
}
],
"B":[
{
"average":3.0,
"name":"Engineering"
},
{
"average":3.0,
"name":"company-wide"
},
{
"average":3.0,
"name":"Accounting"
}
],
"C":[
{
"average":2.0,
"name":"company-wide"
},
{
"average":2.0,
"name":"Engineering"
},
{
"average":2.0,
"en":"Accounting"
}
],
"specialAverages":[
{
"average":2.5,
"name":"Engineering"
},
{
"average":2.5,
"name":"company-wide"
},
{
"average":2.5,
"name":"Accounting"
}
]
}
}
我想要实现的是使用 name
属性按照运行时指定的顺序对地图中的每个对象列表进行动态排序,例如:
1st item of list -> company-wide
2nd item of list -> Engineering
3rd item of list -> Accounting
最简单的方法是什么?
为此,首先您需要建立所需的顺序。这样它将被封装在一个变量中,并在运行时作为参数传递给负责排序的方法。这样,排序顺序将是动态的,取决于提供的参数。
在下面的代码中,List
用于该目的。排序是根据每个name
在sortingRule
列表中占据的索引。
下一步是基于它创建Comparator
。当 name
没有出现在 sortingRule
中时,我使用条件 sortingRule.contains(score.name())
作为预防措施,以防止拼写错误等情况。这样,所有此类对象都将放在排序列表的末尾。
Comparator<ScoreAverage> dynamicComparator =
Comparator.comparingInt(score -> sortingRule.contains(score.name()) ?
sortingRule.indexOf(score.name()) :
sortingRule.size());
如果我们放弃条件比较器归结为
Comparator.comparingInt(score -> sortingRule.indexOf(score.name()));
这样,所有未识别的对象(如果有的话)将被分组在排序列表的开头。
最后,我们需要使用此比较器对映射中的每个值进行排序。
迭代实现(注意:每个列表的防御性副本都是为了完整地保留源代码)。
public static Map<String, List<ScoreAverage>> sortByRule(Map<String, List<ScoreAverage>> averagesMap,
List<String> sortingRule) {
Comparator<ScoreAverage> dynamicComparator =
Comparator.comparingInt(score -> sortingRule.contains(score.name()) ?
sortingRule.indexOf(score.name()) :
sortingRule.size());
Map<String, List<ScoreAverage>> result = new HashMap<>();
for (Map.Entry<String, List<ScoreAverage>> entry: averagesMap.entrySet()) {
List<ScoreAverage> copy = new ArrayList<>(entry.getValue());
copy.sort(dynamicComparator);
result.put(entry.getKey(), copy);
}
return result;
}
基于流的实现(注意:源映射中的列表不会被修改,每个条目将被替换为一个新条目 ).
public static Map<String, List<ScoreAverage>> sortByRule(Map<String, List<ScoreAverage>> averagesMap,
List<String> sortingRule) {
Comparator<ScoreAverage> dynamicComparator =
Comparator.comparingInt(score -> sortingRule.contains(score.name()) ?
sortingRule.indexOf(score.name()) :
sortingRule.size());
return averagesMap.entrySet().stream()
.map(entry -> Map.entry(entry.getKey(),
entry.getValue().stream()
.sorted(dynamicComparator)
.collect(Collectors.toList())))
.collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));
}
main()
public static void main(String[] args) {
Map<String, List<ScoreAverage>> averagesMap =
Map.of("A", List.of(new ScoreAverage(4.0, "Accounting"),
new ScoreAverage(4.0, "company-wide"),
new ScoreAverage(4.0, "Engineering")),
"B", List.of(new ScoreAverage(3.0, "Engineering"),
new ScoreAverage(3.0, "company-wide"),
new ScoreAverage(3.0, "Accounting")),
"C", List.of(new ScoreAverage(2.0, "company-wide"),
new ScoreAverage(2.0, "Engineering"),
new ScoreAverage(2.0, "Accounting")),
"specialAverages", List.of(new ScoreAverage(2.5, "Engineering"),
new ScoreAverage(2.5, "company-wide"),
new ScoreAverage(2.5, "Accounting")));
List<String> sortingRule = List.of("company-wide", "Engineering", "Accounting");
sortByRule(averagesMap, sortingRule).forEach((k, v) -> System.out.println(k + " : " + v));
}
输出
A : [ScoreAverage[average=4.0, name=company-wide], ScoreAverage[average=4.0, name=Engineering], ScoreAverage[average=4.0, name=Accounting]]
B : [ScoreAverage[average=3.0, name=company-wide], ScoreAverage[average=3.0, name=Engineering], ScoreAverage[average=3.0, name=Accounting]]
C : [ScoreAverage[average=2.0, name=company-wide], ScoreAverage[average=2.0, name=Engineering], ScoreAverage[average=2.0, name=Accounting]]
specialAverages : [ScoreAverage[average=2.5, name=company-wide], ScoreAverage[average=2.5, name=Engineering], ScoreAverage[average=2.5, name=Accounting]]
更新
也可以结合封装在列表中的 排序规则 和负责对 中不存在的元素进行排序的 Comparator
排序规则。 排序规则和比较器都将在运行时动态提供。
为此,必须更改方法签名(需要添加第三个参数):
public static Map<String, List<ScoreAverage>> sortByRule(Map<String, List<ScoreAverage>> averagesMap,
List<String> sortingRule,
Comparator<ScoreAverage> downstreamComparator)
比较器看起来像这样:
Comparator<ScoreAverage> dynamicComparator =
Comparator.<ScoreAverage>comparingInt(score -> sortingRule.contains(score.name()) ?
sortingRule.indexOf(score.name()) :
sortingRule.size())
.thenComparing(downstreamComparator);
它将所有名称为包含的对象分组在结果列表的开头sortingRule
中,其余部分将按照[=排序29=].
main 中的方法调用如下所示:
sortByRule(averagesMap, sortingRule, Comparator.comparing(ScoreAverage::name))
.forEach((k, v) -> System.out.println(k + " : " + v));
如果您应用这些更改并提供仅包含一个字符串 "company-wide"
的 sortingRule
,您将得到 output:
A : [ScoreAverage[average=4.0, name=company-wide], ScoreAverage[average=4.0, name=Accounting], ScoreAverage[average=4.0, name=Engineering]]
B : [ScoreAverage[average=3.0, name=company-wide], ScoreAverage[average=3.0, name=Accounting], ScoreAverage[average=3.0, name=Engineering]]
C : [ScoreAverage[average=2.0, name=company-wide], ScoreAverage[average=2.0, name=Accounting], ScoreAverage[average=2.0, name=Engineering]]
specialAverages : [ScoreAverage[average=2.5, name=company-wide], ScoreAverage[average=2.5, name=Accounting], ScoreAverage[average=2.5, name=Engineering]]
有一个 Map,其键类型为 String
,值由对象列表表示,如下所示:
Map<String, List<ScoreAverage>> averagesMap
ScoreAverage
记录:
public record ScoreAverage(
@JsonProperty("average") double average,
@JsonProperty("name") String name
) {}
map保存数据如下:
{
"averagesMap":{
"A":[
{
"average":4.0,
"name":"Accounting"
},
{
"average":4.0,
"name":"company-wide"
},
{
"average":4.0,
"name":"Engineering"
}
],
"B":[
{
"average":3.0,
"name":"Engineering"
},
{
"average":3.0,
"name":"company-wide"
},
{
"average":3.0,
"name":"Accounting"
}
],
"C":[
{
"average":2.0,
"name":"company-wide"
},
{
"average":2.0,
"name":"Engineering"
},
{
"average":2.0,
"en":"Accounting"
}
],
"specialAverages":[
{
"average":2.5,
"name":"Engineering"
},
{
"average":2.5,
"name":"company-wide"
},
{
"average":2.5,
"name":"Accounting"
}
]
}
}
我想要实现的是使用 name
属性按照运行时指定的顺序对地图中的每个对象列表进行动态排序,例如:
1st item of list -> company-wide
2nd item of list -> Engineering
3rd item of list -> Accounting
最简单的方法是什么?
为此,首先您需要建立所需的顺序。这样它将被封装在一个变量中,并在运行时作为参数传递给负责排序的方法。这样,排序顺序将是动态的,取决于提供的参数。
在下面的代码中,List
用于该目的。排序是根据每个name
在sortingRule
列表中占据的索引。
下一步是基于它创建Comparator
。当 name
没有出现在 sortingRule
中时,我使用条件 sortingRule.contains(score.name())
作为预防措施,以防止拼写错误等情况。这样,所有此类对象都将放在排序列表的末尾。
Comparator<ScoreAverage> dynamicComparator =
Comparator.comparingInt(score -> sortingRule.contains(score.name()) ?
sortingRule.indexOf(score.name()) :
sortingRule.size());
如果我们放弃条件比较器归结为
Comparator.comparingInt(score -> sortingRule.indexOf(score.name()));
这样,所有未识别的对象(如果有的话)将被分组在排序列表的开头。
最后,我们需要使用此比较器对映射中的每个值进行排序。
迭代实现(注意:每个列表的防御性副本都是为了完整地保留源代码)。
public static Map<String, List<ScoreAverage>> sortByRule(Map<String, List<ScoreAverage>> averagesMap,
List<String> sortingRule) {
Comparator<ScoreAverage> dynamicComparator =
Comparator.comparingInt(score -> sortingRule.contains(score.name()) ?
sortingRule.indexOf(score.name()) :
sortingRule.size());
Map<String, List<ScoreAverage>> result = new HashMap<>();
for (Map.Entry<String, List<ScoreAverage>> entry: averagesMap.entrySet()) {
List<ScoreAverage> copy = new ArrayList<>(entry.getValue());
copy.sort(dynamicComparator);
result.put(entry.getKey(), copy);
}
return result;
}
基于流的实现(注意:源映射中的列表不会被修改,每个条目将被替换为一个新条目 ).
public static Map<String, List<ScoreAverage>> sortByRule(Map<String, List<ScoreAverage>> averagesMap,
List<String> sortingRule) {
Comparator<ScoreAverage> dynamicComparator =
Comparator.comparingInt(score -> sortingRule.contains(score.name()) ?
sortingRule.indexOf(score.name()) :
sortingRule.size());
return averagesMap.entrySet().stream()
.map(entry -> Map.entry(entry.getKey(),
entry.getValue().stream()
.sorted(dynamicComparator)
.collect(Collectors.toList())))
.collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));
}
main()
public static void main(String[] args) {
Map<String, List<ScoreAverage>> averagesMap =
Map.of("A", List.of(new ScoreAverage(4.0, "Accounting"),
new ScoreAverage(4.0, "company-wide"),
new ScoreAverage(4.0, "Engineering")),
"B", List.of(new ScoreAverage(3.0, "Engineering"),
new ScoreAverage(3.0, "company-wide"),
new ScoreAverage(3.0, "Accounting")),
"C", List.of(new ScoreAverage(2.0, "company-wide"),
new ScoreAverage(2.0, "Engineering"),
new ScoreAverage(2.0, "Accounting")),
"specialAverages", List.of(new ScoreAverage(2.5, "Engineering"),
new ScoreAverage(2.5, "company-wide"),
new ScoreAverage(2.5, "Accounting")));
List<String> sortingRule = List.of("company-wide", "Engineering", "Accounting");
sortByRule(averagesMap, sortingRule).forEach((k, v) -> System.out.println(k + " : " + v));
}
输出
A : [ScoreAverage[average=4.0, name=company-wide], ScoreAverage[average=4.0, name=Engineering], ScoreAverage[average=4.0, name=Accounting]]
B : [ScoreAverage[average=3.0, name=company-wide], ScoreAverage[average=3.0, name=Engineering], ScoreAverage[average=3.0, name=Accounting]]
C : [ScoreAverage[average=2.0, name=company-wide], ScoreAverage[average=2.0, name=Engineering], ScoreAverage[average=2.0, name=Accounting]]
specialAverages : [ScoreAverage[average=2.5, name=company-wide], ScoreAverage[average=2.5, name=Engineering], ScoreAverage[average=2.5, name=Accounting]]
更新
也可以结合封装在列表中的 排序规则 和负责对 中不存在的元素进行排序的 Comparator
排序规则。 排序规则和比较器都将在运行时动态提供。
为此,必须更改方法签名(需要添加第三个参数):
public static Map<String, List<ScoreAverage>> sortByRule(Map<String, List<ScoreAverage>> averagesMap,
List<String> sortingRule,
Comparator<ScoreAverage> downstreamComparator)
比较器看起来像这样:
Comparator<ScoreAverage> dynamicComparator =
Comparator.<ScoreAverage>comparingInt(score -> sortingRule.contains(score.name()) ?
sortingRule.indexOf(score.name()) :
sortingRule.size())
.thenComparing(downstreamComparator);
它将所有名称为包含的对象分组在结果列表的开头sortingRule
中,其余部分将按照[=排序29=].
main 中的方法调用如下所示:
sortByRule(averagesMap, sortingRule, Comparator.comparing(ScoreAverage::name))
.forEach((k, v) -> System.out.println(k + " : " + v));
如果您应用这些更改并提供仅包含一个字符串 "company-wide"
的 sortingRule
,您将得到 output:
A : [ScoreAverage[average=4.0, name=company-wide], ScoreAverage[average=4.0, name=Accounting], ScoreAverage[average=4.0, name=Engineering]]
B : [ScoreAverage[average=3.0, name=company-wide], ScoreAverage[average=3.0, name=Accounting], ScoreAverage[average=3.0, name=Engineering]]
C : [ScoreAverage[average=2.0, name=company-wide], ScoreAverage[average=2.0, name=Accounting], ScoreAverage[average=2.0, name=Engineering]]
specialAverages : [ScoreAverage[average=2.5, name=company-wide], ScoreAverage[average=2.5, name=Accounting], ScoreAverage[average=2.5, name=Engineering]]