2017-08-07 151 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) –