将对象类型和字段传递给比较器
Passing Object type and Field to Comparator
是否可以写一个Comparator
,这样我就可以传递对象类型、字段类型和我喜欢排序的字段?我对 http://www.davekoelle.com/files/AlphanumComparator.java 做了一些小改动,以适应对象类型 User
中类型 String
的字段 email
的排序。我有有效的代码。
public class Main {
public static void main(String[] args) {
List<User> users = new ArrayList<>();
users.add(new User(7, "user1", "user1@c.com"));
users.add(new User(11, "user20", "user20@c.com"));
users.add(new User(5, "admin20", "admin20@c.com"));
users.add(new User(10, "user11", "user11@c.com"));
users.add(new User(6, "admin21", "admin21@c.com"));
users.add(new User(12, "user21", "user21@c.com"));
users.add(new User(8, "user2", "user2@c.com"));
users.add(new User(1, "admin1", "admin1@c.com"));
users.add(new User(3, "admin10", "admin10@c.com"));
users.add(new User(2, "admin2", "admin2@c.com"));
users.add(new User(9, "user10", "user10@c.com"));
users.add(new User(4, "admin11", "admin11@c.com"));
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;
}
}
如何将对象类型、字段类型和我喜欢排序的字段从 Main
中的 Collections.sort(users, new AlphanumComparator());
class 传递到 AlphanumComparator
以及如何传递我在 AlphanumComparator
中适应这个?所以在这种情况下,我将传递对象类型 User
字段 email
和字段类型 String
。但是如果我喜欢按 id
排序,我会传递对象类型 User
、字段 email
和字段类型 int
.
我会保持 AlphanumComparator 不变,并创建一个新的 class 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());
}
}
}
更新:
要处理原始类型,可以更改一行方法比较:
} else if (isComparable || value1 instanceof Comparable) {
更新 2:
您的主要方法将变为:
public static void main(String[] args) {
List<User> users = new ArrayList<>();
users.add(new User(7, "user1", "user1@c.com"));
users.add(new User(11, "user20", "user20@c.com"));
users.add(new User(5, "admin20", "admin20@c.com"));
users.add(new User(10, "user11", "user11@c.com"));
users.add(new User(6, "admin21", "admin21@c.com"));
users.add(new User(12, "user21", "user21@c.com"));
users.add(new User(8, "user2", "user2@c.com"));
users.add(new User(1, "admin1", "admin1@c.com"));
users.add(new User(3, "admin10", "admin10@c.com"));
users.add(new User(2, "admin2", "admin2@c.com"));
users.add(new User(9, "user10", "user10@c.com"));
users.add(new User(4, "admin11", "admin11@c.com"));
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());
}
}
是否可以写一个Comparator
,这样我就可以传递对象类型、字段类型和我喜欢排序的字段?我对 http://www.davekoelle.com/files/AlphanumComparator.java 做了一些小改动,以适应对象类型 User
中类型 String
的字段 email
的排序。我有有效的代码。
public class Main {
public static void main(String[] args) {
List<User> users = new ArrayList<>();
users.add(new User(7, "user1", "user1@c.com"));
users.add(new User(11, "user20", "user20@c.com"));
users.add(new User(5, "admin20", "admin20@c.com"));
users.add(new User(10, "user11", "user11@c.com"));
users.add(new User(6, "admin21", "admin21@c.com"));
users.add(new User(12, "user21", "user21@c.com"));
users.add(new User(8, "user2", "user2@c.com"));
users.add(new User(1, "admin1", "admin1@c.com"));
users.add(new User(3, "admin10", "admin10@c.com"));
users.add(new User(2, "admin2", "admin2@c.com"));
users.add(new User(9, "user10", "user10@c.com"));
users.add(new User(4, "admin11", "admin11@c.com"));
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;
}
}
如何将对象类型、字段类型和我喜欢排序的字段从 Main
中的 Collections.sort(users, new AlphanumComparator());
class 传递到 AlphanumComparator
以及如何传递我在 AlphanumComparator
中适应这个?所以在这种情况下,我将传递对象类型 User
字段 email
和字段类型 String
。但是如果我喜欢按 id
排序,我会传递对象类型 User
、字段 email
和字段类型 int
.
我会保持 AlphanumComparator 不变,并创建一个新的 class 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());
}
}
}
更新:
要处理原始类型,可以更改一行方法比较:
} else if (isComparable || value1 instanceof Comparable) {
更新 2:
您的主要方法将变为:
public static void main(String[] args) {
List<User> users = new ArrayList<>();
users.add(new User(7, "user1", "user1@c.com"));
users.add(new User(11, "user20", "user20@c.com"));
users.add(new User(5, "admin20", "admin20@c.com"));
users.add(new User(10, "user11", "user11@c.com"));
users.add(new User(6, "admin21", "admin21@c.com"));
users.add(new User(12, "user21", "user21@c.com"));
users.add(new User(8, "user2", "user2@c.com"));
users.add(new User(1, "admin1", "admin1@c.com"));
users.add(new User(3, "admin10", "admin10@c.com"));
users.add(new User(2, "admin2", "admin2@c.com"));
users.add(new User(9, "user10", "user10@c.com"));
users.add(new User(4, "admin11", "admin11@c.com"));
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());
}
}