使用 Java 8 自定义排序
Custom Sort using Java 8
考虑下面的 dto class -
public class RoleDto {
private String name;
RoleDto(String name) {
this.name = name;
}
}
它的实例将包含类似 -
的名称
Business Practitioner - Expert
Developer - Expert
Developer - Master
Business Practitioner - Master
Business Practitioner
Developer
Developer - Professional
Business Practitioner - Professional
我想对实例进行排序,顺序为 -
Business Practitioner - Expert
Business Practitioner - Master
Business Practitioner - Professional
Business Practitioner
Developer - Expert
Developer - Master
Developer - Professional
Developer
我用过下面的代码-
ArrayList<RoleDto> roles = new ArrayList<>();
roles.add(new RoleDto("Business Practitioner - Expert"));
roles.add(new RoleDto("Developer - Expert"));
roles.add(new RoleDto("Developer - Master"));
roles.add(new RoleDto("Business Practitioner - Master"));
roles.add(new RoleDto("Business Practitioner"));
roles.add(new RoleDto("Developer"));
roles.add(new RoleDto("Developer - Professional"));
roles.add(new RoleDto("Business Practitioner - Professional"));
List<RoleDto> sorted = roles.stream().sorted(comparing(r -> r.getName(),
comparing((String s) -> !s.contains("-")))).collect(toList());
for(RoleDto r : sorted)
System.out.println(r.getName());
但是输出是
Business Practitioner - Expert
Developer - Expert
Developer - Master
Business Practitioner - Master
Developer - Professional
Business Practitioner - Professional
Business Practitioner
Developer
有人可以帮我实现预期的结果吗
我建议将您的 RoleD 更改为具有两个属性:String role 和 String level
现在排序列表的代码如下所示:
Comparator<RoleDto> compareByRoleAndLevel = Comparator
.comparing(RoleDto::getRole)
.thenComparing(RoleDto::getLevel);
List<RoleDto> sortedRoles = roles.stream()
.sorted(compareByRoleAndLevel)
.collect(Collectors.toList());
你实际上并没有比较名字,比较只是检查名字是否包含“-”,这就是为什么你在所有不做的(随机的)之前得到所有做的(随机顺序)命令)。如果你可以更改 Dto,请参阅@Orr 的答案,否则你可以在适当的位置使用名称 split/compare,如下所示:
final List<RoleDto> sorted =
roles.stream()
.sorted(
comparing(
RoleDto::getName,
comparing((String s) -> s.split(" - ")[0])
.reversed()
.thenComparing(s -> s.split(" - ").length)
.reversed()
.thenComparing(s -> s.split(" - ")[1])))
.collect(toList());
反转可能看起来有点不直观,但实际上每次反转都会反转之前的所有比较,所以第一次比较会反转两次。要跳过它,您可以改写:
final List<RoleDto> sorted =
roles.stream()
.sorted(
comparing(
RoleDto::getName,
comparing((String s) -> s.split(" - ")[0])
.thenComparing(s -> -s.split(" - ").length)
.thenComparing(s -> s.split(" - ")[1])))
.collect(toList());
(注意比较中间的减号)
这里似乎有两个维度的数据在起作用。
- 业务职能
“业务从业者”&“程序员”。
- 熟练程度
“专家”、“大师”、“专业”,隐含“标准”、“基础”、“新手”或一些这样的(显示为无文字)。
试图将此类值表示为文本很笨拙,并且会忽略 Java 中为此目的内置的功能。
如果在 compile-time 处已知所有值,则为每个维度创建一个枚举。枚举是 class 定义的方式,以便在加载 class 时自动实例化和命名多个对象。
public enum Function { BUSINESS_PRACTITIONER, PROGRAMMER ; }
public enum Proficiency { EXPERT, MASTER, PROFESSIONAL, NOVICE; }
将它们合并为一个 class。 class 的主要目的是透明且不可变地传输数据。所以我们可以简单地定义为record.
public record Role( Function function , Proficiency proficiency ) { }
我们希望 Role
的实例分两级排序,首先按 Function
,然后按 Proficiency
。所以我们需要在我们的记录Role
.
上实现Comparable
接口
public record Role( Function function , Proficiency proficiency ) implements Comparable<Role> { … }
每个对象的排序顺序是声明命名对象的顺序。所以我们可以写出必要的compareTo
method using a Comparator
object. We define that Comparator
object using convenience methods comparing
and thenComparing
, passing each a method reference。我们使用 static
将此对象标记为单例,因为频繁 re-use.
private static Comparator < Role > comparator =
Comparator
.comparing( Role :: function )
.thenComparing( Role :: proficiency );
我们在 compareTo
方法中使用该比较器。
@Override public int compareTo ( Role other ) { return Role.comparator.compare( this , other ); }
我们像这样实例化一个角色对象:
Role progPro = new Role( Function.PROGRAMMER , Proficiency.PROFESSIONAL );
如果需要的话,我们可以把所有可能的角色做一个集合。
NavigableSet < Role > allPossibleRoles = new TreeSet();
for ( Function function : Function.values() )
{
for ( Proficiency proficiency : Proficiency.values() )
{
allPossibleRoles.add( new Role( function , proficiency ) );
}
}
System.out.println( "allPossibleRoles = " + allPossibleRoles );
我们可以看到这些是按照问题规定的顺序列出的。
allPossibleRoles = [Role[function=BUSINESS_PRACTITIONER, proficiency=EXPERT], Role[function=BUSINESS_PRACTITIONER, proficiency=MASTER], Role[function=BUSINESS_PRACTITIONER, proficiency=PROFESSIONAL], Role[function=BUSINESS_PRACTITIONER, proficiency=NOVICE], Role[function=PROGRAMMER, proficiency=EXPERT], Role[function=PROGRAMMER, proficiency=MASTER], Role[function=PROGRAMMER, proficiency=PROFESSIONAL], Role[function=PROGRAMMER, proficiency=NOVICE]]
最后,我们需要发出在问题中看到的文本。
修改枚举 classes 以获取显示名称的参数。
public enum Function
{
BUSINESS_PRACTITIONER( "Business Practitioner" ), PROGRAMMER( "Programmer" );
private final String displayName;
// Constructor
Function ( final String displayName ) { this.displayName = displayName; }
public String getDisplayName ( ) { return displayName; }
}
还有另一个枚举。
public enum Proficiency
{
EXPERT( "Expert" ), MASTER( "Master" ), PROFESSIONAL( "Professional" ), NOVICE( "" );
private final String displayName;
// Constructor
Proficiency ( String displayName ) { this.displayName = displayName; }
public String getDisplayName ( ) { return displayName; }
}
修改 Role
记录以使用 getDisplayName
方法生成所需格式的文本。
import java.util.Comparator;
public record Role( Function function , Proficiency proficiency ) implements Comparable < Role >
{
@Override
public int compareTo ( Role other ) { return Role.comparator.compare( this , other ); }
private static Comparator < Role > comparator =
Comparator
.comparing( Role :: function )
.thenComparing( Role :: proficiency );
public String getDisplayName ( )
{
String x = this.function.getDisplayName();
String y = this.proficiency.getDisplayName().isBlank() ? "" : " - ";
String z = this.proficiency.getDisplayName();
return x + y + z;
}
}
用法示例。
NavigableSet < Role > allPossibleRoles = new TreeSet(); // NavigableSet/TreeSet keeps elements sorted.
for ( Function function : Function.values() )
{
for ( Proficiency proficiency : Proficiency.values() )
{
allPossibleRoles.add( new Role( function , proficiency ) );
}
}
// Dump to console.
for ( Role role : allPossibleRoles )
{
System.out.println( "role.getDisplayName() = " + role.getDisplayName() );
}
role.getDisplayName() = Business Practitioner - Expert
role.getDisplayName() = Business Practitioner - Master
role.getDisplayName() = Business Practitioner - Professional
role.getDisplayName() = Business Practitioner
role.getDisplayName() = Programmer - Expert
role.getDisplayName() = Programmer - Master
role.getDisplayName() = Programmer - Professional
role.getDisplayName() = Programmer
考虑下面的 dto class -
public class RoleDto {
private String name;
RoleDto(String name) {
this.name = name;
}
}
它的实例将包含类似 -
的名称Business Practitioner - Expert
Developer - Expert
Developer - Master
Business Practitioner - Master
Business Practitioner
Developer
Developer - Professional
Business Practitioner - Professional
我想对实例进行排序,顺序为 -
Business Practitioner - Expert
Business Practitioner - Master
Business Practitioner - Professional
Business Practitioner
Developer - Expert
Developer - Master
Developer - Professional
Developer
我用过下面的代码-
ArrayList<RoleDto> roles = new ArrayList<>();
roles.add(new RoleDto("Business Practitioner - Expert"));
roles.add(new RoleDto("Developer - Expert"));
roles.add(new RoleDto("Developer - Master"));
roles.add(new RoleDto("Business Practitioner - Master"));
roles.add(new RoleDto("Business Practitioner"));
roles.add(new RoleDto("Developer"));
roles.add(new RoleDto("Developer - Professional"));
roles.add(new RoleDto("Business Practitioner - Professional"));
List<RoleDto> sorted = roles.stream().sorted(comparing(r -> r.getName(),
comparing((String s) -> !s.contains("-")))).collect(toList());
for(RoleDto r : sorted)
System.out.println(r.getName());
但是输出是
Business Practitioner - Expert
Developer - Expert
Developer - Master
Business Practitioner - Master
Developer - Professional
Business Practitioner - Professional
Business Practitioner
Developer
有人可以帮我实现预期的结果吗
我建议将您的 RoleD 更改为具有两个属性:String role 和 String level
现在排序列表的代码如下所示:
Comparator<RoleDto> compareByRoleAndLevel = Comparator
.comparing(RoleDto::getRole)
.thenComparing(RoleDto::getLevel);
List<RoleDto> sortedRoles = roles.stream()
.sorted(compareByRoleAndLevel)
.collect(Collectors.toList());
你实际上并没有比较名字,比较只是检查名字是否包含“-”,这就是为什么你在所有不做的(随机的)之前得到所有做的(随机顺序)命令)。如果你可以更改 Dto,请参阅@Orr 的答案,否则你可以在适当的位置使用名称 split/compare,如下所示:
final List<RoleDto> sorted =
roles.stream()
.sorted(
comparing(
RoleDto::getName,
comparing((String s) -> s.split(" - ")[0])
.reversed()
.thenComparing(s -> s.split(" - ").length)
.reversed()
.thenComparing(s -> s.split(" - ")[1])))
.collect(toList());
反转可能看起来有点不直观,但实际上每次反转都会反转之前的所有比较,所以第一次比较会反转两次。要跳过它,您可以改写:
final List<RoleDto> sorted =
roles.stream()
.sorted(
comparing(
RoleDto::getName,
comparing((String s) -> s.split(" - ")[0])
.thenComparing(s -> -s.split(" - ").length)
.thenComparing(s -> s.split(" - ")[1])))
.collect(toList());
(注意比较中间的减号)
这里似乎有两个维度的数据在起作用。
- 业务职能
“业务从业者”&“程序员”。 - 熟练程度
“专家”、“大师”、“专业”,隐含“标准”、“基础”、“新手”或一些这样的(显示为无文字)。
试图将此类值表示为文本很笨拙,并且会忽略 Java 中为此目的内置的功能。
如果在 compile-time 处已知所有值,则为每个维度创建一个枚举。枚举是 class 定义的方式,以便在加载 class 时自动实例化和命名多个对象。
public enum Function { BUSINESS_PRACTITIONER, PROGRAMMER ; }
public enum Proficiency { EXPERT, MASTER, PROFESSIONAL, NOVICE; }
将它们合并为一个 class。 class 的主要目的是透明且不可变地传输数据。所以我们可以简单地定义为record.
public record Role( Function function , Proficiency proficiency ) { }
我们希望 Role
的实例分两级排序,首先按 Function
,然后按 Proficiency
。所以我们需要在我们的记录Role
.
Comparable
接口
public record Role( Function function , Proficiency proficiency ) implements Comparable<Role> { … }
每个对象的排序顺序是声明命名对象的顺序。所以我们可以写出必要的compareTo
method using a Comparator
object. We define that Comparator
object using convenience methods comparing
and thenComparing
, passing each a method reference。我们使用 static
将此对象标记为单例,因为频繁 re-use.
private static Comparator < Role > comparator =
Comparator
.comparing( Role :: function )
.thenComparing( Role :: proficiency );
我们在 compareTo
方法中使用该比较器。
@Override public int compareTo ( Role other ) { return Role.comparator.compare( this , other ); }
我们像这样实例化一个角色对象:
Role progPro = new Role( Function.PROGRAMMER , Proficiency.PROFESSIONAL );
如果需要的话,我们可以把所有可能的角色做一个集合。
NavigableSet < Role > allPossibleRoles = new TreeSet();
for ( Function function : Function.values() )
{
for ( Proficiency proficiency : Proficiency.values() )
{
allPossibleRoles.add( new Role( function , proficiency ) );
}
}
System.out.println( "allPossibleRoles = " + allPossibleRoles );
我们可以看到这些是按照问题规定的顺序列出的。
allPossibleRoles = [Role[function=BUSINESS_PRACTITIONER, proficiency=EXPERT], Role[function=BUSINESS_PRACTITIONER, proficiency=MASTER], Role[function=BUSINESS_PRACTITIONER, proficiency=PROFESSIONAL], Role[function=BUSINESS_PRACTITIONER, proficiency=NOVICE], Role[function=PROGRAMMER, proficiency=EXPERT], Role[function=PROGRAMMER, proficiency=MASTER], Role[function=PROGRAMMER, proficiency=PROFESSIONAL], Role[function=PROGRAMMER, proficiency=NOVICE]]
最后,我们需要发出在问题中看到的文本。
修改枚举 classes 以获取显示名称的参数。
public enum Function
{
BUSINESS_PRACTITIONER( "Business Practitioner" ), PROGRAMMER( "Programmer" );
private final String displayName;
// Constructor
Function ( final String displayName ) { this.displayName = displayName; }
public String getDisplayName ( ) { return displayName; }
}
还有另一个枚举。
public enum Proficiency
{
EXPERT( "Expert" ), MASTER( "Master" ), PROFESSIONAL( "Professional" ), NOVICE( "" );
private final String displayName;
// Constructor
Proficiency ( String displayName ) { this.displayName = displayName; }
public String getDisplayName ( ) { return displayName; }
}
修改 Role
记录以使用 getDisplayName
方法生成所需格式的文本。
import java.util.Comparator;
public record Role( Function function , Proficiency proficiency ) implements Comparable < Role >
{
@Override
public int compareTo ( Role other ) { return Role.comparator.compare( this , other ); }
private static Comparator < Role > comparator =
Comparator
.comparing( Role :: function )
.thenComparing( Role :: proficiency );
public String getDisplayName ( )
{
String x = this.function.getDisplayName();
String y = this.proficiency.getDisplayName().isBlank() ? "" : " - ";
String z = this.proficiency.getDisplayName();
return x + y + z;
}
}
用法示例。
NavigableSet < Role > allPossibleRoles = new TreeSet(); // NavigableSet/TreeSet keeps elements sorted.
for ( Function function : Function.values() )
{
for ( Proficiency proficiency : Proficiency.values() )
{
allPossibleRoles.add( new Role( function , proficiency ) );
}
}
// Dump to console.
for ( Role role : allPossibleRoles )
{
System.out.println( "role.getDisplayName() = " + role.getDisplayName() );
}
role.getDisplayName() = Business Practitioner - Expert
role.getDisplayName() = Business Practitioner - Master
role.getDisplayName() = Business Practitioner - Professional
role.getDisplayName() = Business Practitioner
role.getDisplayName() = Programmer - Expert
role.getDisplayName() = Programmer - Master
role.getDisplayName() = Programmer - Professional
role.getDisplayName() = Programmer