it-swarm-eu.dev

Effizienteste Möglichkeit, einen Map-Wert in Java

Ich hoffe, diese Frage wird für dieses Forum nicht als zu grundlegend angesehen, aber wir werden sehen. Ich frage mich, wie ich Code umgestalten kann, um eine bessere Leistung zu erzielen, die einige Male ausgeführt wird.

Angenommen, ich erstelle eine Worthäufigkeitsliste mit einer Map (wahrscheinlich eine HashMap), in der jeder Schlüssel eine Zeichenfolge mit dem gezählten Wort und der Wert eine Ganzzahl ist, die jedes Mal erhöht wird, wenn ein Token des Wortes gefunden wird.

In Perl ist das Inkrementieren eines solchen Wertes ganz einfach:

$map{$Word}++;

In Java ist es jedoch viel komplizierter. So mache ich es gerade:

int count = map.containsKey(Word) ? map.get(Word) : 0;
map.put(Word, count + 1);

Was natürlich von der Autobox-Funktion in den neueren Java Versionen abhängt. Ich frage mich, ob Sie eine effizientere Methode zur Erhöhung eines solchen Werts vorschlagen können. Gibt es überhaupt gute Gründe für die Leistung, das Collections-Framework zu umgehen und stattdessen etwas anderes zu verwenden?

Update: Ich habe einige der Antworten getestet. Siehe unten.

338
gregory

Einige Testergebnisse

Ich habe viele gute Antworten auf diese Frage bekommen - danke, Leute - also habe ich beschlossen, einige Tests durchzuführen und herauszufinden, welche Methode tatsächlich am schnellsten ist. Die fünf Methoden, die ich getestet habe, sind folgende:

  • die "ContainsKey" -Methode, die ich in die Frage vorgestellt habe
  • die von Aleksandar Dimitrov vorgeschlagene "TestForNull" -Methode
  • die von Hank Gay vorgeschlagene "AtomicLong" -Methode
  • die von jrudolph vorgeschlagene "Trove" -Methode
  • die von phax.myopenid.com vorgeschlagene "MutableInt" -Methode

Methode

Folgendes habe ich getan ...

  1. erstellte fünf Klassen, die bis auf die unten gezeigten Unterschiede identisch waren. Jede Klasse musste eine für das von mir vorgestellte Szenario typische Operation ausführen: Öffnen und Einlesen einer 10-MB-Datei und anschließende Häufigkeitszählung aller Word-Token in der Datei. Da dies durchschnittlich nur 3 Sekunden dauerte, ließ ich die Frequenzzählung (nicht die E/A) 10 Mal durchführen.
  2. zeitlich die Schleife von 10 Iterationen, aber nicht die I/O-Operation und zeichnete die Gesamtzeit (in Sekunden) im Wesentlichen mit Ian Darwins Methode in Java Kochbuch .
  3. führte alle fünf Tests in Serie durch und machte dies dann noch dreimal.
  4. gemittelt die vier Ergebnisse für jede Methode.

Ergebnisse

Ich werde zuerst die Ergebnisse und den folgenden Code für diejenigen, die interessiert sind, präsentieren.

Die ContainsKey -Methode war erwartungsgemäß die langsamste, daher gebe ich die Geschwindigkeit jeder Methode im Vergleich zur Geschwindigkeit dieser Methode an.

  • Enthält Schlüssel: 30,654 Sekunden (Grundlinie)
  • AtomicLong: 29,780 Sekunden (1,03 mal so schnell)
  • TestForNull: 28,804 Sekunden (1,06 mal so schnell)
  • Fundgrube: 26,313 Sekunden (1,16-mal so schnell)
  • MutableInt: 25,747 Sekunden (1,19-mal so schnell)

Schlussfolgerungen

Es scheint, dass nur die MutableInt-Methode und die Trove-Methode erheblich schneller sind, da nur sie eine Leistungssteigerung von mehr als 10% bewirken. Wenn es sich jedoch um ein Threading handelt, ist AtomicLong möglicherweise attraktiver als die anderen (da bin ich mir nicht sicher). Ich habe TestForNull auch mit final Variablen ausgeführt, aber der Unterschied war vernachlässigbar.

Beachten Sie, dass ich die Speichernutzung in den verschiedenen Szenarien nicht profiliert habe. Ich würde mich freuen, von allen zu hören, die gute Einblicke in die Auswirkungen der MutableInt- und Trove-Methoden auf die Speichernutzung haben.

Persönlich finde ich die MutableInt-Methode am attraktivsten, da keine Klassen von Drittanbietern geladen werden müssen. Wenn ich keine Probleme damit entdecke, gehe ich mit größter Wahrscheinlichkeit so vor.

Der Code

Hier ist der entscheidende Code für jede Methode.

EnthältKey

import Java.util.HashMap;
import Java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
int count = freq.containsKey(Word) ? freq.get(Word) : 0;
freq.put(Word, count + 1);

TestForNull

import Java.util.HashMap;
import Java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
Integer count = freq.get(Word);
if (count == null) {
    freq.put(Word, 1);
}
else {
    freq.put(Word, count + 1);
}

AtomicLong

import Java.util.concurrent.ConcurrentHashMap;
import Java.util.concurrent.ConcurrentMap;
import Java.util.concurrent.atomic.AtomicLong;
...
final ConcurrentMap<String, AtomicLong> map = 
    new ConcurrentHashMap<String, AtomicLong>();
...
map.putIfAbsent(Word, new AtomicLong(0));
map.get(Word).incrementAndGet();

Schatz

import gnu.trove.TObjectIntHashMap;
...
TObjectIntHashMap<String> freq = new TObjectIntHashMap<String>();
...
freq.adjustOrPutValue(Word, 1, 1);

MutableInt

import Java.util.HashMap;
import Java.util.Map;
...
class MutableInt {
  int value = 1; // note that we start at 1 since we're counting
  public void increment () { ++value;      }
  public int  get ()       { return value; }
}
...
Map<String, MutableInt> freq = new HashMap<String, MutableInt>();
...
MutableInt count = freq.get(Word);
if (count == null) {
    freq.put(Word, new MutableInt());
}
else {
    count.increment();
}
348
gregory

OK, mag eine alte Frage sein, aber es gibt einen kürzeren Weg mit Java 8:

Map.merge(key, 1, Integer::sum)

Was es tut: Wenn der Schlüssel nicht existiert, geben Sie 1 als Wert ein Andernfalls addieren Sie 1 zu dem Wert, der mit verknüpft ist. Weitere Informationen hier

190
LE GALL Benoît

Eine kleine Recherche im Jahr 2016: https://github.com/leventov/Java-Word-count , Benchmark-Quellcode

Beste Ergebnisse pro Methode (kleiner ist besser):

                 time, ms
kolobokeCompile  18.8
koloboke         19.8
trove            20.8
fastutil         22.7
mutableInt       24.3
atomicInteger    25.3
Eclipse          26.9
hashMap          28.0
hppc             33.6
hppcRt           36.5

Zeit/Raum-Ergebnisse: 

42
leventov

Google Guave ist dein Freund ...

... zumindest in einigen Fällen. Sie haben diese Nice AtomicLongMap . Besonders schön, weil Sie es mit long als Wert in Ihrer Map zu tun haben.

Z.B.

AtomicLongMap<String> map = AtomicLongMap.create();
[...]
map.getAndIncrement(Word);

Es ist auch möglich, dem Wert mehr als 1 hinzuzufügen:

map.getAndAdd(Word, 112L); 
33
H6.

@Hank Gay

Als Follow-up zu meinem eigenen (ziemlich nutzlosen) Kommentar: Trove sieht aus wie der Weg zu gehen. Wenn Sie sich aus irgendeinem Grund an das Standard-JDK halten wollten, können ConcurrentMap und AtomicLong den Code zu einem winzigen etwas schöner, obwohl YMMV.

    final ConcurrentMap<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong>();
    map.putIfAbsent("foo", new AtomicLong(0));
    map.get("foo").incrementAndGet();

wird verlassen 1 als Wert in der Map für foo. Realistisch gesehen ist eine erhöhte Freundlichkeit beim Einfädeln alles, was dieser Ansatz empfehlen muss.

31
Hank Gay

Es ist immer eine gute Idee, in der Google Collections Library nachzuschauen. In diesem Fall reicht ein Multiset aus:

Multiset bag = Multisets.newHashMultiset();
String Word = "foo";
bag.add(Word);
bag.add(Word);
System.out.println(bag.count(Word)); // Prints 2

Es gibt Map-ähnliche Methoden zum Durchlaufen von Schlüsseln/Einträgen usw. Intern verwendet die Implementierung derzeit ein HashMap<E, AtomicInteger>, so dass Ihnen keine Boxkosten entstehen.

25
Chris Nokleberg
Map<String, Integer> map = new HashMap<>();
String key = "a random key";
int count = map.getOrDefault(key, 0);
map.put(key, count + 1);

Und so erhöhen Sie einen Wert mit einfachem Code.

Vorteil:

  • Keine weitere Klasse für mutable int erstellen
  • Kurzcode
  • Einfach zu verstehen
  • Keine Nullzeigerausnahme

Eine andere Möglichkeit ist die Verwendung der Mergemethode. Dies ist jedoch zu viel, um nur einen Wert zu erhöhen.

map.merge(key, 1, (a,b) -> a+b);

Tipp: Sie sollten sich die meiste Zeit mehr um die Lesbarkeit des Codes als um einen geringen Leistungszuwachs kümmern.

21
off99555

Sie sollten sich der Tatsache bewusst sein, dass Ihr ursprünglicher Versuch

int count = map.containsKey (Word)? map.get (Word): 0;

enthält zwei potenziell teure Operationen auf einer Karte, nämlich containsKey und get. Ersteres führt eine Operation aus, die der letzteren möglicherweise ziemlich ähnlich ist, sodass Sie dieselbe Arbeit ausführen zweimal!

Wenn Sie sich die API für Map ansehen, geben get Vorgänge normalerweise null zurück, wenn die Map das angeforderte Element nicht enthält.

Beachten Sie, dass dies zu einer Lösung wie folgt führt

map.put (key, map.get (key) + 1);

gefährlich, da es NullPointerExceptions ergeben könnte. Sie sollten zuerst nach einem null suchen.

Beachten Sie auch, und das ist sehr wichtig, dass HashMaps can per definitionem nulls enthält. Also sagt nicht jeder zurückgegebene null "es gibt kein solches Element". In dieser Hinsicht verhält sich containsKeyanders als get, wenn es Ihnen tatsächlich sagt ob es ein solches Element gibt. Weitere Informationen finden Sie in der API.

In Ihrem Fall möchten Sie jedoch möglicherweise nicht zwischen einem gespeicherten null und "noSuchElement" unterscheiden. Wenn Sie nulls nicht zulassen möchten, bevorzugen Sie möglicherweise Hashtable. Die Verwendung einer Wrapper-Bibliothek, wie sie bereits in anderen Antworten vorgeschlagen wurde, ist möglicherweise eine bessere Lösung für die manuelle Behandlung, abhängig von der Komplexität Ihrer Anwendung.

Um die Antwort zu vervollständigen (und das habe ich dank der Bearbeitungsfunktion zunächst vergessen!), Ist es am besten, get in eine final -Variable zu setzen und zu überprüfen für null und put kehren Sie mit einem 1 zurück. Die Variable sollte final sein, da sie ohnehin unveränderlich ist. Der Compiler benötigt diesen Hinweis möglicherweise nicht, ist aber auf diese Weise klarer.

 final HashMap map = generateRandomHashMap (); 
 final Object key = fetchSomeKey (); 
 final Integer i = map.get (key); 
 if (i ! = null) {
 map.put (i + 1); 
} else {
 // etwas tun 
} 

Wenn Sie sich nicht auf Autoboxing verlassen möchten, sollten Sie stattdessen map.put(new Integer(1 + i.getValue())); sagen.

21

Eine andere Möglichkeit wäre, eine veränderbare Ganzzahl zu erstellen:

class MutableInt {
  int value = 0;
  public void inc () { ++value; }
  public int get () { return value; }
}
...
Map<String,MutableInt> map = new HashMap<String,MutableInt> ();
MutableInt value = map.get (key);
if (value == null) {
  value = new MutableInt ();
  map.put (key, value);
} else {
  value.inc ();
}

dies impliziert natürlich das Erstellen eines zusätzlichen Objekts, aber der Overhead im Vergleich zum Erstellen einer Ganzzahl (auch mit Integer.valueOf) sollte nicht so groß sein.

18
Philip Helger

Sie können die computeIfAbsent -Methode in der Map -Schnittstelle verwenden, die in Java 8 bereitgestellt wird.

final Map<String,AtomicLong> map = new ConcurrentHashMap<>();
map.computeIfAbsent("A", k->new AtomicLong(0)).incrementAndGet();
map.computeIfAbsent("B", k->new AtomicLong(0)).incrementAndGet();
map.computeIfAbsent("A", k->new AtomicLong(0)).incrementAndGet(); //[A=2, B=1]

Die Methode computeIfAbsent prüft, ob der angegebene Schlüssel bereits einem Wert zugeordnet ist oder nicht. Wenn kein zugeordneter Wert vorhanden ist, wird versucht, den Wert mithilfe der angegebenen Zuordnungsfunktion zu berechnen. In jedem Fall wird der aktuelle (vorhandene oder berechnete) Wert zurückgegeben, der dem angegebenen Schlüssel zugeordnet ist, oder null, wenn der berechnete Wert null ist.

In einer Situation, in der mehrere Threads eine gemeinsame Summe aktualisieren, können Sie sich LongAdder class ansehen. Bei hohen Konflikten ist der erwartete Durchsatz dieser Klasse erheblich höher als AtomicLong auf Kosten eines höheren Platzverbrauchs.

10
i_am_zero

Speicherrotation kann hier ein Problem sein, da jedes Boxen eines int größer oder gleich 128 eine Objektzuordnung verursacht (siehe Integer.valueOf (int)). Obwohl der Müllsammler sehr effizient mit kurzlebigen Objekten umgeht, leidet die Leistung in gewissem Maße.

Wenn Sie wissen, dass die Anzahl der vorgenommenen Inkremente die Anzahl der Schlüssel (= Wörter in diesem Fall) weit übersteigt, sollten Sie stattdessen einen int-Halter verwenden. Phax hat dafür bereits Code vorgelegt. Hier ist es wieder, mit zwei Änderungen (Halterklasse statisch gemacht und Anfangswert auf 1 gesetzt):

static class MutableInt {
  int value = 1;
  void inc() { ++value; }
  int get() { return value; }
}
...
Map<String,MutableInt> map = new HashMap<String,MutableInt>();
MutableInt value = map.get(key);
if (value == null) {
  value = new MutableInt();
  map.put(key, value);
} else {
  value.inc();
}

Wenn Sie extreme Leistung benötigen, suchen Sie nach einer Map-Implementierung, die direkt auf primitive Werttypen zugeschnitten ist. Jrudolph erwähnte GNU Trove .

Ein guter Suchbegriff für dieses Thema ist übrigens "Histogramm".

7
volley

Anstatt containKey () aufzurufen, ist es schneller, map.get aufzurufen und zu prüfen, ob der zurückgegebene Wert null ist oder nicht.

    Integer count = map.get(Word);
    if(count == null){
        count = 0;
    }
    map.put(Word, count + 1);
5
Glever

Es gibt einige Ansätze:

  1. Verwenden Sie einen Taschenalorithmus wie die in Google Collections enthaltenen Sätze.

  2. Erstellen Sie einen veränderlichen Container, den Sie in der Map verwenden können:


    class My{
        String Word;
        int count;
    }

Und benutze put ("Word", new My ("Word")); Dann können Sie überprüfen, ob es existiert und beim Hinzufügen erhöhen.

Vermeiden Sie es, Ihre eigene Lösung mithilfe von Listen zu erstellen, da Ihre Leistung stinkt, wenn Sie nach einer inneren Schleife suchen und sortieren. Die erste HashMap-Lösung ist eigentlich recht schnell, aber eine solche Lösung wie in Google Collections ist wahrscheinlich besser.

Das Zählen von Wörtern mit Google Collections sieht ungefähr so ​​aus:



    HashMultiset s = new HashMultiset();
    s.add("Word");
    s.add("Word");
    System.out.println(""+s.count("Word") );

Die Verwendung des HashMultisets ist sehr elegant, da ein Taschenalgorithmus genau das ist, was Sie zum Zählen von Wörtern benötigen.

3
tovare

Google Collections HashMultiset:
- sehr elegant zu bedienen
- verbrauchen aber CPU und Speicher

Am besten wäre eine Methode wie: Entry<K,V> getOrPut(K); (elegant und kostengünstig)

Eine solche Methode berechnet Hash und Index nur einmal, und dann können wir mit dem Eintrag das tun, was wir wollen (entweder den Wert ersetzen oder aktualisieren).

Eleganter:
- nimm einen HashSet<Entry>
- erweitern Sie es so, dass get(K) bei Bedarf einen neuen Eintrag erstellt
- Eintrag kann Ihr eigenes Objekt sein.
-> (new MyHashSet()).get(k).increment();

3
the felis leo

Eine noch schnellere Variante des MutableInt-Ansatzes ist die Verwendung eines Int-Arrays mit einem einzelnen Element:

Map<String,int[]> map = new HashMap<String,int[]>();
...
int[] value = map.get(key);
if (value == null) 
  map.put(key, new int[]{1} );
else
  ++value[0];

Es wäre interessant, wenn Sie Ihre Leistungstests mit dieser Variante wiederholen könnten. Es könnte das schnellste sein.


Bearbeiten: Das obige Muster hat für mich gut funktioniert, aber schließlich habe ich Troves Sammlungen verwendet, um die Speichergröße in einigen sehr großen Karten, die ich erstellt habe, zu reduzieren - und als Bonus war es auch schneller.

Ein wirklich nettes Feature ist, dass die TObjectIntHashMap -Klasse einen einzelnen adjustOrPutValue -Aufruf hat, der, abhängig davon, ob sich bereits ein Wert an diesem Schlüssel befindet, entweder einen Anfangswert setzt oder den vorhandenen Wert erhöht. Dies ist perfekt für das Inkrementieren:

TObjectIntHashMap<String> map = new TObjectIntHashMap<String>();
...
map.adjustOrPutValue(key, 1, 1);

Ich denke, Ihre Lösung wäre der Standardweg, aber - wie Sie selbst bemerkt haben - ist es wahrscheinlich nicht der schnellste Weg.

Sie können sich GNU Trove ansehen. Das ist eine Bibliothek, die alle Arten von schnellen primitiven Sammlungen enthält. Ihr Beispiel würde ein TObjectIntHashMap verwenden, das eine Methode adjustOrPutValue hat, die genau das tut, was Sie wollen.

3
jrudolph

Sind Sie sicher, dass dies ein Engpass ist? Haben Sie eine Leistungsanalyse durchgeführt?

Versuchen Sie es mit dem NetBeans-Profiler (der kostenlos und in NB 6.1) integriert ist), um sich Hotspots anzusehen.

Schließlich ist ein JVM-Upgrade (z. B. von 1,5 auf 1,6) oft ein billiger Leistungsschub. Sogar ein Upgrade der Build-Nummer kann eine gute Leistungssteigerung bewirken. Wenn Sie unter Windows arbeiten und es sich um eine Anwendung der Serverklasse handelt, verwenden Sie -server in der Befehlszeile, um die Server-Hotspot-JVM zu verwenden. Auf Linux- und Solaris-Computern wird dies automatisch erkannt.

3
John Wright

Ganz einfach, verwenden Sie einfach die integrierte Funktion in Map.Java wie folgt

map.put(key, map.getOrDefault(key, 0) + 1);
2
sudoz

"put" muss "get" sein (um sicherzustellen, dass kein Schlüssel doppelt vorkommt).
Machen Sie also direkt ein "Put",
und wenn es einen vorherigen Wert gab, dann füge hinzu:

Map map = new HashMap ();

MutableInt newValue = new MutableInt (1); // default = inc
MutableInt oldValue = map.put (key, newValue);
if (oldValue != null) {
  newValue.add(oldValue); // old + inc
}

Wenn die Zählung bei 0 beginnt, addieren Sie 1: (oder andere Werte ...)

Map map = new HashMap ();

MutableInt newValue = new MutableInt (0); // default
MutableInt oldValue = map.put (key, newValue);
if (oldValue != null) {
  newValue.setValue(oldValue + 1); // old + inc
}

Hinweis: Dieser Code ist nicht threadsicher. Verwenden Sie es zum Erstellen und dann zum Verwenden der Karte, um sie nicht gleichzeitig zu aktualisieren.

Optimierung: Behalten Sie in einer Schleife den alten Wert bei, um zum neuen Wert der nächsten Schleife zu werden.

Map map = new HashMap ();
final int defaut = 0;
final int inc = 1;

MutableInt oldValue = new MutableInt (default);
while(true) {
  MutableInt newValue = oldValue;

  oldValue = map.put (key, newValue); // insert or...
  if (oldValue != null) {
    newValue.setValue(oldValue + inc); // ...update

    oldValue.setValue(default); // reuse
  } else
    oldValue = new MutableInt (default); // renew
  }
}
2
the felis leo

Wenn Sie Eclipse Collections verwenden, können Sie HashBag verwenden. Dies ist der effizienteste Ansatz in Bezug auf die Speichernutzung und bietet eine gute Leistung in Bezug auf die Ausführungsgeschwindigkeit.

HashBag wird von einem MutableObjectIntMap unterstützt, in dem primitive Ints anstelle von Counter Objekten gespeichert werden. Dies reduziert den Speicheraufwand und verbessert die Ausführungsgeschwindigkeit.

HashBag stellt die API bereit, die Sie benötigen, da es sich um eine Collection handelt, mit der Sie auch die Anzahl der Vorkommen eines Elements abfragen können.

Hier ist ein Beispiel aus der Eclipse Collections Kata .

MutableBag<String> bag =
  HashBag.newBagWith("one", "two", "two", "three", "three", "three");

Assert.assertEquals(3, bag.occurrencesOf("three"));

bag.add("one");
Assert.assertEquals(2, bag.occurrencesOf("one"));

bag.addOccurrences("one", 4);
Assert.assertEquals(6, bag.occurrencesOf("one"));

Hinweis: Ich bin ein Committer für Eclipse-Sammlungen.

1
Craig P. Motlin

Ich würde Apache Collections Lazy Map verwenden (um Werte auf 0 zu initialisieren) und MutableIntegers von Apache Lang als Werte in dieser Map verwenden.

Die größten Kosten entstehen durch zweimaliges Durchsuchen der Karte in Ihrer Methode. Bei mir muss man das nur einmal machen. Holen Sie sich einfach den Wert (der bei Abwesenheit initialisiert wird) und erhöhen Sie ihn.

1
jb.

Die TreeMap-Datenstruktur der Functional Java -Bibliothek hat im neuesten Trunk-Head eine update -Methode:

public TreeMap<K, V> update(final K k, final F<V, V> f)

Anwendungsbeispiel:

import static fj.data.TreeMap.empty;
import static fj.function.Integers.add;
import static fj.pre.Ord.stringOrd;
import fj.data.TreeMap;

public class TreeMap_Update
  {public static void main(String[] a)
    {TreeMap<String, Integer> map = empty(stringOrd);
     map = map.set("foo", 1);
     map = map.update("foo", add.f(1));
     System.out.println(map.get("foo").some());}}

Dieses Programm gibt "2" aus.

1
Apocalisp

Ich weiß nicht, wie effizient es ist, aber der folgende Code funktioniert auch. Sie müssen am Anfang ein BiFunction definieren. Außerdem können Sie mit dieser Methode mehr als nur eine Erhöhung vornehmen.

public static Map<String, Integer> strInt = new HashMap<String, Integer>();

public static void main(String[] args) {
    BiFunction<Integer, Integer, Integer> bi = (x,y) -> {
        if(x == null)
            return y;
        return x+y;
    };
    strInt.put("abc", 0);


    strInt.merge("abc", 1, bi);
    strInt.merge("abc", 1, bi);
    strInt.merge("abc", 1, bi);
    strInt.merge("abcd", 1, bi);

    System.out.println(strInt.get("abc"));
    System.out.println(strInt.get("abcd"));
}

ausgabe ist

3
1
1
MGoksu

Die verschiedenen primitiven Wrapper, z. B. Integer, sind unveränderlich, sodass es wirklich keine präzisere Möglichkeit gibt, das zu tun, was Sie verlangen , außer Sie kann es mit etwas wie AtomicLong tun. Ich kann das in einer Minute ausprobieren und aktualisieren. Übrigens, Hashtable ist ein Teil des Collections Framework .

1
Hank Gay

@ Vilmantas Baranauskas: Zu dieser Antwort würde ich einen Kommentar abgeben, wenn ich die Wiederholungspunkte hätte, aber das tue ich nicht. Ich wollte beachten, dass die dort definierte Counter-Klasse NICHT thread-sicher ist, da es nicht ausreicht, nur inc () zu synchronisieren, ohne value () zu synchronisieren. Anderen Threads, die value () aufrufen, wird der Wert nicht garantiert angezeigt, es sei denn, mit der Aktualisierung wurde eine Beziehung hergestellt, die vor dem Auftreten einer Störung besteht.

1
Alex Miller