Давайте начнем с быстрой уборки, давайте создадим enum
и удалим те неприятные String
константы, которые вы нигде не определили:
public enum Sign {
POSITIVE,
ZERO,
NEGATIVE;
public static Sign of(double d) {
if (d > 0) {
return POSITIVE;
}
if (d < 0) {
return NEGATIVE;
}
return ZERO;
}
}
Теперь мы можем тривиально написать метод для увеличения соответствующего значения:
public void increment(final double d, final Map<Sign, Integer> count) {
count.merge(Sign.of(d), 1, Integer::sum);
}
Для быстрого теста:
final Map<Sign, Integer> count = new EnumMap<>(Sign.class);
increment(0, count);
System.out.println(count);
increment(-1, count);
System.out.println(count);
increment(1, count);
System.out.println(count);
increment(-2, count);
System.out.println(count);
increment(2, count);
System.out.println(count);
Выход:
{ZERO=1}
{NEGATIVE=1, ZERO=1}
{POSITIVE=1, NEGATIVE=1, ZERO=1}
{POSITIVE=1, NEGATIVE=2, ZERO=1}
{POSITIVE=2, NEGATIVE=2, ZERO=1}
Так как же работает эта магия? С документация для Map.merge
Если указанный ключ еще не связан со значением или является
связанный с нулем, связывает его с заданным ненулевым значением.
В противном случае заменяет ассоциированное значение на результаты данного
функция переназначения или удаляет, если результат нулевой. Этот метод может
быть полезным при комбинировании нескольких сопоставленных значений для ключа.
Таким образом, key
принимает в качестве первого аргумента merge
- в случае Sign.of(d)
; это выбирает правильное ведро. Если значение, сопоставленное этому key
, равно null
, то оно просто помещает сопоставление для key
в value
, переданное в качестве второго аргумента - в данном случае 1
. Иначе это становится немного сложнее; он принимает значение, сопоставленное с этим ключом, и использует remappingFunction
, переданный в качестве третьего аргумента Это BiFunction<V,V,V>
, поэтому он принимает два аргумента типа V
, типа value
и возвращает один - он объединяет два вместе. Здесь мы используем Integer::sum
, чтобы взять существующие value
, новые value
и вернуть их sum
.
Но мы можем пойти еще дальше, мы можем использовать Stream
, чтобы выполнить это на произвольной double[]
:
public Map<Sign, Long> count(final double[] d) {
return Arrays.stream(d)
.mapToObj(Sign::of)
.collect(groupingBy(identity(), () -> new EnumMap<>(Sign.class), counting()));
}
Примечание: здесь я использовал EnumMap
, который Map
оптимизирован для использования enum
в качестве ключа.