有没有办法让下面的代码更通用?
Is there a way to make the following code more generic?
我将 gson 用于 serialize/deserialize 和 java 8 流。以下是代码片段,
private long sumofTime() {
Line[] lines = gson.fromJson(jsonString, Line[].class);
return Arrays.stream(lines).filter(x -> x.hasTime())
.mapToInt(x -> x.getTime).sum();
}
第 class 行看起来像,
public class Line {
String name;
String stamp;
Integer time;
Integer xx;
Integer yy;
Integer zz;
...
...
boolean hasTotalTime() {
return totalTime != null;
}
...
getters...setters...}
Stream用于检查每个数组元素(即Line)的特定变量(上例中的eg.time)是否不为空,然后得到所有时间的总和。
问题:Line对象中有大约30个变量需要求和,那么如何使解决方案更通用,而不是为每个变量编写一个求和方法。请注意,有超过 1000 个 Line 对象要处理,这就是为什么我认为 Stream 会更好。
不是每个属性都有一个字段,您可以为属性定义一个枚举,然后有一个从枚举到 int 的映射:
public enum LineAttribute {
XX,
YY,
ZZ,
...
}
private final EnumMap<LineAttribute, Integer> attributes;
public Line() {
attributes = new EnumMap<>(LineAttribute.class);
// init all attributes to 0
for (LineAttribute attr : LineAttribute.values()) {
attributes.put(attr, 0);
}
}
然后您可以遍历每个属性,获取它的行值并将这些值相加。
private long sumOf(LineAttribute attr) {
Line[] lines = gson.fromJson(jsonString, Line[].class);
return Arrays.stream(lines)
.filter(x -> x.has(attr))
.mapToInt(x -> x.get(attr))
.sum();
}
您尝试做的部分是动态 属性 提取,部分是部分应用函数,两者在 Java 中都不是特别简单。您最好以不同的方式定义 Line
class。我会使用 Map
来存储属性,如下所示:
public class Line {
Map<String, Integer> props;
public Line() {
// Initialize props
}
public boolean has(String prop) {
return props.containsKey(prop);
}
public Integer get(String prop) {
return props.get(prop);
}
public void set(String prop, Object value) {
return props.put(prop, value);
}
}
现在,当您要查找一堆事物的总和时,您可以调用
public int sumOf(Line[] lines, String prop) {
return Arrays.stream(lines)
.filter(l -> l.has(prop))
.reduce(0, Integer::sum);
}
您可以将一个函数传递给 sum 方法,以从要求和的每一行中获取值:
public int sumLines(Function<Line, Integer> extractor){
Line[] lines = ...
return Arrays.stream(lines).map(extractor)
.filter(Objects::nonNull).mapToInt(i -> i).sum();
}
....
int time = sumLines(Line::getTime);
假设如果属性不存在,函数将 return 为空,但如果不是这种情况,您也可以传入 Predicate
进行过滤。
我将 gson 用于 serialize/deserialize 和 java 8 流。以下是代码片段,
private long sumofTime() {
Line[] lines = gson.fromJson(jsonString, Line[].class);
return Arrays.stream(lines).filter(x -> x.hasTime())
.mapToInt(x -> x.getTime).sum();
}
第 class 行看起来像,
public class Line {
String name;
String stamp;
Integer time;
Integer xx;
Integer yy;
Integer zz;
...
...
boolean hasTotalTime() {
return totalTime != null;
}
...
getters...setters...}
Stream用于检查每个数组元素(即Line)的特定变量(上例中的eg.time)是否不为空,然后得到所有时间的总和。
问题:Line对象中有大约30个变量需要求和,那么如何使解决方案更通用,而不是为每个变量编写一个求和方法。请注意,有超过 1000 个 Line 对象要处理,这就是为什么我认为 Stream 会更好。
不是每个属性都有一个字段,您可以为属性定义一个枚举,然后有一个从枚举到 int 的映射:
public enum LineAttribute {
XX,
YY,
ZZ,
...
}
private final EnumMap<LineAttribute, Integer> attributes;
public Line() {
attributes = new EnumMap<>(LineAttribute.class);
// init all attributes to 0
for (LineAttribute attr : LineAttribute.values()) {
attributes.put(attr, 0);
}
}
然后您可以遍历每个属性,获取它的行值并将这些值相加。
private long sumOf(LineAttribute attr) {
Line[] lines = gson.fromJson(jsonString, Line[].class);
return Arrays.stream(lines)
.filter(x -> x.has(attr))
.mapToInt(x -> x.get(attr))
.sum();
}
您尝试做的部分是动态 属性 提取,部分是部分应用函数,两者在 Java 中都不是特别简单。您最好以不同的方式定义 Line
class。我会使用 Map
来存储属性,如下所示:
public class Line {
Map<String, Integer> props;
public Line() {
// Initialize props
}
public boolean has(String prop) {
return props.containsKey(prop);
}
public Integer get(String prop) {
return props.get(prop);
}
public void set(String prop, Object value) {
return props.put(prop, value);
}
}
现在,当您要查找一堆事物的总和时,您可以调用
public int sumOf(Line[] lines, String prop) {
return Arrays.stream(lines)
.filter(l -> l.has(prop))
.reduce(0, Integer::sum);
}
您可以将一个函数传递给 sum 方法,以从要求和的每一行中获取值:
public int sumLines(Function<Line, Integer> extractor){
Line[] lines = ...
return Arrays.stream(lines).map(extractor)
.filter(Objects::nonNull).mapToInt(i -> i).sum();
}
....
int time = sumLines(Line::getTime);
假设如果属性不存在,函数将 return 为空,但如果不是这种情况,您也可以传入 Predicate
进行过滤。