counter

Counter: iterators whose values only increase or only decrease

Interfaces for counters: Iterators whose values only increase or only decrease, over any Comparable type, with no dependencies beyond the JDK.

Rationale

Why. Ticket numbers, attempt counts, version stamps and clocks all issue values in one direction. Code that consumes them needs that direction, not a particular implementation.

Problem. A plain Iterator<T> or Supplier<T> says nothing about order, so a caller cannot tell from the type whether the next value can repeat or go back.

Solution. The direction is a type. Accept an Increasing.Strict<T> where values must never repeat, or an Increasing<T> where they may repeat but never fall, and the caller may supply any implementation.

Key Concepts

Counter

Role. An Iterator<T> over Comparable values. hasNext() is true by default, for a counter with no last value; a counter with one overrides it. It is sealed: every counter is Increasing or Decreasing.

Increasing

Role. Each next() is greater than or equal to the one before. Increasing.Strict requires greater, so no value repeats.

Decreasing

Role. Each next() is less than or equal to the one before. Decreasing.Strict requires less, so no value repeats.

How It Works

Guarantees. The interfaces declare an order but cannot enforce it; each implementation must keep it. next() is the one abstract method, so a lambda or method reference can implement Increasing, Decreasing or their Strict subtypes.

Thread safety. Thread safety is up to each implementation.

Lane

Purpose. Typing a source of values that only increase or only decrease.

When to avoid. Values that may move in either direction: use Iterator or Stream.

Usage

A strictly increasing counter from a method reference

import java.util.concurrent.atomic.AtomicLong;
import software.visionary.counter.Increasing;

var issued = new AtomicLong();
Increasing.Strict<Long> tickets = issued::getAndIncrement;
System.out.println(tickets.next());
System.out.println(tickets.next());
System.out.println(tickets.hasNext());

Output

0
1
true

Dependencies

  • software.visionary:junit-closure:2026.09.17 at Testing

Caveats

  • On a counter with no last value, forEachRemaining never returns, and neither does a stream over it unless it stops early, as limit, takeWhile or findFirst do.

Views

Drawn from the release. A link in a view opens its source at the release tag.

architecture.svg
architecture.svg
classes.svg
classes.svg
packages.svg
packages.svg