Java 仅基于字段名对类数组进行排序

Java 仅基于字段名对类数组进行排序,java,sorting,reflection,Java,Sorting,Reflection,我有一个应用程序,其中用户向我提供一个字段的名称,例如name或costInCents,我必须按该字段排序。我有办法保证字段名是正确的。这个应用程序导致了一个复杂问题,即我无法使我的类具有可比性并实现特定的compareTo(),因为使用compareTo()的自定义实现时,我需要知道在实现时要使用哪些字段/方法 为了实现这个目标,我尝试使用反射来匹配字段和它的访问器。这是我想做的事情 ClassProduct是一个简单的POJO类,我想对其实例进行两两比较: 公共类产品 { 最后的字符串名;

我有一个应用程序,其中用户向我提供一个字段的名称,例如
name
costInCents
,我必须按该字段排序。我有办法保证字段名是正确的。这个应用程序导致了一个复杂问题,即我无法使我的类
具有可比性
并实现特定的
compareTo()
,因为使用
compareTo()
的自定义实现时,我需要知道在实现时要使用哪些字段/方法

为了实现这个目标,我尝试使用反射来匹配字段和它的访问器。这是我想做的事情

Class
Product
是一个简单的POJO类,我想对其实例进行两两比较:

公共类产品
{
最后的字符串名;
最终整数数量;
最终长期成本激励;
公共产品(最终字符串名称、最终整数数量、最终长成本)
{
this.name=名称;
这个。数量=数量;
this.costInCents=costInCents;
}
公共字符串getName()
{
返回名称;
}
公共整数getQuantity()
{
退货数量;
}
公共长期成本激励()
{
返回成本激励;
}
}
以及我的
Main
类,该类当前不完整:

公共类主{
公共静态void main(字符串[]args){
最终产品[]productArray=
{
新产品(“Clorox湿巾”,50700L),
新产品(“桌椅”,1012000L),
新产品(“TV”,53000升),
新产品(“书架”,512000L),
新产品(“水瓶”,20700L),
};
//以下void方法应该使用类似于array.sort()或Collections.sort()的内容进行就地排序,
//但我也乐于接受涉及Stream::sorted()或类似内容的解决方案,它们返回一个排序数组。
sortByField(产品阵列,“成本激励”);
sortByField(productArray,“名称”);
}
私有无效排序字段(最终产品[]productArray,最终字符串排序字段名称)
{
final Field sorterField=getSorterField(sorterFieldName,LiteProduct.class);//以某种方式获取字段
最后一个方法sorterraccessor=getsorterraccessor(sorterField,LiteProduct.class);//给定字段,这很简单
Arrays.sort((产品p1,产品p2)->((可比)sorterraccessor.invoke(p1)).compareTo(sorterraccessor.invoke(p2))>0;//捕获?而不是对象
}
}
不幸的是,
Arrays.sort()
行导致编译时错误,消息是
Capture of?而不是对象
。我已尝试将第二个参数转换为
Comparable
Comparable,您可以尝试以下方法:

public class Test{
  public static void main(String arg[]){
  final Product[] productArray =
            {
                new Product("Clorox wipes", 50, 700L),
                new Product("Desk chair", 10, 12000L),
                new Product("TV", 5, 30000L),
                new Product("Bookcase", 5, 12000L),
                new Product("Water bottle", 20, 700L),
            };
            
      Arrays.sort(productArray,(p1, p2) -> p1.getName().compareTo(p2.getName()));
 
     for(Product p: productArray){
        System.out.println(p.getName());
     }
    }
   }

在本例中,比较器是一个函数接口,所以我使用了lambad表达式。但您也可以这样做

您可以为每个字段编写一个比较器,并通过映射按名称使用它:

public class Product
{
    private final static Map<String,Comparator<Product>> COMPARATORS;

    static {
        COMPARATORS = new HashMap<>();
        COMPARATORS.put("name", new NameComparator());
        COMPARATORS.put("costInCents", new CostInCentsComparator());
    }

    final String name;
    final Integer quantity;
    final Long costInCents;

    public Product(final String name, final Integer quantity, final Long costInCents)
    {
        this.name = name;
        this.quantity = quantity;
        this.costInCents = costInCents;
    }

    public String getName()
    {
        return name;
    }
    public Integer getQuantity()
    {
        return quantity;
    }
    public Long getCostInCents()
    {
        return costInCents;
    }

    static class NameComparator implements Comparator<Product> {

        @Override
        public int compare(Product o1, Product o2) {
            return o1.getName().compareTo(o2.getName());
        }

    }

    static class CostInCentsComparator implements Comparator<Product> {

        @Override
        public int compare(Product o1, Product o2) {
            return o1.getCostInCents().compareTo(o2.getCostInCents());
        }

    }

    static Comparator<Product> getComparator(String name) {
        return COMPARATORS.get(name);
    }

}

您可能需要做一些小的更改,比如将其设置为空安全,或者做一些可能是最好的方法—使用排序策略。无需反射,与更复杂的排序逻辑兼容:

Map<String, Comparator<Product>> sortingStrategies = new HashMap<>(){
    {
        put("costInCents", Comparator.comparingLong(p->p.costInCents));
        put("quantity", Comparator.comparingLong(p->p.quantity));
        put("name", Comparator.comparing(p->p.name));
    }
};

private void sortByField(final Product[] productArray, final String sorterFieldName)
{
    Arrays.sort(productArray, sortingStrategies.get(sorterFieldName));
}
Map sortingStrategies=newhashmap(){
{
put(“成本激励”,比较器。比较长(p->p.成本激励));
放置(“数量”,比较器。比较长(p->p.quantity));
put(“name”,Comparator.comparing(p->p.name));
}
};
私有无效排序字段(最终产品[]productArray,最终字符串排序字段名称)
{
sort(productArray,sortingStrategies.get(sorterFieldName));
}

可比性此答案举例说明了为什么我不能使用此方法:我需要按用户提供名称的字段进行排序。您的方法只是按名称排序。@Jason ok。你可以创建两个Comparator的实现并使用你想要的,毕竟你有办法保证由用户提供的字段名称会非常相似,但是使用Comparator会更好,我向上投票,谢谢你的回复。考虑到我事先不知道我的字段将是什么,我认为有必要对
Product.getFields()
的结果进行循环,然后填充映射。再次感谢。@Qwer-Izuken如果您使用
Comparator.comparating
,在上面使用
Comparator.comparingIng/Long
的实例中,比较
,可以说是实现上的成功吗?询问排序策略实现自动化的原因。实际上,对于长包装类使用Comparator.compare更好。但是如果您使用的是原语long,那么应该使用comparingLong。这是一种与公认的方法类似的方法,只需要多一点代码:)谢谢@Turo@Jason这段代码的优点是产品知道它的可排序字段,并且只有在那里才有响应性(对于新字段,只有产品更改,而不是Main。并且不需要反射),因此,我认为组合将是最好的:-)
Map<String, Comparator<Product>> sortingStrategies = new HashMap<>(){
    {
        put("costInCents", Comparator.comparingLong(p->p.costInCents));
        put("quantity", Comparator.comparingLong(p->p.quantity));
        put("name", Comparator.comparing(p->p.name));
    }
};

private void sortByField(final Product[] productArray, final String sorterFieldName)
{
    Arrays.sort(productArray, sortingStrategies.get(sorterFieldName));
}