1/* 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package java.util; 26 27import java.util.function.DoubleConsumer; 28 29/** 30 * A state object for collecting statistics such as count, min, max, sum, and 31 * average. 32 * 33 * <p>This class is designed to work with (though does not require) 34 * {@linkplain java.util.stream streams}. For example, you can compute 35 * summary statistics on a stream of doubles with: 36 * <pre> {@code 37 * DoubleSummaryStatistics stats = doubleStream.collect(DoubleSummaryStatistics::new, 38 * DoubleSummaryStatistics::accept, 39 * DoubleSummaryStatistics::combine); 40 * }</pre> 41 * 42 * <p>{@code DoubleSummaryStatistics} can be used as a 43 * {@linkplain java.util.stream.Stream#collect(Collector) reduction} 44 * target for a {@linkplain java.util.stream.Stream stream}. For example: 45 * 46 * <pre> {@code 47 * DoubleSummaryStatistics stats = people.stream() 48 * .collect(Collectors.summarizingDouble(Person::getWeight)); 49 *}</pre> 50 * 51 * This computes, in a single pass, the count of people, as well as the minimum, 52 * maximum, sum, and average of their weights. 53 * 54 * @implNote This implementation is not thread safe. However, it is safe to use 55 * {@link java.util.stream.Collectors#summarizingDouble(java.util.function.ToDoubleFunction) 56 * Collectors.toDoubleStatistics()} on a parallel stream, because the parallel 57 * implementation of {@link java.util.stream.Stream#collect Stream.collect()} 58 * provides the necessary partitioning, isolation, and merging of results for 59 * safe and efficient parallel execution. 60 * @since 1.8 61 */ 62public class DoubleSummaryStatistics implements DoubleConsumer { 63 private long count; 64 private double sum; 65 private double sumCompensation; // Low order bits of sum 66 private double simpleSum; // Used to compute right sum for non-finite inputs 67 private double min = Double.POSITIVE_INFINITY; 68 private double max = Double.NEGATIVE_INFINITY; 69 70 /** 71 * Construct an empty instance with zero count, zero sum, 72 * {@code Double.POSITIVE_INFINITY} min, {@code Double.NEGATIVE_INFINITY} 73 * max and zero average. 74 */ 75 public DoubleSummaryStatistics() { } 76 77 /** 78 * Records another value into the summary information. 79 * 80 * @param value the input value 81 */ 82 @Override 83 public void accept(double value) { 84 ++count; 85 simpleSum += value; 86 sumWithCompensation(value); 87 min = Math.min(min, value); 88 max = Math.max(max, value); 89 } 90 91 /** 92 * Combines the state of another {@code DoubleSummaryStatistics} into this 93 * one. 94 * 95 * @param other another {@code DoubleSummaryStatistics} 96 * @throws NullPointerException if {@code other} is null 97 */ 98 public void combine(DoubleSummaryStatistics other) { 99 count += other.count; 100 simpleSum += other.simpleSum; 101 sumWithCompensation(other.sum); 102 sumWithCompensation(other.sumCompensation); 103 min = Math.min(min, other.min); 104 max = Math.max(max, other.max); 105 } 106 107 /** 108 * Incorporate a new double value using Kahan summation / 109 * compensated summation. 110 */ 111 private void sumWithCompensation(double value) { 112 double tmp = value - sumCompensation; 113 double velvel = sum + tmp; // Little wolf of rounding error 114 sumCompensation = (velvel - sum) - tmp; 115 sum = velvel; 116 } 117 118 /** 119 * Return the count of values recorded. 120 * 121 * @return the count of values 122 */ 123 public final long getCount() { 124 return count; 125 } 126 127 /** 128 * Returns the sum of values recorded, or zero if no values have been 129 * recorded. 130 * 131 * If any recorded value is a NaN or the sum is at any point a NaN 132 * then the sum will be NaN. 133 * 134 * <p> The value of a floating-point sum is a function both of the 135 * input values as well as the order of addition operations. The 136 * order of addition operations of this method is intentionally 137 * not defined to allow for implementation flexibility to improve 138 * the speed and accuracy of the computed result. 139 * 140 * In particular, this method may be implemented using compensated 141 * summation or other technique to reduce the error bound in the 142 * numerical sum compared to a simple summation of {@code double} 143 * values. 144 * 145 * @apiNote Values sorted by increasing absolute magnitude tend to yield 146 * more accurate results. 147 * 148 * @return the sum of values, or zero if none 149 */ 150 public final double getSum() { 151 // Better error bounds to add both terms as the final sum 152 double tmp = sum + sumCompensation; 153 if (Double.isNaN(tmp) && Double.isInfinite(simpleSum)) 154 // If the compensated sum is spuriously NaN from 155 // accumulating one or more same-signed infinite values, 156 // return the correctly-signed infinity stored in 157 // simpleSum. 158 return simpleSum; 159 else 160 return tmp; 161 } 162 163 /** 164 * Returns the minimum recorded value, {@code Double.NaN} if any recorded 165 * value was NaN or {@code Double.POSITIVE_INFINITY} if no values were 166 * recorded. Unlike the numerical comparison operators, this method 167 * considers negative zero to be strictly smaller than positive zero. 168 * 169 * @return the minimum recorded value, {@code Double.NaN} if any recorded 170 * value was NaN or {@code Double.POSITIVE_INFINITY} if no values were 171 * recorded 172 */ 173 public final double getMin() { 174 return min; 175 } 176 177 /** 178 * Returns the maximum recorded value, {@code Double.NaN} if any recorded 179 * value was NaN or {@code Double.NEGATIVE_INFINITY} if no values were 180 * recorded. Unlike the numerical comparison operators, this method 181 * considers negative zero to be strictly smaller than positive zero. 182 * 183 * @return the maximum recorded value, {@code Double.NaN} if any recorded 184 * value was NaN or {@code Double.NEGATIVE_INFINITY} if no values were 185 * recorded 186 */ 187 public final double getMax() { 188 return max; 189 } 190 191 /** 192 * Returns the arithmetic mean of values recorded, or zero if no 193 * values have been recorded. 194 * 195 * If any recorded value is a NaN or the sum is at any point a NaN 196 * then the average will be code NaN. 197 * 198 * <p>The average returned can vary depending upon the order in 199 * which values are recorded. 200 * 201 * This method may be implemented using compensated summation or 202 * other technique to reduce the error bound in the {@link #getSum 203 * numerical sum} used to compute the average. 204 * 205 * @apiNote Values sorted by increasing absolute magnitude tend to yield 206 * more accurate results. 207 * 208 * @return the arithmetic mean of values, or zero if none 209 */ 210 public final double getAverage() { 211 return getCount() > 0 ? getSum() / getCount() : 0.0d; 212 } 213 214 /** 215 * {@inheritDoc} 216 * 217 * Returns a non-empty string representation of this object suitable for 218 * debugging. The exact presentation format is unspecified and may vary 219 * between implementations and versions. 220 */ 221 @Override 222 public String toString() { 223 return String.format( 224 "%s{count=%d, sum=%f, min=%f, average=%f, max=%f}", 225 this.getClass().getSimpleName(), 226 getCount(), 227 getSum(), 228 getMin(), 229 getAverage(), 230 getMax()); 231 } 232} 233