Methoden Methoden anpassen und fehlende Funktionen hinzufügen

Diskutiere Methoden anpassen und fehlende Funktionen hinzufügen im Allgemeine Java-Themen Bereich.
D

DagobertDuck

Guten Abend,

ich melde mich erneut, um meine bisherigen Methoden noch etwas anzupassen und angenehmer für den Benutzer zu gestalten.

Es muss noch geprüft werden, ob der Start- oder Endpunkt des hinzuzufügenden Tracks mit einem Start- oder Endpunkt eines anderen Tracks verbunden ist - natürlich nur wenn !edges.isEmpty(). Im Falle einer Weiche: einer der drei Punkte muss auf einem Start- oder Endpunkt eines anderen Gleises liegen. Nur ein anderes Gleis (normales Gleis oder Weiche) kann mit einem Punkt eines Gleises verbunden werden.

Hier ist meine derzeitige addTrack() Methode:
Java:
    public void addTrack(Track track) throws LogicException {
        /* TODO:
            - Check if start- or endpoint of the track to be added is connected with a start- or endpoint of another track - only if !edges.isEmpty().
                -> In case of a switch: one of the three points must be on a start or end point of another track.
            - Only one other track (normal track or track switch) can be connected to a point of a track.
         */
         edges.computeIfAbsent(track.getStartPoint(), x -> new ArrayList<>()).add(track);
         track.getEndPoints().forEach(p -> edges.computeIfAbsent(p, x -> new ArrayList<>()).add(track));
         ...
    }
Sollte ich hierbei mit Tiefensuche arbeiten? @mihe7 kann die isConnectedAfterRemoving() Methode ggf. einfach etwas angepasst werden?
Außerdem würde ich gerne computeIfAbsent() ersetzen: Falls der key bereits existiert, möchte ich eine Exception werfen lassen. Kann ich dies mit computeIfAbsent() bewerkstelligen, oder ist das gar nicht möglich? Falls nicht, wie kann der Code umstrukturiert werden, sodass eine Exception geworfen wird?

Ähnliches Problem habe ich mit der Methode zum Entfernen eines Tracks:
Java:
    public void removeTrack(int trackId) {
        ...
        edges.remove(track.getStartPoint(), track.getPoints());

        //edges.computeIfAbsent(track.getStartPoint(), x -> new ArrayList<>()).remove(track);
        //track.getEndPoints().forEach(p -> edges.computeIfAbsent(p, x -> new ArrayList<>()).remove(track));
        this.tracks.remove(trackId, track);
    }
Funktioniert computeIfAbsent() im Falle des Entfernens überhaupt? Falls nicht, wie kann dies angepasst werden? edges.remove(track.getStartPoint(), track.getPoints()); funktioniert leider nicht.

Viele Grüße
 
mihe7

mihe7

Sollte ich hierbei mit Tiefensuche arbeiten?
Nein, dazu brauchst Du doch keine Tiefensuche. Ganz einfach: edges liefert Dir zu einem gegebenen Punkt alle Gleise, die dort starten oder enden.

Java:
//erstmal eine Abkürzung einführen
private boolean hasConnections(Point p) {
    return !edges.computeIfAbsent(p, x -> new ArrayList<>()).isEmpty();
}

public void addTrack(Track track) throws LogicException {
    if (edges.isEmpty() || hasConnections(track.getStartPoint()) || 
            track.getEndPoints().stream().anyMatch(this::hasConnections)) {
        // track hinzufügen
    } else {
        // Kein Anschlusspunkt vorhanden
    }
}
Falls der key bereits existiert, möchte ich eine Exception werfen lassen.
Das ergibt wenig Sinn. Ich wiederhole mich: "edges liefert Dir zu einem gegebenen Punkt alle Gleise, die dort starten oder enden. "

Funktioniert computeIfAbsent() im Falle des Entfernens überhaupt?
Ja, computeIfAbsent sorgt nur dafür, dass Du nicht null sondern einen bestimmten Wert (hier: eine leere Liste) erhältst. Aus einer Liste etwas entfernen, das in der Liste nicht vorhanden ist, ist kein Problem.

Du kannst natürlich auch explizit vorher prüfen, ob der Punkt in der Map vorhanden ist:
Java:
    public void removeTrack(int trackId) {
        Track track = tracks.get(trackId);
        if (track == null) {
            return; // oder Exception, da Track nicht existiert
        } 
        tracks.remove(trackId);
        for (Point p : track.getPoints()) {
            if (edges.containsKey(p)) {
                edges.get(p).remove(track);
            }
        }
    }
 
D

DagobertDuck

Danke! An einem Punkt an einem Gleis kann immer nur ein anderes Gleis (normales Gleis oder Gleisweiche) angeschlossen werden.
Wie kann ich dies nun noch beim Hinzufügen eines neuen Tracks überprüfen und damit allerdings nicht die Funktion einer Switch blockieren?

Da ich noch nicht wirklich oft mit Sets/HashSets gearbeitet habe, habe ich die Instanziierung eines HashSets mit nur einem Element leider nicht kürzer hinbekommen:
if (!isConnectedAfterRemoving(Stream.of(track).collect(Collectors.toCollection(HashSet::new)))) - geht das auch etwas kürzer, oder ist das gut so? (Erinnerung: isConnectedAfterRemoving(Set<Track> toRemove))
 
mihe7

mihe7

Da ich noch nicht wirklich oft mit Sets/HashSets gearbeitet habe, habe ich die Instanziierung eines HashSets mit nur einem Element leider nicht kürzer hinbekommen:
Collections.singleton(track) liefert ein Set mit dem einzigen Element track.

An einem Punkt an einem Gleis kann immer nur ein anderes Gleis (normales Gleis oder Gleisweiche) angeschlossen werden.
Wie kann ich dies nun noch beim Hinzufügen eines neuen Tracks überprüfen und damit allerdings nicht die Funktion einer Switch blockieren?
Naja, wie viele Tracks dürfen denn von einem Punkt ausgehen oder an einem Punkt enden?

BTW: Die Funktion der Weiche hat damit gar nichts zu tun.
 
D

DagobertDuck

Naja, wie viele Tracks dürfen denn von einem Punkt ausgehen oder an einem Punkt enden?

BTW: Die Funktion der Weiche hat damit gar nichts zu tun.
Ich habe mich vielleicht etwas unglücklich ausgedrückt. Die Funktion hat damit nichts zu tun, allerdings hängt die Anzahl der potenziell anliegenden Tracks vom Track-Typ ab.

Das reicht doch dann im Grunde genommen schon, oder?
Java:
        if (track.isSwitch(track)) {
            // TODO: Check if number of adjacent tracks is < 3
        } else {
            // TODO: Check if number of adjacent tracks is < 2
        }
Jetzt weiß ich nicht so richtig, ob damit bereits der Fall einer Kreuzung ausgeschlossen wird (?). Also es kann ja passieren, dass eine Kreuzung durch folgenden Ablauf entsteht. So ein Fall muss damit auch abgedeckt sein.

Code:
add switch (1,1) -> (1,2),(2,1)
add switch (2,1) -> (2,-2),(3,1)
add track (1,2) -> (2,2)
add track (2,2) -> (2,1)
 
Zuletzt bearbeitet:
mihe7

mihe7

Um an einen Punkt ein Gleis anschließen zu können, darf dort höchstens ein einfaches Gleis liegen, d. h. die Liste ist entweder leer oder sie enthält ein Element mit 2 Punkten.
 
D

DagobertDuck

Ich tue mich damit gerade etwas schwer, diese Funktion gescheit zu implementieren.

Das ist mit Sicherheit nicht richtig, aber wie würde es richtig aussehen...? Hierbei beachte ich die Endpunkte auch noch gar nicht.
Java:
        if (track.isSwitch(track)) {
            if (getTrackConnections(track.getStartPoint()).isEmpty() || getTrackConnections(track.getStartPoint()).size() == 1) {
                return;
            } else {
                // Throw exception
            }
        } else {
            if (getTrackConnections(track.getStartPoint()).isEmpty() || getTrackConnections(track.getStartPoint()).size() == 2) {
                return;
            } else {
                // Throw exception
            }
        }
Des Weiteren hat sich herausgestellt, dass es keine Grundeinstellung für eine Weiche gibt. Ich habe die Initialisierung this.switchedTo = this.points.get(0); nun aus dem Konstruktor entfernt. Sollte ich this.switchedTo stattdessen einen anderen Wert (null o. Ä.) zuweisen?
Wirklich problematisch wird es erst bei der Instanziierung einer neuen Track, da mir nicht klar ist, was der Parameter int length beim Hinzufügen einer neuen Switch für einen Wert annehmen soll.

Beispiel:
Code:
add track (1,1) -> (2,1)
1
add switch (2,1) -> (3,1),(2,-2)
2
list tracks
t 1 (1,1) -> (2,1) 1
s 2 (2,1) -> (3,1),(2,-2)
Die Zahl hinter den Endpunkten ist die Länge. Bei einer Switch wird dieser Wert nicht unmittelbar berechnet.

Die Länge soll also erst berechnet werden, sobald mit Hilfe des set-switch-Befehls eine Weichenstellung gewählt wurde. Wäre es nun sinnvoll, den Parameter ganz zu entfernen, oder wie löst man das Problem am besten?
 
Zuletzt bearbeitet:
mihe7

mihe7

Das ist mit Sicherheit nicht richtig, aber wie würde es richtig aussehen...? Hierbei beachte ich die Endpunkte auch noch gar nicht.
Richtig. Das äußere if brauchst Du nicht, es bleibt nur
if (getTrackConnections(track.getStartPoint()).isEmpty() || getTrackConnections(track.getStartPoint()).size() == 2) {
übrig, wobei Du track.getStartPoint() durch track.getPoints() ersetzen musst. hinten getTrackConnections(track.getStartPoint()).get(0).getPoints().size() == 2 prüfen musst.

Des Weiteren hat sich herausgestellt, dass es keine Grundeinstellung für eine Weiche gibt.
Wie das?
Sollte ich this.switchedTo stattdessen einen anderen Wert (null o. Ä.) zuweisen?
Wenn Du switchTo nichts zuweist, initialisiert Java automatisch mit null. Wenn es aber nur einen Endpunkt gibt, muss switchTo auf diesen gesetzt werden (normales Gleis).

da mir nicht klar ist, was der Parameter int length beim Hinzufügen einer neuen Switch für einen Wert annehmen soll.
Mir auch nicht. Wozu brauchst Du den Parameter, wenn die Länge aus den Punkten berechnet wird?
 
D

DagobertDuck

Danke.

Leider ist die Aufgabenstellung sehr undurchsichtig und wirklich realitätsnah ist dies auch nicht (?). Gibt es Weichen, die keine Weichenstellung haben...? Jedenfalls wird dies so gefordert und die Weichenstellung wird erst mit Hilfe des zugehörigen Befehls eingestellt.

Wenn Du switchTo nichts zuweist, initialisiert Java automatisch mit null. Wenn es aber nur einen Endpunkt gibt, muss switchTo auf diesen gesetzt werden (normales Gleis).
Wieso muss switchTo bei einem normalen Gleis überhaupt gesetzt werden? Kann ich beim Fahrbetrieb nicht einfach überprüfen, ob es sich um ein normales Gleis oder eine Weiche handelt (isSwitch())?

Mir auch nicht. Wozu brauchst Du den Parameter, wenn die Länge aus den Punkten berechnet wird?
Um z. B. beim Befehl list tracks die Länge auszugeben. Oder soll ich diese dann jedes Mal erneut berechnen? Daher hatte ich die Idee, dies direkt einmal zu speichern, was sich jetzt als schwierig erweisen wird.
 
D

DagobertDuck

Ich bekomme außerdem eine NullPointerException, wegen getTrackConnections(track.getStartPoint()).get(0).getPoints().size() == 2 .
 
D

DagobertDuck

Code:
add track (1,1) -> (2,1)
Exception in thread "main" java.lang.NullPointerException
    at RailNetwork.addTrack(RailNetwork.java:79)
    at Register.addTrack(Register.java:79)
    at AddTrackCommand.execute(AddTrackCommand.java:23)
    at Session.run(Session.java:48)
    at Main.main(Main.java:24)
Die Exception wird geworfen, da die Methode
Java:
    public List<Track> getTrackConnections(Point point) {
        return edges.get(point);
    }
hier aufgerufen wird
Java:
getTrackConnections(track.getStartPoint()).get(0).getPoints().size() != 2
obwohl es zu dem Zeitpunkt (beim Hinzufügen der allerersten Track) noch keine TrackConnections gibt und versucht wird auf das Element mit Index 0 zuzugreifen.
 
mihe7

mihe7

Nein, die Exception bekommst Du bereits, weil
getTrackConnections(track.getStartPoint()).isEmpty()
aufgerufen wird. Du musst halt sicherstellen, dass getTrackConnectios() nicht null ist, bevor Du darauf zugreifst.

Zum Beispiel:
Java:
List<Track> connections = getTrackConnections(track.getStartPoint());
if (connections != null && (connections.isEmpty() || connections.get(0).getPoints().size() == 2)) {
Oder eben mit
Java:
List<Track> connections = edges.computeIfAbsent(track.getStartPoint(), () -> new ArrayList<>());
if (connections.isEmpty() || connections.get(0).getPoints().size() == 2) {
 
D

DagobertDuck

Danke. Warum funktioniert in diesem Fall () bei dem Lambda-Ausdruck nicht? Mit einer Variable x funktioniert es beispielsweise. Gibt es hierbei bestimmte Naming Conventions? Wird () nicht sogar beim Instanziieren benutzt, was hier ja passiert (?).

Jetzt funktionieren auf jeden Fall schon mal die Funktionen Gleis hinzufügen/entfernen und ändern der Weicheneinstellung.

Da ich den Parameter int length entfernen musste, habe ich mir jetzt eine Notlösung gebastelt:
Java:
    private void checkLength(Track track) {
        try {
            int length = track.getStartPoint().distanceTo(track.getSwitchedTo());
        } catch (ArithmeticException e) {
            // Throw exception
        }
    }
und in der addTrack Methode:
Java:
        if (track.getSwitchedTo() != null) {
            checkLength(track);
        }
Dies ist nötig, um einen Integer overflow zu verhindern, der durch eine zu weit entfernte Punktelage entstehen kann.
Meine Methode funktioniert so auch, ist aber nicht wirklich effizient, oder? In der distanceTo() Methode wird eine ArithmeticException geworfen, wenn ein Integer overflow stattfindet. Jetzt weise ich der Variable length einen Wert zu, der allerdings hier nie verwendet wird. Kann das auch irgendwie geschickter gemacht werden?

Geht das hier auch etwas schöner (beim Auflisten der Tracks)?
Java:
private int length;

...

@Override
    public String toString() {
        ...
        if (switchedTo != null) {
            length = getStartPoint().distanceTo(getSwitchedTo());
        }
        if (isSwitch(this)) {
            if (switchedTo != null) {
                return "s " + getId() + " " + getStartPoint() + " -> " + endPoints + " " + length;
            } else {
                return "s " + getId() + " " + getStartPoint() + " -> " + endPoints;
            }
        } else {
            return "t " + getId() + " " + getStartPoint() + " -> " + endPoints + " " + length;
        }
    }
 
mihe7

mihe7

Warum funktioniert in diesem Fall () bei dem Lambda-Ausdruck nicht? Mit einer Variable x funktioniert es beispielsweise.
Weil die Funktion einen Parameter erwartet und ich mich verschrieben habe.

Dies ist nötig, um einen Integer overflow zu verhindern, der durch eine zu weit entfernte Punktelage entstehen kann.
Da die Punkte ja nur horizontal/vertikal angeordnet sein dürfen, frage ich mich, wie es hier zu einem Overflow kommen sollte...
 
D

DagobertDuck

Zum Beispiel bei einer Track (-1073741824,1) -> (1073741824,1). Damit würde ein Integer overflow zustande kommen. Die Überprüfung in der distanceTo() Methode habe ich auch schon programmiert, mir geht es jetzt nur um eine gescheite Abfrage in der addTrack Methode, da der Parameter length wegfällt und eine Switch/Track gar nicht erst erstellt werden soll, wenn der Abstand zu einen der Endpunkte zu weit ist.
 
mihe7

mihe7

OK, dann muss doch nur ein entsprechender Wertebereich für die Länge gewählt werden: long.
 
Thema: 

Methoden anpassen und fehlende Funktionen hinzufügen

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben