2017-07-28 5 views
0

Comparatorと書くことができます。オブジェクトタイプ、フィールドタイプ、ソートしたいフィールドを渡すことができますか?オブジェクトタイプUserStringというフィールドemailの並べ替えに対応するために、http://www.davekoelle.com/files/AlphanumComparator.javaを少し変更しました。私は動作するこのコードを持っています。オブジェクトタイプとフィールドをコンパレータに渡す

public class Main { 

    public static void main(String[] args) { 

     List<User> users = new ArrayList<>(); 

     users.add(new User(7, "user1", "[email protected]")); 
     users.add(new User(11, "user20", "[email protected]")); 
     users.add(new User(5, "admin20", "[email protected]")); 
     users.add(new User(10, "user11", "[email protected]")); 
     users.add(new User(6, "admin21", "[email protected]")); 
     users.add(new User(12, "user21", "[email protected]")); 
     users.add(new User(8, "user2", "[email protected]")); 
     users.add(new User(1, "admin1", "[email protected]")); 
     users.add(new User(3, "admin10", "[email protected]")); 
     users.add(new User(2, "admin2", "[email protected]")); 
     users.add(new User(9, "user10", "[email protected]")); 
     users.add(new User(4, "admin11", "[email protected]")); 

     for (User item : users) { 
      System.out.println(item.getEmail()); 
     } 

     System.out.println("__________________________"); 

     Collections.sort(users, new AlphanumComparator()); 

     for (User item : users) { 
      System.out.println(item.getEmail()); 
     } 
    } 
} 

public class User { 

    int id; 
    String name; 
    String email; 

// Constructor, Getters and Setters 
} 

public class AlphanumComparator implements Comparator<User> { 
    private final boolean isDigit(char ch) { 
     return ((ch >= 48) && (ch <= 57)); 
    } 

    /** 
    * Length of string is passed in for improved efficiency (only need to calculate it once) 
    **/ 
    private final String getChunk(String s, int slength, int marker) { 
     StringBuilder chunk = new StringBuilder(); 
     char c = s.charAt(marker); 
     chunk.append(c); 
     marker++; 
     if (isDigit(c)) { 
      while (marker < slength) { 
       c = s.charAt(marker); 
       if (!isDigit(c)) 
        break; 
       chunk.append(c); 
       marker++; 
      } 
     } else { 
      while (marker < slength) { 
       c = s.charAt(marker); 
       if (isDigit(c)) 
        break; 
       chunk.append(c); 
       marker++; 
      } 
     } 
     return chunk.toString(); 
    } 

    public int compare(User u1, User u2) { 
     if ((u1 == null) || (u2 == null)) { 
      return 0; 
     } 

     int thisMarker = 0; 
     int thatMarker = 0; 
     int s1Length = u1.getEmail().length(); 
     int s2Length = u2.getEmail().length(); 

     while (thisMarker < s1Length && thatMarker < s2Length) { 
      String thisChunk = getChunk(u1.getEmail(), s1Length, thisMarker); 
      thisMarker += thisChunk.length(); 

      String thatChunk = getChunk(u2.getEmail(), s2Length, thatMarker); 
      thatMarker += thatChunk.length(); 

      // If both chunks contain numeric characters, sort them numerically 
      int result = 0; 
      if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) { 
       // Simple chunk comparison by length. 
       int thisChunkLength = thisChunk.length(); 
       result = thisChunkLength - thatChunk.length(); 
       // If equal, the first different number counts 
       if (result == 0) { 
        for (int i = 0; i < thisChunkLength; i++) { 
         result = thisChunk.charAt(i) - thatChunk.charAt(i); 
         if (result != 0) { 
          return result; 
         } 
        } 
       } 
      } else { 
       result = thisChunk.compareTo(thatChunk); 
      } 

      if (result != 0) 
       return result; 
     } 

     return s1Length - s2Length; 
    } 
} 

どのように私は、オブジェクトタイプ、フィールドタイプと私はAlphanumComparatorMainクラスにCollections.sort(users, new AlphanumComparator());からでソートするように、私はAlphanumComparatorでこれに対処することができますどのようにフィールドを渡すことができますか?したがってこの場合、オブジェクトタイプUserフィールドemailとフィールドタイプStringを渡します。しかし、私がidを並べ替えたい場合は、オブジェクトタイプUser、フィールドemail、フィールドタイプintを渡します。

+0

リストを 'user.id'でソートしたいのですか?フィールドを直接比較するだけです。 – Killjoy1221

答えて

1

私があるようAlphanumComparatorを維持し、新しいクラスFieldComparator作成します。

public class FieldComparator<T> implements Comparator<T> { 
    private static final Logger LOG = Logger.getLogger(
      FieldComparator.class.getName()); 
    private static final AlphanumComparator ALPHANUM = new AlphanumComparator(); 

    private final Field field; 
    private final boolean isString; 
    private final boolean isComparable; 

    public FieldComparator(Class<T> clazz, String name) { 
     try { 
      field = clazz.getDeclaredField(name); 
      field.setAccessible(true); 
      Class<?> fieldType = field.getType(); 
      isString = fieldType == String.class; 
      isComparable = Comparable.class.isAssignableFrom(fieldType); 
     } catch (NoSuchFieldException | SecurityException ex) { 
      LOG.log(Level.SEVERE, null, ex); 
      throw new RuntimeException(ex.getMessage()); 
     } 
    } 

    @Override 
    public int compare(T o1, T o2) { 
     try { 
      Object value1 = field.get(o1); 
      Object value2 = field.get(o2); 
      if (value1 == null) { 
       return value2 == null ? 0 : -1; 
      } else if (value2 == null) { 
       return 1; 
      } else if (isString) { 
       return ALPHANUM.compare((String)value1, (String)value2); 
      } else if (isComparable) { 
       return ((Comparable)value1).compareTo(value2); 
      } else { 
       // don't know how to compare fields 
       return 0; 
      } 
     } catch (IllegalArgumentException | IllegalAccessException ex) { 
      LOG.log(Level.SEVERE, null, ex); 
      throw new RuntimeException(ex.getMessage()); 
     } 
    } 
} 

UPDATE:

プリミティブ型に対処するために、あなたは方法の1行比較を変更することができます

} else if (isComparable || value1 instanceof Comparable) { 

更新2:

メインメソッドは次のようになります。

public static void main(String[] args) { 

    List<User> users = new ArrayList<>(); 

    users.add(new User(7, "user1", "[email protected]")); 
    users.add(new User(11, "user20", "[email protected]")); 
    users.add(new User(5, "admin20", "[email protected]")); 
    users.add(new User(10, "user11", "[email protected]")); 
    users.add(new User(6, "admin21", "[email protected]")); 
    users.add(new User(12, "user21", "[email protected]")); 
    users.add(new User(8, "user2", "[email protected]")); 
    users.add(new User(1, "admin1", "[email protected]")); 
    users.add(new User(3, "admin10", "[email protected]")); 
    users.add(new User(2, "admin2", "[email protected]")); 
    users.add(new User(9, "user10", "[email protected]")); 
    users.add(new User(4, "admin11", "[email protected]")); 

    for (User item : users) { 
     System.out.println(item.getEmail()); 
    } 

    System.out.println("__________________________"); 

    Collections.sort(users, new FieldComparator(User.class, "email")); 

    for (User item : users) { 
     System.out.println(item.getEmail()); 
    } 

    System.out.println("__________________________"); 

    Collections.sort(users, new FieldComparator(User.class, "name")); 

    for (User item : users) { 
     System.out.println(item.getEmail()); 
    } 

    System.out.println("__________________________"); 

    Collections.sort(users, new FieldComparator(User.class, "id")); 

    for (User item : users) { 
     System.out.println(item.getEmail()); 
    } 
} 
+0

ありがとう!ちょうど私が探していたもの。私は、最初のバージョンがプリミティブ(私の場合は 'int')では機能しないことにも気付きました。私はあなたのように優雅ではない解決策を持って来ました。 'boolean isInt = fieldType == int.class;'と 'else if(isComparable || isInt)'を使用しています。もちろん、これは他のプリミティブのケースをカバーしません。それだけでなく、 'int.class'は正しいですか?私はちょうど 'int'がオブジェクトではなくプリミティブであると考えていました。 – g3blv

+0

@ g3blvはい、int.classはintフィールドの型です。 –

関連する問題