High Dynamic Range (HDR) Histogram for recording and analyzing value distributions with configurable precision across wide dynamic ranges.
—
Comprehensive iteration capabilities for detailed exploration and analysis of histogram data. HdrHistogram provides multiple iterator types for different analysis patterns, from percentile exploration to linear bucket analysis.
Base class for all histogram iterators providing common iteration functionality.
public abstract class AbstractHistogramIterator implements Iterator<HistogramIterationValue> {
// Iterator interface
boolean hasNext();
HistogramIterationValue next();
void remove(); // Unsupported operation
// Reset and reuse
void reset();
void reset(AbstractHistogram histogram);
// Current state access
long getValueIteratedTo();
long getValueIteratedFrom();
long getCountAtValueIteratedTo();
long getCountAddedInThisIterationStep();
long getTotalCountToThisValue();
long getTotalValueToThisValue();
double getPercentileIteratedTo();
double getPercentileLevelIteratedTo();
}Iterates through histogram values according to percentile levels with exponentially decreasing resolution, focusing detail on higher percentiles.
public class PercentileIterator extends AbstractHistogramIterator {
// Constructor
public PercentileIterator(AbstractHistogram histogram, int percentileTicksPerHalfDistance);
// Reset for reuse
void reset(int percentileTicksPerHalfDistance);
void reset(AbstractHistogram histogram, int percentileTicksPerHalfDistance);
}// Create percentile iterator with 5 ticks per half distance
// This provides more detail at higher percentiles
PercentileIterator iterator = new PercentileIterator(histogram, 5);
System.out.println("Percentile Distribution:");
System.out.println("Value(μs) | Percentile | Count | TotalCount");
System.out.println("----------|------------|-------|------------");
while (iterator.hasNext()) {
HistogramIterationValue iterationValue = iterator.next();
System.out.printf("%8d | %9.4f%% | %5d | %10d%n",
iterationValue.getValueIteratedTo(),
iterationValue.getPercentileIteratedTo(),
iterationValue.getCountAddedInThisIterationStep(),
iterationValue.getTotalCountToThisValue());
}The percentileTicksPerHalfDistance parameter controls resolution:
// Low resolution - fewer data points, faster iteration
PercentileIterator coarse = new PercentileIterator(histogram, 1);
// Medium resolution - good balance
PercentileIterator balanced = new PercentileIterator(histogram, 5);
// High resolution - more data points, detailed analysis
PercentileIterator detailed = new PercentileIterator(histogram, 10);
// Compare percentile coverage
System.out.println("Percentile Resolution Comparison:");
analyzePercentileResolution("Coarse (1)", coarse);
analyzePercentileResolution("Balanced (5)", balanced);
analyzePercentileResolution("Detailed (10)", detailed);public void analyzeSLACompliance(AbstractHistogram histogram, long slaThreshold) {
PercentileIterator iterator = new PercentileIterator(histogram, 5);
System.out.printf("SLA Analysis (threshold: %d μs):%n", slaThreshold);
while (iterator.hasNext()) {
HistogramIterationValue value = iterator.next();
long latency = value.getValueIteratedTo();
double percentile = value.getPercentileIteratedTo();
// Highlight key percentiles and SLA violations
if (percentile >= 50.0) { // Focus on P50 and above
String status = latency <= slaThreshold ? "✓" : "✗ VIOLATION";
System.out.printf("P%.1f: %d μs %s%n", percentile, latency, status);
}
// Stop at 99.99th percentile for most analyses
if (percentile >= 99.99) break;
}
}Iterates through histogram values using linear steps of equal size, ideal for uniform bucket analysis.
public class LinearIterator extends AbstractHistogramIterator {
// Constructor
public LinearIterator(AbstractHistogram histogram, long valueUnitsPerBucket);
// Reset for reuse
void reset(long valueUnitsPerBucket);
void reset(AbstractHistogram histogram, long valueUnitsPerBucket);
}// Create linear iterator with 1000μs (1ms) buckets
LinearIterator iterator = new LinearIterator(histogram, 1000);
System.out.println("Linear Distribution (1ms buckets):");
System.out.println("Range(ms) | Count | Density");
System.out.println("----------|-------|--------");
while (iterator.hasNext()) {
HistogramIterationValue iterationValue = iterator.next();
if (iterationValue.getCountAddedInThisIterationStep() > 0) {
long fromValue = iterationValue.getValueIteratedFrom();
long toValue = iterationValue.getValueIteratedTo();
long count = iterationValue.getCountAddedInThisIterationStep();
System.out.printf("%3d - %3d | %5d | %s%n",
fromValue / 1000, toValue / 1000, count,
"*".repeat((int)(count / 100))); // Simple ASCII histogram
}
}public void analyzeDistributionBuckets(AbstractHistogram histogram, long bucketSize) {
LinearIterator iterator = new LinearIterator(histogram, bucketSize);
List<BucketInfo> buckets = new ArrayList<>();
while (iterator.hasNext()) {
HistogramIterationValue value = iterator.next();
if (value.getCountAddedInThisIterationStep() > 0) {
buckets.add(new BucketInfo(
value.getValueIteratedFrom(),
value.getValueIteratedTo(),
value.getCountAddedInThisIterationStep()
));
}
}
// Find peak bucket
BucketInfo peakBucket = buckets.stream()
.max(Comparator.comparing(b -> b.count))
.orElse(null);
if (peakBucket != null) {
System.out.printf("Peak distribution: %d-%d range with %d samples%n",
peakBucket.fromValue, peakBucket.toValue, peakBucket.count);
}
// Analyze distribution spread
long totalRange = histogram.getMaxValue() - histogram.getMinNonZeroValue();
long activeRanges = buckets.size();
System.out.printf("Distribution spread: %d active ranges out of %d total range%n",
activeRanges, totalRange / bucketSize);
}Iterates through histogram values at logarithmically increasing levels, providing higher resolution at lower values.
public class LogarithmicIterator extends AbstractHistogramIterator {
// Constructor
public LogarithmicIterator(AbstractHistogram histogram,
long valueUnitsInFirstBucket,
double logBase);
// Reset for reuse
void reset(long valueUnitsInFirstBucket, double logBase);
void reset(AbstractHistogram histogram, long valueUnitsInFirstBucket, double logBase);
}// Create logarithmic iterator: first bucket = 10μs, base = 2.0
LogarithmicIterator iterator = new LogarithmicIterator(histogram, 10, 2.0);
System.out.println("Logarithmic Distribution:");
System.out.println("Bucket Range | Count | Cumulative%");
System.out.println("-------------|-------|------------");
long totalCount = histogram.getTotalCount();
while (iterator.hasNext()) {
HistogramIterationValue iterationValue = iterator.next();
if (iterationValue.getCountAddedInThisIterationStep() > 0) {
long fromValue = iterationValue.getValueIteratedFrom();
long toValue = iterationValue.getValueIteratedTo();
long count = iterationValue.getCountAddedInThisIterationStep();
double cumulative = 100.0 * iterationValue.getTotalCountToThisValue() / totalCount;
System.out.printf("%5d - %5d | %5d | %9.2f%%n",
fromValue, toValue, count, cumulative);
}
}Different logarithmic bases provide different resolution patterns:
public void compareLogarithmicBases(AbstractHistogram histogram) {
double[] bases = {1.5, 2.0, 3.0, 10.0};
for (double base : bases) {
LogarithmicIterator iterator = new LogarithmicIterator(histogram, 1, base);
int bucketCount = 0;
while (iterator.hasNext()) {
HistogramIterationValue value = iterator.next();
if (value.getCountAddedInThisIterationStep() > 0) {
bucketCount++;
}
}
System.out.printf("Base %.1f: %d active buckets%n", base, bucketCount);
}
}Iterates through all recorded histogram values using finest granularity steps, including only non-zero counts.
public class RecordedValuesIterator extends AbstractHistogramIterator {
// Constructor
public RecordedValuesIterator(AbstractHistogram histogram);
// Reset for reuse
void reset();
void reset(AbstractHistogram histogram);
}// Iterate through all recorded values
RecordedValuesIterator iterator = new RecordedValuesIterator(histogram);
System.out.println("All Recorded Values:");
System.out.println("Value | Count | Cumulative Count | Percentile");
System.out.println("------|-------|------------------|------------");
while (iterator.hasNext()) {
HistogramIterationValue iterationValue = iterator.next();
long value = iterationValue.getValueIteratedTo();
long count = iterationValue.getCountAtValueIteratedTo();
long totalCount = iterationValue.getTotalCountToThisValue();
double percentile = iterationValue.getPercentileIteratedTo();
System.out.printf("%5d | %5d | %14d | %9.4f%%%n",
value, count, totalCount, percentile);
// Limit output for large histograms
if (totalCount > histogram.getTotalCount() * 0.999) {
System.out.println("... (showing first 99.9% of data)");
break;
}
}public void findExactValues(AbstractHistogram histogram, long... searchValues) {
RecordedValuesIterator iterator = new RecordedValuesIterator(histogram);
Set<Long> searchSet = Arrays.stream(searchValues).boxed().collect(Collectors.toSet());
System.out.println("Exact Value Search:");
while (iterator.hasNext() && !searchSet.isEmpty()) {
HistogramIterationValue value = iterator.next();
long currentValue = value.getValueIteratedTo();
if (searchSet.contains(currentValue)) {
System.out.printf("Value %d: count=%d, percentile=%.4f%%%n",
currentValue,
value.getCountAtValueIteratedTo(),
value.getPercentileIteratedTo());
searchSet.remove(currentValue);
}
}
// Report missing values
if (!searchSet.isEmpty()) {
System.out.println("Values not found: " + searchSet);
}
}Iterates through all possible histogram values using finest granularity, including zero counts.
public class AllValuesIterator extends AbstractHistogramIterator {
// Constructor
public AllValuesIterator(AbstractHistogram histogram);
// Reset for reuse
void reset();
void reset(AbstractHistogram histogram);
}// Analyze value coverage and gaps
AllValuesIterator iterator = new AllValuesIterator(histogram);
long consecutiveZeros = 0;
long maxGap = 0;
long gapStart = 0;
while (iterator.hasNext()) {
HistogramIterationValue iterationValue = iterator.next();
long value = iterationValue.getValueIteratedTo();
long count = iterationValue.getCountAtValueIteratedTo();
if (count == 0) {
if (consecutiveZeros == 0) {
gapStart = value;
}
consecutiveZeros++;
} else {
if (consecutiveZeros > maxGap) {
maxGap = consecutiveZeros;
System.out.printf("Largest gap: %d values (%d - %d)%n",
maxGap, gapStart, gapStart + maxGap - 1);
}
consecutiveZeros = 0;
}
}Container class for histogram iteration results with comprehensive value information.
public class HistogramIterationValue {
// Value information
long getValueIteratedTo();
long getValueIteratedFrom();
// Count information
long getCountAtValueIteratedTo();
long getCountAddedInThisIterationStep();
long getTotalCountToThisValue();
long getTotalValueToThisValue();
// Percentile information
double getPercentile();
double getPercentileLevelIteratedTo();
// Utility methods
String toString();
}public void analyzeIterationValue(HistogramIterationValue value) {
System.out.printf("Iteration Value Analysis:%n");
System.out.printf(" Current value: %d%n", value.getValueIteratedTo());
System.out.printf(" Value range: %d - %d%n",
value.getValueIteratedFrom(), value.getValueIteratedTo());
System.out.printf(" Count at value: %d%n", value.getCountAtValueIteratedTo());
System.out.printf(" Count in step: %d%n", value.getCountAddedInThisIterationStep());
System.out.printf(" Cumulative count: %d%n", value.getTotalCountToThisValue());
System.out.printf(" Cumulative value: %d%n", value.getTotalValueToThisValue());
System.out.printf(" Percentile level: %.4f%%%n", value.getPercentileLevelIteratedTo());
}HdrHistogram provides corresponding iterator types for DoubleHistogram values.
public class DoublePercentileIterator implements Iterator<DoubleHistogramIterationValue> {
public DoublePercentileIterator(DoubleHistogram histogram, int percentileTicksPerHalfDistance);
boolean hasNext();
DoubleHistogramIterationValue next();
void reset(int percentileTicksPerHalfDistance);
}public class DoubleLinearIterator implements Iterator<DoubleHistogramIterationValue> {
public DoubleLinearIterator(DoubleHistogram histogram, double valueUnitsPerBucket);
boolean hasNext();
DoubleHistogramIterationValue next();
void reset(double valueUnitsPerBucket);
}// Analyze double histogram with percentile iterator
DoubleHistogram responseTimeHist = getResponseTimeHistogram();
DoublePercentileIterator iterator = new DoublePercentileIterator(responseTimeHist, 5);
System.out.println("Response Time Percentile Analysis:");
while (iterator.hasNext()) {
DoubleHistogramIterationValue value = iterator.next();
double responseTime = value.getValueIteratedTo();
double percentile = value.getPercentileLevelIteratedTo();
if (percentile >= 50.0) { // Focus on P50+
System.out.printf("P%.1f: %.3f seconds%n", percentile, responseTime);
}
}public class DoubleHistogramIterationValue {
double getValueIteratedTo();
double getValueIteratedFrom();
long getCountAtValueIteratedTo();
long getCountAddedInThisIterationStep();
long getTotalCountToThisValue();
double getPercentileLevelIteratedTo();
}public void compareIteratorTypes(AbstractHistogram histogram) {
// Setup different iterator types
PercentileIterator percentile = new PercentileIterator(histogram, 5);
LinearIterator linear = new LinearIterator(histogram, 1000);
LogarithmicIterator logarithmic = new LogarithmicIterator(histogram, 100, 2.0);
System.out.println("Iterator Type Comparison:");
// Count steps for each iterator type
int percentileSteps = countSteps(percentile);
int linearSteps = countSteps(linear);
int logSteps = countSteps(logarithmic);
System.out.printf("Percentile iterator: %d steps%n", percentileSteps);
System.out.printf("Linear iterator: %d steps%n", linearSteps);
System.out.printf("Logarithmic iterator: %d steps%n", logSteps);
}
private int countSteps(Iterator<HistogramIterationValue> iterator) {
int steps = 0;
while (iterator.hasNext()) {
iterator.next();
steps++;
}
return steps;
}public class HistogramAnalyzer {
public void findOutliers(AbstractHistogram histogram, double outlierThreshold) {
PercentileIterator iterator = new PercentileIterator(histogram, 10);
System.out.printf("Outlier Analysis (threshold: %.1f%%):%n", outlierThreshold);
while (iterator.hasNext()) {
HistogramIterationValue value = iterator.next();
double percentile = value.getPercentileIteratedTo();
if (percentile >= outlierThreshold) {
long latency = value.getValueIteratedTo();
long count = value.getCountAddedInThisIterationStep();
System.out.printf("P%.4f: %d μs (%d samples)%n",
percentile, latency, count);
}
}
}
public void analyzeDistributionShape(AbstractHistogram histogram) {
LinearIterator iterator = new LinearIterator(histogram, 500); // 500μs buckets
List<Long> bucketCounts = new ArrayList<>();
while (iterator.hasNext()) {
HistogramIterationValue value = iterator.next();
bucketCounts.add(value.getCountAddedInThisIterationStep());
}
// Calculate distribution metrics
long maxBucket = Collections.max(bucketCounts);
double mean = bucketCounts.stream().mapToLong(Long::longValue).average().orElse(0);
System.out.printf("Distribution shape analysis:%n");
System.out.printf(" Buckets: %d%n", bucketCounts.size());
System.out.printf(" Peak bucket count: %d%n", maxBucket);
System.out.printf(" Mean bucket count: %.1f%n", mean);
System.out.printf(" Peak-to-mean ratio: %.2f%n", maxBucket / mean);
}
}public class OptimizedIteratorUsage {
// Reuse iterators to minimize object creation
private final PercentileIterator percentileIter;
private final LinearIterator linearIter;
public OptimizedIteratorUsage(AbstractHistogram templateHistogram) {
this.percentileIter = new PercentileIterator(templateHistogram, 5);
this.linearIter = new LinearIterator(templateHistogram, 1000);
}
public void analyzeManyHistograms(List<AbstractHistogram> histograms) {
for (AbstractHistogram histogram : histograms) {
// Reset and reuse existing iterators
percentileIter.reset(histogram, 5);
linearIter.reset(histogram, 1000);
analyzeWithPercentiles(percentileIter);
analyzeWithLinearBuckets(linearIter);
}
}
private void analyzeWithPercentiles(PercentileIterator iterator) {
// Analysis implementation using reused iterator
while (iterator.hasNext()) {
HistogramIterationValue value = iterator.next();
// Process value...
}
}
private void analyzeWithLinearBuckets(LinearIterator iterator) {
// Analysis implementation using reused iterator
while (iterator.hasNext()) {
HistogramIterationValue value = iterator.next();
// Process value...
}
}
}| Analysis Need | Recommended Iterator | Configuration |
|---|---|---|
| SLA compliance | PercentileIterator | percentileTicksPerHalfDistance = 5-10 |
| Distribution shape | LinearIterator | valueUnitsPerBucket = expected_range/100 |
| Scale analysis | LogarithmicIterator | logBase = 2.0 or 10.0 |
| Exact value lookup | RecordedValuesIterator | N/A |
| Gap analysis | AllValuesIterator | N/A (use sparingly) |
| Response time analysis | DoublePercentileIterator | For double histograms |
| Iterator Type | Performance | Memory | Use Case |
|---|---|---|---|
| PercentileIterator | Fast | Low | High-level percentile analysis |
| LinearIterator | Fast | Low | Uniform bucket analysis |
| LogarithmicIterator | Fast | Low | Scale-aware analysis |
| RecordedValuesIterator | Medium | Low | Detailed value exploration |
| AllValuesIterator | Slow | Medium | Complete coverage analysis |
Install with Tessl CLI
npx tessl i tessl/maven-org-hdrhistogram--hdr-histogram