Reputation: 1599
I'm using gson to serialize/deserialize and java 8 stream. Following is the code snippet,
private long sumofTime() {
Line[] lines = gson.fromJson(jsonString, Line[].class);
return Arrays.stream(lines).filter(x -> x.hasTime())
.mapToInt(x -> x.getTime).sum();
}
Line class looks like,
public class Line {
String name;
String stamp;
Integer time;
Integer xx;
Integer yy;
Integer zz;
...
...
boolean hasTotalTime() {
return totalTime != null;
}
...
getters...setters...}
Stream is used to check if specific variable(eg.time in the above example) is not null for each array element (i.e., Line), and then get the sum of all times.
Question: There are like 30 variables in the Line objects whose sum are needed, so how to make the solution more generic, instead of writing a sum method for each variable. Note that there is more than 1000 Line objects to process, thats why I thought Stream would be better.
Upvotes: 2
Views: 203
Reputation: 15537
You could pass a function into the sum method to get the value from each line that you want to 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);
This assumes that the function will return null if the attribute is not present, but you could also pass in a Predicate
to filter with if that is not the case.
Upvotes: 4
Reputation: 5739
What you're trying to do is partly a dynamic property extraction and partly a partially applied function, neither of which is particularly straightforward in Java. You'd be better off defining your Line
class in a different way. I would use a Map
to store the properties like so:
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);
}
}
Now, when you're looking for the sum of a bunch of things, you can call
public int sumOf(Line[] lines, String prop) {
return Arrays.stream(lines)
.filter(l -> l.has(prop))
.reduce(0, Integer::sum);
}
Upvotes: 2
Reputation: 43391
Rather than having a field per attribute, you could define an enum for the attributes, and then have a mapping from enum to 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);
}
}
Then you can loop over each attribute, getting its values for the lines and summing those values.
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();
}
Upvotes: 1