2017-08-07 120 views
2

假设我们有以下属性的对象的参数属性的对象的列表:排序基于对象

排序此对象的名单 mylist,是根据它的属性 attr1
public class MyObject { 

    private String attr1; 
    private Integer attr2; 

    //... 

    public String getAttr1() { 
     return this.attr1; 
    } 

    public Integer getAttr2() { 
     return this.attr2; 
    } 
} 

的一种方式是:

mylist.sort(Comparator.comparing(MyObject::getAttr1)); 

是否有可能使用的方法在这里面的代码以动态的方式并更换getAttr1部分与基于其名称返回对象的属性,吸气的方法?喜欢的东西:

public void sortListByAttr(List<MyObject> list, String attr) { 
    list.sort(Comparator.comparing(MyObject::getGetterByAttr(attr))); 
} 

MyObject::getGetterByAttr(attr)部分不编译,我写它只是作为一个例子来解释我的想法

我试图实现与下面的代码new PropertyDescriptor(attr, MyObject.class).getReadMethod().invoke(new MyObject())的方法,但它仍然是不可能的从comparing方法调用方法与参数

回答

6

您可以添加喜欢

public static Function<MyObject,Object> getGetterByAttr(String s) { 
    switch(s) { 
     case "attr1": return MyObject::getAttr1; 
     case "attr2": return MyObject::getAttr2; 
    } 
    throw new IllegalArgumentException(s); 
} 
的方法210

到您的类,但返回的功能是不适合Comparator.comparing,因为它需要一个类型实现U extends Comparable<? super U>,虽然每个StringInteger能够在一个单独的调用实现这一约束,没有办法申报的通用返回类型为getGetterByAttr以允许这两种类型并且仍然与comparing的声明兼容。

另一种方法是完成Comparator s的工厂。

public static Comparator<MyObject> getComparator(String s) { 
    switch(s) { 
     case "attr1": return Comparator.comparing(MyObject::getAttr1); 
     case "attr2": return Comparator.comparing(MyObject::getAttr2); 
    } 
    throw new IllegalArgumentException(s); 
} 

使用像

public void sortListByAttr(List<MyObject> list, String attr) { 
    list.sort(getComparator(attr)); 
} 

这具有它也可以支持其类型是未Comparable和需要自定义属性Comparator的优点。而且,对于原始类型的更有效的比较器(例如使用comparingInt)将是可能的。

您也可以考虑使用的Map代替switch

private static Map<String,Comparator<MyObject>> COMPARATORS; 
static { 
    Map<String,Comparator<MyObject>> comparators=new HashMap<>(); 
    comparators.put("attr1", Comparator.comparing(MyObject::getAttr1)); 
    comparators.put("attr2", Comparator.comparing(MyObject::getAttr2)); 
    COMPARATORS = Collections.unmodifiableMap(comparators); 
} 
public static Comparator<MyObject> getComparator(String s) { 
    Comparator<MyObject> comparator = COMPARATORS.get(s); 
    if(comparator != null) return comparator; 
    throw new IllegalArgumentException(s); 
} 

更多动态只能通过反射,但这将代码复杂化,增添了很多潜在的误差源的,只有一点好处,考虑到您只需在上面的任一示例中添加一行源代码以添加对其他属性的支持。毕竟,定义的属性集在编译时是固定的。

3

你也可以拥有这地方比较将被定义一个地方:

static enum MyObjectComparator { 

    ATTR1("attr1", Comparator.comparing(MyObject::getAttr1)); 

    MyObjectComparator(String attrName, Comparator<MyObject> comparator) { 
     this.comparator = comparator; 
     this.attrName = attrName; 
    } 

    private final Comparator<MyObject> comparator; 

    private final String attrName; 

    private static MyObjectComparator[] allValues = MyObjectComparator.values(); 

    public static Comparator<MyObject> findByValue(String value) { 
     return Arrays.stream(allValues) 
       .filter(x -> x.attrName.equalsIgnoreCase(value)) 
       .map(x -> x.comparator) 
       .findAny() 
       .orElseThrow(RuntimeException::new); 
    } 

} 

而且你的用法是:

public void sortListByAttr(List<MyObject> list, String attr) { 
    list.sort(MyObjectComparator.findByValue(attr)); 
} 
+0

第一,给予好评所有。我想用一个枚举来做到这一点。如果你把'findByValue'重命名为'by',我认为它更有意义。 –

+1

@ holi-java这实际上是我在很多项目中看到的模式('findBy'),主要是Spring--所以我有点习惯它 – Eugene

+0

是啊,具体地介绍一个枚举,我们可以获取更多信息,使它以OO方式。那么我们可以用'sort(by(“attr1”)。comparator)'或'process(by(“attr1”)getter) –