2015-07-10 6 views
2

Я использую gson для сериализации/десериализации и потока 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(); 
} 

класса Line выглядит,

public class Line { 
    String name; 
    String stamp; 
    Integer time; 
    Integer xx; 
    Integer yy; 
    Integer zz; 
    ... 
    ... 

    boolean hasTotalTime() { 
     return totalTime != null; 
    } 
    ... 
    getters...setters...} 

потока используется для проверки, если конкретные переменные (eg.time в приведенном выше примере) не является нулевой для каждого элемента массива (т.е. Line), а затем получить сумму всех раз.

Вопрос: В объектах линии есть сумма, равная 30 переменным, сумма которых необходима, поэтому как сделать решение более общим, вместо того, чтобы писать метод суммы для каждой переменной. Обратите внимание, что для обработки более 1000 объектов Line, поэтому я подумал, что Stream будет лучше.

+0

, пожалуйста, оставьте комментарий для получения дополнительной информации, а не downvoting. – user3366706

+1

Во-первых, используйте ссылки на методы - это несколько чище. Что еще более важно, просто попросите какую-то функцию «extract extract» как входной сигнал к вашему методу. Затем примените эту функцию к своим объектам. Затем вы можете легко передать в методе ссылку на геттер в качестве входных данных для вашего метода суммирования. –

+0

* Что-то нужно знать, какие переменные (xx, yy, zz ..) подытожить и либо получить значения в последовательности для суммирования или выполнения самой суммы и вернуть результат. Недостаточно отражения и эвристики/аннотаций, которые приведет к дополнительному сложному коду - нет способа сделать его «более общим», и попытка сделать это - это борьба с статической типизацией времени компиляции. – user2864740

ответ

4

Вы можете передать функцию в метод суммы для получения значения из каждой строки, которую вы хотите подвести:

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); 

Это предполагает, что функция возвращает нулевое значение, если атрибут не присутствует, но вы также можете пройти в Predicate для фильтрации, если это не так.

+0

Ницца. Если вы считаете его более читаемым (и менее похожим на тавтологию), 'mapToInt' arg также может быть' Integer :: intValue'. – yshavit

+0

получение исключения null указателя. потому что некоторый объект Line имеет нулевые значения; атрибуты в строке являются Integer (по умолчанию - null), а не int (по умолчанию - 0). – user3366706

1

Вместо того, чтобы иметь поле для каждого атрибута, можно определить перечисление для атрибутов, а затем отображение из перечисления в 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(); 
} 
+0

Класс Line также имеет два атрибута String - имя и штамп. Хотя это не используется для вычислений, они связаны (вместе) с другими целыми атрибутами XX, YY и т. Д. – user3366706

+0

Это прекрасно, просто объявите их как обычные поля (как в вашем вопросе). Они не будут доступны в методе 'sumOf', но это вполне разумно (желательно, даже). – yshavit

+1

Хотя ваш ответ технически корректен, я считаю, что вы забываете, что массив 'Line []' десериализуется из строки JSON, поэтому структура класса 'Line' должна отражать этот вход JSON. –

2

То, что вы пытаетесь сделать, это частично динамическое извлечение собственности и частично частично применяется функция, ни один из которых особенно проста в Java. Вам было бы лучше определить ваш класс Line по-другому. Я хотел бы использовать 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); 
} 
+0

Если набор атрибутов исправлен (как он выглядит), лучше отобразить карту с помощью перечисления, чем 'String'. Это дает вам безопасность типов и значительно облегчает понимание того, какие атрибуты доступны при использовании 'Line'. В качестве бонуса EnumMap более эффективен (как в памяти, так и в ЦП), чем HashMap или TreeMap. – yshavit

+0

Хотя ваш ответ технически корректен, я считаю, что вы забываете, что массив 'Line []' десериализуется из строки JSON, поэтому структура класса 'Line' должна отражать этот вход JSON. –

+0

@yshavit не могли бы вы сообщить мне, как выполнить пользовательскую десериализацию в класс Line, описанный выше? – user3366706