Boolesche Rückgabe von set.add () in if conditional?

15

Der Operator add der Klasse set gibt einen Booleschen Wert zurück, der true ist, wenn das Element (das hinzugefügt werden soll) noch nicht vorhanden ist, andernfalls false. Schreibt

if (set.add(entry)) {
    //do some more stuff
}

als guter Stil in Bezug auf das Schreiben von sauberem Code? Ich frage mich, da Sie zwei Dinge gleichzeitig tun. 1) Hinzufügen des Elements und 2) Überprüfen, ob das Element vorhanden ist.

Andreas Braun
quelle
6
Sie sprechen über den Standard java.util.Set, der wahr ist, addwenn das Element noch nicht da war, oder?
user2357112 unterstützt Monica
1
Normalerweise würde ich den gegenteiligen Test in Betracht ziehen:if (!set.add(entry)) {// entry already present, possibly a case you want to handle}
njzk2
Ist etwas falsch daran, zwei Dinge gleichzeitig zu tun?
user207421
@ user2357112 ja, das stimmt.
Andreas Braun

Antworten:

19

Ja ist es.

Wenn eine Operation einen Booleschen Wert zurückgibt, können Sie ihn normalerweise verwenden, um Entscheidungen zu treffen, z if Konstrukts. Die einzige andere Möglichkeit, dieses Potenzial zu realisieren, besteht darin, den Rückgabewert in einer Variablen zu speichern und ihn dann sofort in einer wiederzuverwenden if, was einfach albern und dem Schreiben keineswegs vorzuziehen ist if(operation()) { ... }. Also mach einfach weiter, wir werden dich nicht beurteilen.

Kilian Foth
quelle
Danke für deine Antwort. Wie @Niels vanReijmersdal in seiner Antwort ausführt, unterbricht dies die Trennung von Befehl und Abfrage, nicht wahr? Denkst du nicht, dass das wichtig ist?
Andreas Braun
4
@AndreasBraun Ich persönlich finde die Trennung von Befehlsabfragen doof. Oft ist es logisch, dass eine Methode eine Aktion ausführt und einen Wert zurückgibt, der sich auf diese Aktion bezieht. Das Erzwingen einer künstlichen Trennung zwischen den beiden führt zu unnötig ausführlichem Code.
1
@ Dan1111: in der Tat. Darüber hinaus führt "Trennung von Befehlen und Abfragen" zu "check then act" und ähnlichen Anti-Mustern, die in Multithread-Kontexten brechen können. Es ist ziemlich seltsam, für eine solche Trennung zu werben, wenn der Rest der Welt gleichzeitig versucht, atomare Fusionsoperationen für robuste und effiziente SMP-Lösungen aufzubauen…
Holger,
2
@ Jörg W Mittag: Seite 759 von was? Da eine atomare Operation an sich immun gegen das Anti-Pattern "check then act" ist, ist es nicht vorteilhaft, es aufzuteilen, um ein "gesamtes Kapitel" zu lesen, in dem herausgefunden wird, wie der Konstrukt-Thread sicher gemacht werden kann nochmal. Da Sie keine konkreten Argumente genannt haben, habe ich keine „spezifischen Einwände gegen diese Argumente“.
Holger
1
@ Jörg W Mittag: Nun, ich spreche über die Antwort auf dieser Seite und rate davon ab, Set.addals einzelne Operation zu verwenden, ohne eine Alternative zu nennen. Wenn die beabsichtigte Operation darin besteht, ein einzelnes Element hinzuzufügen und herauszufinden, ob es hinzugefügt wurde, besteht keine Notwendigkeit für eine leistungsfähigere Alternative, die die Operation immer noch ohne Nutzen verkompliziert. Ich hoffe, Sie wissen, dass Set.addes sich um eine integrierte JRE-Methode handelt, die verwendet werden kann, ohne zuvor ein Buch mit mehr als 700 Seiten zu studieren.
Holger
12

Ich würde sagen, es ist nicht so sauber wie möglich, weil es den Betreuer zwingt, entweder bereits zu wissen oder nachzuschlagen, was der Rückgabewert bedeutet. Bedeutet dies, dass der Wert bereits vorhanden war, noch nicht vorhanden war und erfolgreich eingefügt wurde? Wenn Sie es nicht oft benutzen, werden Sie es nicht wissen, und selbst wenn Sie es tun, ist es eine viel größere mentale Belastung.

Ich würde folgendes bevorzugen:

boolean added = set.add(entry);

if (added) {
    //do some more stuff
}

Ja, ein bisschen ausführlicher, aber der Compiler sollte so ziemlich den gleichen Bytecode generieren, und selbst Leute, die seit Jahren keine Java-Sets mehr verwendet haben, können der Logik folgen, ohne etwas nachzuschlagen.

Karl Bielefeldt
quelle
2
addGibt true zurück, wenn das Element noch nicht vorhanden war. (Der Wortlaut der Frage ist irreführend.)
user2357112 unterstützt Monica am
9
Das Nachschlagen einer Methode ist weniger belastend als der Versuch, die Absicht des ursprünglichen Entwicklers für eine redundante Variable zu verstehen, die außerhalb des Gültigkeitsbereichs deklariert wurde, in dem sie verwendet wird. In allen modernen Java-IDEs kann ein Entwickler den Mauszeiger über eine Methode bewegen und sofort auf deren Dokumentation zugreifen. Gute Entwickler tun dies ständig, wenn sie mit einer unbekannten API arbeiten.
Kevin Krumwiede
9
Ich zweiter @Holger. Wenn Sie es nicht wissen Set.add, sind Sie unerfahren und sollten diese Methoden ausprobieren, um sie zu erlernen und erfahrener zu werden.
Setzen Sie Monica am
7
notAlreadyPresentist nicht die beste Formulierung. Ich würde verwenden addedund erwarten, dass der Leser weiß, warum ein Wert nicht zu einem Set hinzugefügt wird.
njzk2
3
@JustinTime: Die Verwendung von Kommentaren zur Beschreibung der Code-Funktionen wird allgemein als schlechte Vorgehensweise angesehen. Ihr Code sollte sich selbst beschreiben. In einer Codeüberprüfung würde ich Ihr Beispiel als inakzeptabel kennzeichnen.
BlueRaja - Danny Pflughoeft
8

Wenn wahr Erfolg bedeutet, dann ist es guter, klarer Code.

Es gibt eine weit verbreitete Konvention, dass eine Funktion oder Methode bei Erfolg true (oder etwas, das als true ausgewertet wird) zurückgibt. Solange Ihr Code dem folgt, denke ich, dass es in Ordnung ist, die Methode in die Bedingung zu setzen.

Code wie dieser ist aus meiner Sicht unnötig unübersichtlich:

boolean frobulate_succeeded = thing.frobulate();

if (frobulate_succeeded) {
    ...
}

Es fühlt sich an, als würdest du dich wiederholen.

Die Frage ist jedoch zweideutig in Bezug auf die Bedeutung des Rückgabewerts. Sie sagen "ein Boolescher Wert, der angibt, ob das hinzugefügte Element bereits vorhanden war". Dies könnte bedeuten, dass true bedeutet, dass das Element vorhanden war (und add nicht aufgetreten ist). In diesem Fall würde ich das Rückgabeverhalten der Methode im Idealfall konventioneller gestalten. Wenn dies nicht möglich ist, würde ich eine zusätzliche Zwischenvariable hinzufügen, mit der Sie das Rückgabeergebnis in Ihrem Code eindeutig kennzeichnen können (wie von anderen vorgeschlagen).


quelle
Was bedeutet Erfolg im Zusammenhang mit dem Hinzufügen eines Gegenstands zu einem Set? Keine Ausnahme zu machen, bedeutet Erfolg. true vs. false bedeutet in diesem Zusammenhang etwas Spezifischeres.
Setzen Sie Monica am
@ Solomonoff'sSecret In diesem Fall bedeutet eine Ausnahme, dass die Menge das Hinzufügen des Elements verweigert hat, falsedass das Element nicht hinzugefügt wurde, weil es bereits enthalten war, und truedass das Element noch nicht enthalten war. As add()fügt das Element der Menge hinzu, wenn die Menge es noch nicht enthält. trueDies bedeutet, dass das Element erfolgreich zur Menge hinzugefügt wurde.
Justin Time - Reinstate Monica
@JustinTime Sie addieren Ihre eigenen Werturteile zu den beiden Fällen. Eine andere Interpretation ist, dass der Erfolg darin besteht, dass sich das Element im Set befindet, wenn die Methode zurückgegeben wird. Wenn der Artikel bereits im Set war, addgelingt dies, ohne dass etwas getan werden muss. Wenn das Element noch nicht im Set enthalten war, wird addes durch Hinzufügen des Elements zum Set erfolgreich abgeschlossen. Welche Interpretation richtig ist, ist willkürlich. Die weniger willkürliche Definition von Erfolg stammt aus der Sprache: Die Methode ist erfolgreich, wenn sie normal zurückgibt.
Setzen Sie Monica am
1
Die am wenigsten willkürliche Definition für Erfolg ist, dass die Methode die von ihr geplante Aufgabe erfolgreich ausgeführt hat. Wenn ich eine Methode habe firstLessThanSecond(int l, int r)und sie zurückgibt, truewenn l > roder falsewenn l <= r, dann ist diese Methode nicht erfolgreich, obwohl sie normal zurückgibt.
Justin Time - Reinstate Monica
1
@JustinTime addist eine strenge Abkürzung des Vertrages. Die Dokumentation beginnt mit "Fügt das angegebene Element zu dieser Gruppe hinzu, falls es noch nicht vorhanden ist". Dies ist zufriedenstellend, unabhängig davon, ob das Element bereits in der Gruppe enthalten war. Sie können den Rückgabewert frei interpretieren, aber letztendlich ist es nur Ihre Interpretation. In Ihrem zweiten Beispiel wertet die Methode aus, ob eine Bedingung erfüllt ist. Wenn die Bedingung falsch ist, ist die Methode sicherlich nicht fehlgeschlagen, da sie die Bedingung erfolgreich ausgewertet hat.
Setzen Sie Monica am
2

Ich würde sagen, es ist sehr C-like. Die meiste Zeit würde ich es vorziehen, eine beschreibend benannte Variable für ein Mutationsergebnis zu haben, und keine Mutation, die in einer auftrittif Bedingung auftritt.

Ein Compiler entfernt diese Variable, wenn sie sofort wiederverwendet wird. Ein Mensch wird es leichter haben, die Quelle zu lesen. Für mich ist es wichtiger.

Sollte jemand die Bedingung durch Hinzufügen einer and/ or-Klausel zur if-Bedingung erweitern müssen, kann dies dazu führen, dass er .add()in bestimmten Fällen aufgrund einer Kurzschlussbewertung nicht anruft . Wenn nicht ausdrücklich ein Kurzschluss erwartet wird, kann dies zu einem Fehler führen.

9000
quelle
Wenn ich schreibe if (set.contains(entry)){set.add(entry); //do more stuff}wird das auch vom Compiler beseitigt?
Andreas Braun
1
@ Andreas Braun: Das glaube ich nicht. Der Compiler hat keine Ahnung von der semantischen Verbindung zwischen containsund add. Außerdem erledigt es die doppelte Arbeit, entryim Set nachzuschauen. Dies kann bei sehr großen Sets und sehr leichten Loops eine Rolle spielen.
9000
1
Also ich nehme an, Sie würden lieber nicht schreiben, if (set.contains(entry)){set.add(entry); //do more stuff}sondern z. B. mit Karl Bielefeldts Antwort gehen?
Andreas Braun
@AndreasBraun: genau (hat diese Antwort positiv bewertet).
9000
1
ein guter Punkt. Mutationen in ifs sind schwierig, da ein zukünftiger Entwickler andere Bedingungen mit Kurzschlüssen haben könnte.
njzk2
0

Ihr Code scheint die Trennung von Befehlsabfragen zu unterbrechen . Dies wird im Clean Code-Buch und im Funktionsstruktur- Video erläutert . Aus der Perspektive von Clean Code denke ich, dass dies nicht als guter Stil angesehen wird.

„Sauberer Code ist einfach und direkt. Sauberer Code liest sich wie gut geschriebene Prosa. Sauberer Code verdeckt niemals die Absicht des Designers, sondern steckt voller klarer Abstraktionen und direkter Kontrolllinien.

- Grady Booch Autor von objektorientierter Analyse und Design mit Anwendungen “

Für mich ist die Absicht Ihres Codes unklar. Wird das if both ausgeführt, wenn der Eintrag erfolgreich hinzugefügt wurde oder auch wenn er bereits vorhanden ist? Was kehrt add()zurück? der Gegenstand? Der Fehlercode?

Niels van Reijmersdal
quelle
2
Na ja, das habe ich mir auch gedacht. Aber ich denke auch, dass @Kilian Foth einen Punkt hat. Wenn ich Abfrage und Befehl trennen wollte, musste ich schreiben, if (set.contains(entry)){set.add(entry); //do more stuff}was irgendwie albern erscheint. Wie sehen Sie das?
Andreas Braun
Ich kenne die Domäne und den Kontext dieses Codes nicht, um einen guten Namen vorzuschlagen, aber ich würde dies mit klarer Absicht in eine Funktion einschließen. Beispiel: doesEntryExistOrCreateEntry (Eintrag), dies könnte Ihre contains- () + add- () Logik enthalten und einen Booleschen Wert zurückgeben. Ähnliche Frage hier: softwareengineering.stackexchange.com/questions/149708/…, in der diese Praxis außerhalb von sauberem Code erörtert wird.
Niels van Reijmersdal
6
Ich denke, die Trennungsregel für Befehlsabfragen ist dumm, besonders wenn sie auf einen Fall wie diesen angewendet wird, in dem eine Methode sowohl eine Aktion ausführt als auch den Erfolgsstatus der Aktion zurückgibt. Aber Sie erklären auch nicht, was die Regel ist, und liefern nicht viel Rechtfertigung dafür. Aus diesen Gründen abgelehnt.
1
"Was gibt add () zurück?" Ich erwarte, dass jeder Java-Entwickler, der die CollectionCodeüberprüfung durchführt , die API kennt .
njzk2
3
Einverstanden mit @ dan1111. Es ist besonders dumm, weil es die Art von Dingen ist, die fruchtbaren Boden für Rassenbedingungen bilden.
Paul Draper