例如:"[{"plateNumber":"京A00001","model":"tesla90","color":"black"},{"plateNumber":"京A00001","model":"tesla90","color":"black"},{"plateNumber":"京A00001","model":"tesla90","color":"red"}]"
假设我们认为plateNumber相同的元素具有等价性,那么如上json数组可以去重为:[{"plateNumber":"京A00001","model":"tesla90","color":"black"}]
思路是将一个json数组映射为List<Entity>,利用Set<Entity>本身的特性去重;所以我们需要对Entity覆盖HashCode和Equals方法,为了最小化最实体对象的侵入,并将该去重作为一个通用组件使用,有如下的设计。
注:按照这个设计思路,去重时具体选择保留哪个元素是不确定且无法指定的,算是这个思路的缺陷。有新的想法,欢迎交流探讨。
1.设计一个接口,用来设置去重的依据属性。
public interface Reducible {
void setType(String...type);
}
2.为了减少在覆盖HashCode和Equals时对实体类的侵入,我们设计一个抽象类(实现Reducible),来负责记录去重的字段名称,同时覆盖HashCode和Equals方法。如果不指定去重的依据属性,则按照全部属性去重。
public abstract class ReduceAbstract implements Reducible {
private String[] typeArray;
@Override
public void setType(String... type) {
this.typeArray = type;
}
@Override
public int hashCode() {
int hash = 1;
checkTypeArray();
int size = typeArray.length;
for (int i = 0; i < size; i++) {
try {
Field field = this.getClass().getDeclaredField(typeArray[i]);
field.setAccessible(true);
Object value = field.get(this);
hash += value.hashCode();
} catch (Exception e) {
e.printStackTrace();
}
}
return hash;
}
@Override
public boolean equals(Object other) {
if (this == other)
return true;
if (other == null)
return false;
if (getClass() != other.getClass())
return false;
checkTypeArray();
int size = typeArray.length;
boolean result = true;
for (int i = 0; i < size; i++) {
try {
Field field = this.getClass().getDeclaredField(typeArray[i]);
field.setAccessible(true);
Object subValue = field.get(this);
Object otherValue = field.get(other);
if ((subValue == null && otherValue != null) || (subValue != null && otherValue == null)) {
result = false;
break;
}
if (subValue != null && otherValue != null) {
if (!subValue.equals(otherValue)) {
result = false;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
private void checkTypeArray() {
if (typeArray == null || typeArray.length < 1) {
// 如果不指定去重属性,则根据所有属性进行比对
Field[] fields = this.getClass().getDeclaredFields();
typeArray = new String[fields.length];
for (int i = 0; i < fields.length; i++) {
typeArray[i] = fields[i].getName();
}
}
}
}
3.写一个工具类用来对一个List<Entity>进行去重。
public static List<? extends ReduceAbstract> reduce(List<? extends ReduceAbstract> list, String... type) {
for (ReduceAbstract reducible : list) {
reducible.setType(type);
}
return new ArrayList<>(new HashSet<>(list));
}
4.对于任何一个想要去重的实体类,只需要继承ReduceAbstract,然后调用工具类ReduceUtil的reduce方法,并且传入重复的依据属性就可以了。
public class Vehicle extends ReduceAbstract{
private String plateNumber;
private String model;
private String color;
public String getPlateNumber() {
return plateNumber;
}
public void setPlateNumber(String plateNumber) {
this.plateNumber = plateNumber;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Vehicle [plateNumber=" + plateNumber + ", model=" + model + ", color=" + color + "]";
}
}
public class Client {
public static void main(String[] args) {
String originJson = "[{\"plateNumber\":\"京A00001\",\"model\":\"tesla90\",\"color\":\"black\"},"
+ "{\"plateNumber\":\"京A00002\",\"model\":\"tesla90\",\"color\":\"black\"},"
+ "{\"plateNumber\":\"京A00002\",\"model\":\"tesla90\",\"color\":\"red\"}]";
List<Vehicle> list = JSONArray.parseArray(originJson, Vehicle.class);
System.out.println("before: " + list);
List<Vehicle> result = (List<Vehicle>) ReduceUtil.reduce(list, "color");
System.out.println("after: " + result);
}
}
得到的结果是:
before: [Vehicle [plateNumber=京A00001, model=tesla90, color=black], Vehicle [plateNumber=京A00002, model=tesla90, color=black], Vehicle [plateNumber=京A00002, model=tesla90, color=red]]
after: [Vehicle [plateNumber=京A00002, model=tesla90, color=red], Vehicle [plateNumber=京A00001, model=tesla90, color=black]]
如果判断重复的依据改变了,只需要在调用reduce方法的时候传入指定的类型即可。