This commit is contained in:
ngb
2022-07-27 13:56:58 +02:00
parent 250d9d17d3
commit fea1083926
2 changed files with 209 additions and 19 deletions

View File

@@ -4,33 +4,101 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/**
* Eine Hilfsklasse um Dinge zu zählen.
* <p>
* Im einfachsten Fall kann der Zähler als geteilte Zählvariable genutzt werden,
* die mit {@link #inc()} und {@link #dec()} aus verschiedenen Objekten oder
* Methoden verändert werden kann.
* <p>
* Der Zähler kann aber auch Objekte zählen, indem die Instanzen an
* {@link #count(Object)} übergeben werden. Am Ende kann mit {@link #getCount()}
* die Anzahl der Objkete abgerufen werden.
* <p>
* Handelt es sich bei den Objekten um Zahlen, dann merkt sich ein Zähler auch
* das Maximum, das Minimum, die Summe und den Durchschnitt der gezählten
* Werte.
* <p>
* Ein Zähler kann auch komplette Arrays oder Listen von Zahlen zählen und die
* obigen Statistiken auswerten.
*/
@SuppressWarnings( "unused" ) @SuppressWarnings( "unused" )
public final class Counter { public final class Counter {
/**
* Erstellt einen neuen {@code Counter}, der alle Integer im angegebenen
* Array gezählt hat.
*
* @param values Die zu zählenden Werte.
* @return Ein neuer {@code Counter}.
*/
public static Counter fromArray( int[] values ) {
return new Counter().countAll(values);
}
/**
* Erstellt einen neuen {@code Counter}, der alle Doubles im angegebenen
* Array gezählt hat.
*
* @param values Die zu zählenden Werte.
* @return Ein neuer {@code Counter}.
*/
public static Counter fromArray( double[] values ) { public static Counter fromArray( double[] values ) {
return new Counter().countAll(values); return new Counter().countAll(values);
} }
/**
* Erstellt einen neuen {@code Counter}, der alle Zahlen im angegebenen
* Array gezählt hat.
*
* @param values Die zu zählenden Werte.
* @return Ein neuer {@code Counter}.
*/
public static Counter fromArray( Number[] values ) { public static Counter fromArray( Number[] values ) {
return new Counter().countAll(values); return new Counter().countAll(values);
} }
/**
* Erstellt einen neuen {@code Counter}, der alle Zahlen in der angegebenen
* Liste gezählt hat.
*
* @param values Die zu zählenden Werte.
* @return Ein neuer {@code Counter}.
*/
public static Counter fromList( List<Number> values ) { public static Counter fromList( List<Number> values ) {
return new Counter().countAll(values); return new Counter().countAll(values);
} }
private AtomicInteger count = new AtomicInteger(0); /**
* Aktuelle Anzahl gezählter Werte.
*/
private final AtomicInteger count = new AtomicInteger(0);
/**
* Statistiken zu den gezählten Werten.
*/
private double min = Double.NaN, max = Double.NaN, sum = Double.NaN; private double min = Double.NaN, max = Double.NaN, sum = Double.NaN;
public void Counter() { /**
* Erstellt einen neuen, leeren {@code Counter}.
*/
public Counter() {
} }
public void Counter( int initial ) { /**
* Ertstellt einen neuen {@code Counter}, der mit dem angegebenen Wert
* initialisiert ist.
*
* @param initial Wert des Zählers zu Beginn.
*/
public Counter( int initial ) {
count.set(initial); count.set(initial);
} }
/**
* Gibt die aktuelle Anzahl zurück.
* @return Die aktuelle Anzahl.
*/
public int getCount() { public int getCount() {
return count.get(); return count.get();
} }
@@ -61,6 +129,8 @@ public final class Counter {
} }
} }
@SuppressWarnings( "UnusedReturnValue" )
public Counter setCount( int count ) { public Counter setCount( int count ) {
this.count.set(count); this.count.set(count);
return this; return this;
@@ -94,10 +164,10 @@ public final class Counter {
inc(); inc();
// Update stats // Update stats
synchronized( count ) { synchronized( count ) {
sum = Double.isNaN(sum) ? value : sum + value; sum = Double.isNaN(sum) ? value : sum + value;
if( Double.isNaN(max) || max < value ) if( Double.isNaN(max) || max < value )
max = value; max = value;
if( Double.isNaN(min) ||min > value ) if( Double.isNaN(min) || min > value )
min = value; min = value;
} }
return this; return this;
@@ -120,7 +190,15 @@ public final class Counter {
@SuppressWarnings( "UnusedReturnValue" ) @SuppressWarnings( "UnusedReturnValue" )
public synchronized Counter countAll( double[] values ) { public synchronized Counter countAll( double[] values ) {
for( double value: values ) { for( double value : values ) {
count(value);
}
return this;
}
@SuppressWarnings( "UnusedReturnValue" )
public synchronized Counter countAll( int[] values ) {
for( double value : values ) {
count(value); count(value);
} }
return this; return this;
@@ -128,7 +206,7 @@ public final class Counter {
@SuppressWarnings( "UnusedReturnValue" ) @SuppressWarnings( "UnusedReturnValue" )
public synchronized Counter countAll( Number[] values ) { public synchronized Counter countAll( Number[] values ) {
for( Number value: values ) { for( Number value : values ) {
count(value); count(value);
} }
return this; return this;
@@ -136,7 +214,7 @@ public final class Counter {
@SuppressWarnings( "UnusedReturnValue" ) @SuppressWarnings( "UnusedReturnValue" )
public synchronized Counter countAll( Collection<Number> values ) { public synchronized Counter countAll( Collection<Number> values ) {
for( Number value: values ) { for( Number value : values ) {
count(value); count(value);
} }
return this; return this;

View File

@@ -2,77 +2,189 @@ package schule.ngb.zm.util;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/**
* Eine Hilfsklasse zur Zeitmessung im Nanosekundenbereich.
* <p>
* Die Klasse kann zum Beispiel genutzt werden, um die Laufzeit eines
* Quelltextes zu messen. Wie eine echte Stoppuhr läuft der {@code Timer}
* weiter, wenn nach einem {@link #stop()} wieder {@link #start()} aufgerufen
* wird. Soll die Zeitmessung wieder bei null beginnen, muss vorher
* {@link #reset()} genutzt werden.
* <p>
* Die gemessene Zeit kann in {@link #getMillis() Millisekunden} oder
* {@link #getSeconds() Sekunden} abgerufen werden. Wird eine noch größere
* Genauigkeit benötigt, können mit {@link #getTime(TimeUnit)} beliebige
* Zeiteinheiten (zum Beispiel {@link TimeUnit#NANOSECONDS Nanosekunden})
* abgerufen werden.
* <p>
* Die Zeit kann auch bei laufender Uhr abgefragt werden. In dem Fall wird die
* bis zu diesem Zeitpunkt gemessene Zeit zurückgegeben.
*/
@SuppressWarnings( "unused" ) @SuppressWarnings( "unused" )
public final class Timer { public final class Timer {
/**
* Die Basiseinheit für die Zeitmessung.
*/
private final TimeUnit baseUnit; private final TimeUnit baseUnit;
/**
* Ob die Zeitmessung gerade läuft.
*/
private boolean running = false; private boolean running = false;
private long starttime = -1; /**
* Startzeit der Zeitmessung. -1, wenn noch keine Messung gestartet wurde.
*/
private long start = -1;
/**
* Zeit, die bisher bei allen Zeitmessungen, die gestoppt wurden, insgesamt
* vergangen ist.
*/
private long elapsed = 0; private long elapsed = 0;
/**
* Erstellt einen neuen {@code Timer} mit Millisekunden als Basiseinheit.
*/
public Timer() { public Timer() {
this(TimeUnit.MILLISECONDS); this(TimeUnit.MILLISECONDS);
} }
/**
* Erstellt einen {@code Timer}, der die angegebene Einheit als Basiseinheit
* für {@link #getTime()} benutzt.
* <p>
* Um eine Zeitmessung in Nanosekunden durchzuführen, kann der {@code Timer}
* beispielsweise so instanziiert werden:
* <pre><code>
* Timer clock = new Timer(TimeUnit.NANOSECONDS);
* </code></pre>
*
* @param baseUnit Die Basiseinheit für die Zeitmessung.
*/
public Timer( TimeUnit baseUnit ) { public Timer( TimeUnit baseUnit ) {
this.baseUnit = baseUnit; this.baseUnit = baseUnit;
} }
/**
* Ob die Zeitmessung gerade läuft.
*
* @return {@code true}, wenn die Zeitmessung mit {@link #start()} gestartet
* wurde.
*/
public boolean isRunning() { public boolean isRunning() {
return running; return running;
} }
/**
* Startet die Zeitmessung.
* <p>
* Wenn zuvor schon eine Zeitmessung gestartet wurde, wird die neue Messung
* zur Summe aller Messungen hinzuaddiert. Soll die Messung bei null
* starten, muss vorher {@link #reset()} verwendet werden:
*
* <pre><code>
* // Timer auf null stellen und sofort starten
* timer.reset().start();
* </code></pre>
*
* @return Dieser {@code Timer} selbst (method chaining).
*/
@SuppressWarnings( "UnusedReturnValue" ) @SuppressWarnings( "UnusedReturnValue" )
public Timer start() { public Timer start() {
starttime = System.nanoTime(); start = System.nanoTime();
running = true; running = true;
return this; return this;
} }
/**
* Stoppt den {@code Timer}, wenn er derzeit läuft. Die gemessene Zeit wird
* zur Summe aller gemessenen Zeiten hinzuaddiert.
*
* @return Dieser {@code Timer} selbst (method chaining).
*/
@SuppressWarnings( "UnusedReturnValue" ) @SuppressWarnings( "UnusedReturnValue" )
public Timer stop() { public Timer stop() {
running = false; if( running ) {
elapsed += System.nanoTime() - starttime; running = false;
elapsed += System.nanoTime() - start;
}
return this; return this;
} }
/**
* Setzt den {@code Timer} auf den Startzustand und löscht alle bisher
* gemessenen Zeiten. Falls die Zeitmessung gerade läuft, wird sie nicht
* gestoppt, sondern läuft vom Zeitpunkt des Aufrufs weiter.
*
* @return Dieser {@code Timer} selbst (method chaining).
*/
@SuppressWarnings( "UnusedReturnValue" ) @SuppressWarnings( "UnusedReturnValue" )
public Timer reset() { public Timer reset() {
running = false;
starttime = -1;
elapsed = 0; elapsed = 0;
start = -1;
if( running ) {
start = System.nanoTime();
}
return this; return this;
} }
/**
* Gibt die Zeit in der Basiseinheit zurück.
*
* @return Die bisher insgesamt gemessene Zeit.
*/
public long getTime() { public long getTime() {
return getTime(baseUnit); return getTime(baseUnit);
} }
/**
* Gibt die Zeit in der angegebenen Einheit zurück.
* <p>
* Größere Zeiteinheiten werden gerundet und verlieren daher an Genauigkeit.
* Eine Zeitmessung von 999 Millisekunden wird als 0 Sekunden
* zurückgegeben.
* <p>
* Um genauere Ergebnisse zu erhalten, kann mit {@link #getMillis()} und
* {@link #getSeconds()} die gemessene Zeit in Minuten beziehungsweise
* Sekunden als Kommazahl abgefragt werden.
*
* @param unit Zeiteinheit
* @return Die bisher insgesamt gemessene Zeit in der gewählten Zeiteinheit.
*/
public long getTime( TimeUnit unit ) { public long getTime( TimeUnit unit ) {
if( running ) { if( running ) {
return unit.convert(System.nanoTime() - starttime + elapsed, TimeUnit.NANOSECONDS); return unit.convert(System.nanoTime() - start + elapsed, TimeUnit.NANOSECONDS);
} else { } else {
return unit.convert(elapsed, TimeUnit.NANOSECONDS); return unit.convert(elapsed, TimeUnit.NANOSECONDS);
} }
} }
/**
* Gibt die gemessene Zeit in Millisekunden (gerundet) zurück.
*
* @return Die gemessene Zeit in ms.
*/
public int getMillis() { public int getMillis() {
if( running ) { if( running ) {
return (int) ((System.nanoTime() - starttime + elapsed) / 1000000); return (int) ((System.nanoTime() - start + elapsed) / 1000000);
} else { } else {
return (int) (elapsed / 1000000); return (int) (elapsed / 1000000);
} }
} }
/**
* Gibt die gemessene Zeit in Sekunden zurück.
*
* @return Die gemessene Zeit in s.
*/
public double getSeconds() { public double getSeconds() {
if( running ) { if( running ) {
return (System.nanoTime() - starttime + elapsed) / 1000000000.0; return (System.nanoTime() - start + elapsed) / 1000000000.0;
} else { } else {
return elapsed / 1000000000.0; return elapsed / 1000000000.0;
} }