DoubleSummaryStatistics.java revision 01c4dc04704e34c54162b52f390623038befa514
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// TODO: Revert changes from {@linkplain} and {@link} to {@code} once 30// the streams changes have landed. 31/** 32 * A state object for collecting statistics such as count, min, max, sum, and 33 * average. 34 * 35 * <p>This class is designed to work with (though does not require) 36 * {@code java.util.stream streams}. For example, you can compute 37 * summary statistics on a stream of doubles with: 38 * <pre> {@code 39 * DoubleSummaryStatistics stats = doubleStream.collect(DoubleSummaryStatistics::new, 40 * DoubleSummaryStatistics::accept, 41 * DoubleSummaryStatistics::combine); 42 * }</pre> 43 * 44 * <p>{@code DoubleSummaryStatistics} can be used as a 45 * {@code java.util.stream.Stream#collect(Collector) reduction} 46 * target for a {@code java.util.stream.Stream stream}. For example: 47 * 48 * <pre> {@code 49 * DoubleSummaryStatistics stats = people.stream() 50 * .collect(Collectors.summarizingDouble(Person::getWeight)); 51 *}</pre> 52 * 53 * This computes, in a single pass, the count of people, as well as the minimum, 54 * maximum, sum, and average of their weights. 55 * 56 * @implNote This implementation is not thread safe. However, it is safe to use 57 * {@code java.util.stream.Collectors#summarizingDouble(java.util.function.ToDoubleFunction) 58 * Collectors.toDoubleStatistics()} on a parallel stream, because the parallel 59 * implementation of {@code java.util.stream.Stream#collect Stream.collect()} 60 * provides the necessary partitioning, isolation, and merging of results for 61 * safe and efficient parallel execution. 62 * @since 1.8 63 */ 64public class DoubleSummaryStatistics implements DoubleConsumer { 65 private long count; 66 private double sum; 67 private double sumCompensation; // Low order bits of sum 68 private double simpleSum; // Used to compute right sum for non-finite inputs 69 private double min = Double.POSITIVE_INFINITY; 70 private double max = Double.NEGATIVE_INFINITY; 71 72 /** 73 * Construct an empty instance with zero count, zero sum, 74 * {@code Double.POSITIVE_INFINITY} min, {@code Double.NEGATIVE_INFINITY} 75 * max and zero average. 76 */ 77 public DoubleSummaryStatistics() { } 78 79 /** 80 * Records another value into the summary information. 81 * 82 * @param value the input value 83 */ 84 @Override 85 public void accept(double value) { 86 ++count; 87 simpleSum += value; 88 sumWithCompensation(value); 89 min = Math.min(min, value); 90 max = Math.max(max, value); 91 } 92 93 /** 94 * Combines the state of another {@code DoubleSummaryStatistics} into this 95 * one. 96 * 97 * @param other another {@code DoubleSummaryStatistics} 98 * @throws NullPointerException if {@code other} is null 99 */ 100 public void combine(DoubleSummaryStatistics other) { 101 count += other.count; 102 simpleSum += other.simpleSum; 103 sumWithCompensation(other.sum); 104 sumWithCompensation(other.sumCompensation); 105 min = Math.min(min, other.min); 106 max = Math.max(max, other.max); 107 } 108 109 /** 110 * Incorporate a new double value using Kahan summation / 111 * compensated summation. 112 */ 113 private void sumWithCompensation(double value) { 114 double tmp = value - sumCompensation; 115 double velvel = sum + tmp; // Little wolf of rounding error 116 sumCompensation = (velvel - sum) - tmp; 117 sum = velvel; 118 } 119 120 /** 121 * Return the count of values recorded. 122 * 123 * @return the count of values 124 */ 125 public final long getCount() { 126 return count; 127 } 128 129 /** 130 * Returns the sum of values recorded, or zero if no values have been 131 * recorded. 132 * 133 * If any recorded value is a NaN or the sum is at any point a NaN 134 * then the sum will be NaN. 135 * 136 * <p> The value of a floating-point sum is a function both of the 137 * input values as well as the order of addition operations. The 138 * order of addition operations of this method is intentionally 139 * not defined to allow for implementation flexibility to improve 140 * the speed and accuracy of the computed result. 141 * 142 * In particular, this method may be implemented using compensated 143 * summation or other technique to reduce the error bound in the 144 * numerical sum compared to a simple summation of {@code double} 145 * values. 146 * 147 * @apiNote Values sorted by increasing absolute magnitude tend to yield 148 * more accurate results. 149 * 150 * @return the sum of values, or zero if none 151 */ 152 public final double getSum() { 153 // Better error bounds to add both terms as the final sum 154 double tmp = sum + sumCompensation; 155 if (Double.isNaN(tmp) && Double.isInfinite(simpleSum)) 156 // If the compensated sum is spuriously NaN from 157 // accumulating one or more same-signed infinite values, 158 // return the correctly-signed infinity stored in 159 // simpleSum. 160 return simpleSum; 161 else 162 return tmp; 163 } 164 165 /** 166 * Returns the minimum recorded value, {@code Double.NaN} if any recorded 167 * value was NaN or {@code Double.POSITIVE_INFINITY} if no values were 168 * recorded. Unlike the numerical comparison operators, this method 169 * considers negative zero to be strictly smaller than positive zero. 170 * 171 * @return the minimum recorded value, {@code Double.NaN} if any recorded 172 * value was NaN or {@code Double.POSITIVE_INFINITY} if no values were 173 * recorded 174 */ 175 public final double getMin() { 176 return min; 177 } 178 179 /** 180 * Returns the maximum recorded value, {@code Double.NaN} if any recorded 181 * value was NaN or {@code Double.NEGATIVE_INFINITY} if no values were 182 * recorded. Unlike the numerical comparison operators, this method 183 * considers negative zero to be strictly smaller than positive zero. 184 * 185 * @return the maximum recorded value, {@code Double.NaN} if any recorded 186 * value was NaN or {@code Double.NEGATIVE_INFINITY} if no values were 187 * recorded 188 */ 189 public final double getMax() { 190 return max; 191 } 192 193 /** 194 * Returns the arithmetic mean of values recorded, or zero if no 195 * values have been recorded. 196 * 197 * If any recorded value is a NaN or the sum is at any point a NaN 198 * then the average will be code NaN. 199 * 200 * <p>The average returned can vary depending upon the order in 201 * which values are recorded. 202 * 203 * This method may be implemented using compensated summation or 204 * other technique to reduce the error bound in the {@link #getSum 205 * numerical sum} used to compute the average. 206 * 207 * @apiNote Values sorted by increasing absolute magnitude tend to yield 208 * more accurate results. 209 * 210 * @return the arithmetic mean of values, or zero if none 211 */ 212 public final double getAverage() { 213 return getCount() > 0 ? getSum() / getCount() : 0.0d; 214 } 215 216 /** 217 * {@inheritDoc} 218 * 219 * Returns a non-empty string representation of this object suitable for 220 * debugging. The exact presentation format is unspecified and may vary 221 * between implementations and versions. 222 */ 223 @Override 224 public String toString() { 225 return String.format( 226 "%s{count=%d, sum=%f, min=%f, average=%f, max=%f}", 227 this.getClass().getSimpleName(), 228 getCount(), 229 getSum(), 230 getMin(), 231 getAverage(), 232 getMax()); 233 } 234} 235