# Probleme mit Collections im Konstruktor



## bugbunny (25. Feb 2018)

Hallo, 
ich habe ein Verständnisproblem bei einer Hausaufgabe. 
Es gibt einmal die Klasse Spieler die das Interface Comparable<Spieler> implementieren soll und die Spieler nach ihrer id vergleichen soll. 

Das habe ich so "gelöst":

```
public class Spieler implements Comparable<Spieler>
{ 
int id;
String name;

public static List<Spieler> spieler = new ArrayList<Spieler>();

@Override
public int compareTo(Spieler other)
{    if (getId() > other.getId())
    {        return 1;
    } else if (getId() < other.getId())
    { return -1;
    } else return 0;
}
public Spieler(int id, String name)
{
    if (id < 0 || name.isEmpty())
        throw new IllegalArgumentException( "Id negativ oder name leer" );

    this.id = id;
    this.name = name;
    spieler.add( this );
    Collections.sort( spieler );
}
```
Zum Testen habe ich mir ein paar Spieler erstellt und mir die Liste einmal unsortiert und dann mit Collections.sort sortiert, das funktioniert. 

Dann soll ich in der Klasse Spielermanager folgendes machen: 
public SpieleManager(Collection<Spieler> spieler)
Erzeugt einen SpielerManager, dem die übergebenen Spieler bekannt sind.
Befinden sich in der übergebenen Collection mehrere Spieler mit der gleichen ID oder
dem gleichen Namen oder kennt der SpielerManager bereits Spieler mit der ID oder dem
Namen eines übergebenen Spieler, soll eine java.lang.IllegalArgumentException geworfen werden.
Die übergebene Collection darf (auch bei Aufrufen weiterer Methoden dieser Klasse)
nicht verändert werden.

Ich verstehe nicht wie das mit dem Übergeben der Liste funktionieren soll, kann mir das jemand erklären oder hat eine gutes Beispiel?

Grüße Leonie


----------



## Robat (25. Feb 2018)

Was genau verstehst du daran nicht? Die Signatur des Konstruktors hast du ja schon. Jetzt musst du nur noch eine Liste von Spielern erzeugen und diese dem Konstruktor übergeben.

```
public static void main(String[] args) {
    List<Spieler> spieler = new ArrayList<>();
    // liste füllen
 
    SpielerManager manager = new SpielerManager(spieler);
}
```


```
public class SpielerManager {
     private List<Spieler> spieler;
     public SpielerManager(Collection<Spieler> spieler) {
           ...
     }
}
```
BTW: Warum besitzt die Klasse Spieler eine Liste von Spielern?


----------



## bugbunny (25. Feb 2018)

Ist meine erste größere Aufgabe, deswegen sind mir manchmal die Zusammenhänge was in welcher Klasse passiert noch nicht ganz klar, hier hatte ich bspw. durch das implements Collection<Spieler> im Manager angenommen, dass die Liste dann schon in der Spielerklasse sein muss.

Ok, das bedeutet in der Spielerklasse habe benötige ich nur die compareTo() und die Spielerliste wird erst im Manager deklariert? 
Dann kann ich aber die erzeugten Spieler nicht mehr über den Spielerkonstruktur direkt in die Liste speichern?

Und gibt es zur Duplikatserkennung eine einfache Möglichkeit?


----------



## Javinner (25. Feb 2018)

bugbunny hat gesagt.:
			
		

> Ok, das bedeutet in der Spielerklasse habe benötige ich nur die compareTo() und die Spielerliste wird erst im Manager deklariert?


Eine Klasse sollte nur Daten und Methoden beinhalten, welche unmittelbar mit der Klasse zutun haben. 
Eine Spielerliste ist ganz sicher kein Bestandteil der Klasse Spieler.


> Und gibt es zur Duplikatserkennung eine einfache Möglichkeit?


Abfrage beim Hinzufügen zur Liste, ob bereits vorhanden.


----------



## Robat (25. Feb 2018)

bugbunny hat gesagt.:


> Dann kann ich aber die erzeugten Spieler nicht mehr über den Spielerkonstruktur direkt in die Liste speichern?


Das sollst du ja auch nicht.
So wie ich das anhand des gegebenen Codes / Textes sehe soll der SpielerManager am Anfang eine Collection von Spielern bekommen.
Hinzufügen von Spielern wird sicherlich über eine separate Methode geschehen.


----------



## bugbunny (25. Feb 2018)

Danke für die Hilfe, aber je mehr ich mich "einarbeite" desto weniger verstehe ich 

Robat du hast recht, die nächste Methode ist
public Player add(Player player)
Fügt dem PlayerManager einen Player hinzu und gibt ihn zurück.
Kennt der PlayerManager bereits Player mit der ID oder dem Namen des übergebenen
Players, soll eine java.lang.IllegalArgumentException geworfen werden.

Nicht einmal das bekomme ich hin, bisher habe ich immer alles in der Mainmethode mit add() hinzugefügt und auch alle Erklärungen die ich die letzten Stunden angeschaut habe waren direkt in der main. 

die add methode wird also mit return player; enden oder?
public Player add(Player player){
players.add( player );
return player;} 
wird falsch sein, aber wie stelle ich das hinzufügen und die Überprüfung dar? Ich kann verstehen wenn ihr mir nicht die Lösung dazu sagen wollt, aber vielleicht habt ihr ein gutes Beispiel zum verstehen. 

Und wieso muss ich in der Methode noch eine IllegalArgumentExeption werfen, wenn Name oder ID schon vorhanden sind, im PlayerManagerKonstruktor wird doch schon bei den selben Bedingungen eine geworfen?

Entschuldigt die vielleicht blöden Fragen.

Grüße Leonie


----------



## Robat (25. Feb 2018)

```
public Player add(Player player){
   players.add( player );
   return player;
}
```
Sieht aber ganz gut aus.
Naja du musst dir jeden Spieler aus der Liste einmal anschauen (Tipp: Schleife) und die ID mit der ID vom Spieler der hinzugefügt werden soll vergleichen.
Solltest du die selbe ID gefunden haben darf der Spieler nicht hinzugefügt werden.



bugbunny hat gesagt.:


> Und wieso muss ich in der Methode noch eine IllegalArgumentExeption werfen[...]


Du musst sie noch mal werfen, weil die Bedingung ja nur einmal geprüft wird. Du sollst aber zstl. noch bei jedem hinzufügen prüfen, ob die Spieler-ID schon vorhanden ist.


----------



## bugbunny (25. Feb 2018)

```
for (Player player: players){
    if(players.contains( player)){
     throw new IllegalArgumentException( "" );
     }
    else players.add( player );
}
return player;
```
Das ist mein aktueller Stand, aber ich weiß nicht wie ich einzeln auf Name oder ID prüfen kann, funktioniert das mit contains überhaupt?


----------



## Robat (25. Feb 2018)

Du hast doch sicherlich eine getID() Methode oder ähnliches in der Player-Klasse oder nicht?


----------



## MoxxiManagarm (26. Feb 2018)

> Naja du musst dir jeden Spieler aus der Liste einmal anschauen (Tipp: Schleife) und die ID mit der ID vom Spieler der hinzugefügt werden soll vergleichen.



Nicht unbedingt. Er könnte im PlayerManager intern auch eine Datenstruktur verwenden, welche keine Duplikate zulässt und entsprechende Rückgabewerte liefert, sofern das neue Element nicht ergänzt werden kann.

z.B. https://docs.oracle.com/javase/7/docs/api/java/util/TreeSet.html#add(E)

Das wäre im Fall von TreeSet sogar auch gleich sortiert, falls das in den weiteren Schritten mal irgendwie eine Rolle spielen sollte.



> Das ist mein aktueller Stand, aber ich weiß nicht wie ich einzeln auf Name oder ID prüfen kann, funktioniert das mit contains überhaupt?



Ich glaube nicht, es wird m.E. die equals Methode verwendet. Du könntest diese aber auch überschreiben.

Unabhängig davon macht keinen Sinn das innerhalb der Schleife zu tun. Entweder in der Schleife einzeln vergleichen, oder contains oder wie oben eine Datenstruktur, welche duplikatslos ist.


----------



## sascha-sphw (26. Feb 2018)

BTW: Dieser code wird immer eine Exception werfen.


bugbunny hat gesagt.:


> ```
> for (Player player: players){
> if(players.contains( player)){
> throw new IllegalArgumentException( "" );
> ...



Du prüfst ob ein Spieler aus der Liste in der Liste enthalten ist...


----------



## Robat (26. Feb 2018)

MoxxiManagarm hat gesagt.:


> Nicht unbedingt. Er könnte im PlayerManager intern auch eine Datenstruktur verwenden


Das ist mir bewusst. Würde mich aber einfach mal soweit aus dem Fenster lehnen und behaupten, dass das nicht Ziel der Aufgabe ist.


----------



## MoxxiManagarm (26. Feb 2018)

Ich sehe insgesamt 5 Alternative Implementierungen.

```
public static void main(String[] args) {
            DuplicateDemo demo = new DuplicateDemo();
          
            System.out.println(demo.addIdObject(new IdObject(1)));
            System.out.println(demo.addIdObject(new IdObject(2)));
            System.out.println(demo.addIdObject(new IdObject(3)));
            System.out.println(demo.addIdObject(new IdObject(3)));
      }
```

Alle Beipsiele bringen damit die Ausgabe 1, 2, 3, null

Mein IdObject hat folgende Methoden @Override: equals, compareTo, toString
Sowie getId()

Alternative 1-4 ist z.B. eine ArrayList
Alternative 5 ist z.B. ein TreeSet

Alternative 1: Vergleich via überschriebener equals Methode

```
public IdObject addIdObject(IdObject o) {
            for(IdObject obj : list) {
                  if(obj.equals(0)) return null;
            }
            list.add(o);
            return o;
      }
```

Alternative 2: Vergleich via überschriebener compareTo Methode

```
public IdObject addIdObject(IdObject o) {
            for(IdObject obj : list) {
                  if(obj.compareTo(o) == 0) return null;
            }
            list.add(o);
            return o;
      }
```

Alternative 3: Direktvergleich

```
public IdObject addIdObject(IdObject o) {
            for(IdObject obj : list) {
                  if(obj.getId() == o.getId()) return null;
            }
            list.add(o);
            return o;
      }
```

Alternative 4: contains

```
public IdObject addIdObject(IdObject o) {
            if(list.contains(o)) return null;
          
            list.add(o);
            return o;
      }
```

Alternative 5: Duplikatsfreie Datenstruktur

```
public IdObject addIdObject(IdObject o) {
            return set.add(o) ? o : null;
      }
```


----------



## mrBrown (26. Feb 2018)

MoxxiManagarm hat gesagt.:


> Alternative 5 ist z.B. ein TreeSet


Da geht btw jedes Set, zB HashSet.

Alternativ ginge statt TreeSet + Compareable auch TreeSet + Comparator, könnte uU geeigneter sein


----------



## MoxxiManagarm (26. Feb 2018)

> Da geht btw jedes Set, zB HashSet.



Bei mir steht ja auch z.B. ^^


----------



## mrBrown (26. Feb 2018)

bugbunny hat gesagt.:


> Es gibt einmal die Klasse Spieler die das Interface Comparable<Spieler> implementieren soll und die Spieler nach ihrer id vergleichen soll.





bugbunny hat gesagt.:


> Erzeugt einen SpielerManager, dem die übergebenen Spieler bekannt sind.
> Befinden sich in der übergebenen Collection mehrere Spieler mit der gleichen ID oder
> dem gleichen Namen oder kennt der SpielerManager bereits Spieler mit der ID oder dem
> Namen eines übergebenen Spieler, soll eine java.lang.IllegalArgumentException geworfen werden.


Sind diese beiden Teile korrekt aus der Aufgabenstellung zitiert?

Wenn ja, fallen alle Varianten mit equals oder Comparator/Comparable weg, da man den jeweilige Methode-Contract brechen müsste.
Es bleibt also nur die Variante mit direktem Vergleich der beiden Attribute, in deinem Fall am einfachste mit einer Schleife über die Liste.


----------



## MoxxiManagarm (26. Feb 2018)

Oh das mit dem Namen habe ich überlesen. Dann bleibt eigentlich nur Variante 3.

Edit: Habe den 2. Teil herausgelöscht, war Blödsinn in zusammenhang mit der Aufgabenstellung


----------



## bugbunny (26. Feb 2018)

Danke für die vielen Antworten, ich habe meinen Code mal umgebastelt und mit meinem Verständnis kommentiert. 

```
for(Spieler s : spieler) //durchläuft die Liste spieler wenn neuer Spieler erzeugt wird
{    if (s.getId() == spieler.getId()) //Wenn Id des erzeugten Spielers bereits in Liste vorhanden ist, wird Exception geworfen
    {        throw new IllegalArgumentException( "ID bereits vorhanden" );
    }
    if (s.getName().equals(.getName() )) //Gleicher Test für name, nur equals da String
    {        throw new IllegalArgumentException( "Name bereits vorhanden");
    }
}
    spieler.add( spieler ); //fügt Spieler der Liste hinzu
    return spieler; // gibt hinzugefügten Spieler zurück
}
```
Stimmt das in etwa?
Und funktioniert der equalsvergleich von name auch wenn ich in der Spielerklasse die equals Methode so überschrieben habe, dass zwei Spieler gleich sind wenn sie die gleich ID haben? Wurde nämlich gefordert. 

Und noch etwas verstehe ich nicht: Wenn ich mir in der main-Methode Spieler erstelle und die mit Add der Liste hinzufüge, wird nicht meine neue add Methode genommen, sondern die ganz normale Listen.add ohne Prüfung, wie kann ich auf meine neue zugreifen?


----------



## mrBrown (26. Feb 2018)

bugbunny hat gesagt.:


> Und funktioniert der equalsvergleich von name auch wenn ich in der Spielerklasse die equals Methode so überschrieben habe, dass zwei Spieler gleich sind wenn sie die gleich ID haben? Wurde nämlich gefordert.


Ja, name ist ein String und hat nichts mit dem in der anderen klasse überschriebenem equals zu tun 



bugbunny hat gesagt.:


> Und noch etwas verstehe ich nicht: Wenn ich mir in der main-Methode Spieler erstelle und die mit Add der Liste hinzufüge, wird nicht meine neue add Methode genommen, sondern die ganz normale Listen.add ohne Prüfung, wie kann ich auf meine neue zugreifen?


Du solltest die Spieler nicht einer Liste hinzufügen, sondern einem Spielermanager


----------



## bugbunny (26. Feb 2018)

Danke an alle für die Hilfe, jetzt habe ich zumindest die Methode verstanden


----------

