# Objekte in einer ArrayList vergleichen



## lalas (5. Jan 2009)

Hallo zusammen,

ich sitze grad an meiner Semester-Projektaufgabe und habe ein Problem, wo ich einfach nicht weiterkomme. Wir müssen ein Reservierungssystem für ein Hotel entwickeln. Dabei muss während des Reservierungsvorgang geprüft werden, ob ein Zimmer gebucht werden kann oder nicht.
Das Hauptkriterium ist hierbei das Anfangs- und Enddatum.

Dabei werden Reservierungs-Objekte in einer arraylist abgelegt. Ich wollte nun das Comparable-interface nutzen, stehe aber etwas auf dem Schlauch wie ich nun Startdatum UND Enddatum vergleiche. Zumal es ja einige Kombinatioen geben kann.
Z.B.:
Kunde1: 01.01.2009 bis 05.01.2009
Kunde2: 01.01.2009 bis 04.01.2009
Kunde3: 04.01.2009 bis 10.01.2009

Es darf nun also bei einer Reservierung zu keinen Überschneidungen kommen. Kunde3 könnte also das Zimmer von Kunde 2 bekommen, aber nicht von Kunde1.
Ebenso könnte Kunde1 nicht die Zimmer von Kunde2 und 3 bekommen, wenn er z.B. nach den beiden Kunden reservieren würde.

Mir ist klar, dass ich Comparable in meiner Klasse "Datum" implementieren muss. Aber bei wie implementiere ich es bei meiner arraylist-Funktion ?
Dazu kommt das in meiner Datum-Klasse die Attribute Tag/Monat/Jahr jeweils int-Werte sind und hinterher per toString-Methode in die Form string "5.12.2008" ausgegeben werden.
Ich wollte es zunächst einfach mit Summenbildung der int-Werte versuchen. 

Ich hoffe da kann mir jemand den entscheidenen Tipp geben.

Hier mal mein code um einen Überblick zu haben (auszugweise):

Zunächst das Ablegen von Reservierungs-Objekten in eine arraylist:

```
public ArrayList<Reservierung> buche2(Kunde k, int anzZimmer, int anzBetten, Datum beginn, Datum ende) {
		
		Zimmer[] zimmer_array = getHotel().getZimmer_array();
		Zimmer zimmer = null;
		Reservierung res_temp = null;
		ArrayList<Reservierung> res_liste = new ArrayList<Reservierung>();
				
		for (int i=0; i<=anzZimmer; i++) {
									
					if (zimmer_array[i].getBetten() >= anzBetten) {
						zimmer = zimmer_array[i];
						
						res_temp = new Reservierung (k, zimmer, beginn, ende);
						res_liste.add(res_temp);	
					}
		}
				
		return res_liste;
	}
```

Und nun noch die Klasse "Datum":

```
public class Datum implements Comparable<Datum> {

	private int tag;
	private int monat;
	private int jahr;

// getter/setter/etc. lasse ich mal raus, wird zu lang


	
	public String toString() {
		String datum = new Integer (getTag()).toString() +"." +new Integer (getMonat()).toString() +"." +new Integer (getJahr()).toString() +"\n";
		return datum;
		
	}
	

	@Override
	public int compareTo(Datum argument) {
		if (jahr+monat+tag < argument.jahr+monat+tag )
			return -1;
		if (jahr+monat+tag > argument.jahr+monat+tag )
			return 1;
		
		return 0;
	}
```


----------



## SlaterB (5. Jan 2009)

jahr+monat+tag ist schlecht, zwei Tage sollten nicht stärker gelten als ein Jahr,
du musst die Felder gewichten, z.B. Jahr mit 365 multiplizieren,

> argument.jahr+monat+tag 

hier ist übrigens monat und tag vom eigenen Objekt!
argument.jahr+argument.monat+argument.tag 

-----

was du ansonsten vergleichen willst, ist mir zumindest noch nicht klar,
vergiss Java-Denkweisen wie Comparable und ArrayList und beschreibe erstmal in deutschen Worten,
wie du zwei Reservierungen vergleichen willst,

danach noch überlegen wie die Reservierungen organisiert sind, gibts nur eine Liste pro Zimmer oder wie?


----------



## lalas (5. Jan 2009)

Okay, dann erstmal in Deutsch 

Vorgabe für unser Projekt ist folgendes:

Ein Hotel mit:
Anzahl Zimmern
Anzahl Betten pro Zimmer (1 oder 2)

Nun haben wir diverse Funktionen und Klassen geschrieben um mit dem ganzen umgehen zu können. Es gibt z.B.

- Datum (Tag Monat Jahr)
- Zimmer (Zimmer-Nr, Anzahl Betten)
- Kunde (Vorname, Nachname, Adresse, Kundennummer, ...)
- Reservierung (Reservierungsnummer, Kunde, Zimmer, Anfangsdatum Enddatum)

Alle Reservierungen sollen in einer Container-Klasse abgelegt werden.


Nun soll folgendes geschehen:

Kunde A ruft im Hotel an und möchte ein 2-Bett-Zimmer für den Zeitraum 01.01.2009 bis 05.01.2009

2 Stunden später ruft Kunde B an und möchte ein 2-Bett-Zimmer für 31.12.2008 bis 04.01.2009

Die Reservierung von Kunde A wurde in einer arraylist abgelegt, mit dem zugeteilten Zimmer, Anfangsdatum, Enddatum, Reservierungsnummer.
Nun muss ein weitere Reservierung für Kunde B vorgenommen werden. Dabei ist mein Gedankengang so:

Alle vorhandenen Reservierungen durchgehen und vergleichen ob eine Reservierung existiert mit Anfangsdatum GLEICH oder FRÜHER.
Wenn FRÜHER, muss zusätzlich geschaut werden, ob das Enddatum dieser Reservierung FRÜHER oder GLEICH dem Anfangsdatum der neuen Reservierung ist.

Wenn kein FRÜHERES Anfangsdatum bereits existiert (dafür SPÄTER), muss zusätzlich geprüft werden ob das vorhandene Enddatum FRÜHER oder GLEICH dem neuen Anfangsdatum ist.

So müssen alle Reservierungen durchgegangen werden, bis alle Bedingunen zutreffen, dass eben ein Zimmer gefunden wurde welches vergeben werden kann.
Trifft keine Bedingung zu, muss eben ein freies, noch nicht reserviertes Zimmer gewählt werden.

Übrigens noch einige Vorgaben vom Dozenten:

- Wir MÜSSEN mit Comparable arbeiten
- Wir dürfen nicht mit der Kalender-Klasse von java arbeiten (wenn sich das so nennt)

Übrigens ist die Verwendung von ArrayList nicht zwingend, wir sollen halt eine geeignete Container-Klasse verwenden.

Ich hoffe das ist nun verständlich wie ich es meine 

Vielen Dank

Gruss,
Lalas


----------



## SlaterB (5. Jan 2009)

Comparable hilft bei diesem Problem wenig, ob du es dennoch krummerweise einsetzen willst du nicht, sei dir überlassen,

du brauchst eine Methode 
boolean ueberscheindenSichZweiReservierungen(Reservierung 1, Reservierung 2)

dort werden die Anfangs- und Enddati der beiden Reservierungen angeschaut, und zwar so wie du es schon beschrieben hast:

" Anfangsdatum GLEICH oder FRÜHER. 
Wenn FRÜHER, muss zusätzlich geschaut werden, ob das Enddatum dieser Reservierung FRÜHER oder GLEICH dem Anfangsdatum der neuen Reservierung ist. 

Wenn kein FRÜHERES Anfangsdatum bereits existiert (dafür SPÄTER), muss zusätzlich geprüft werden ob das vorhandene Enddatum FRÜHER oder GLEICH dem neuen Anfangsdatum ist. "

dann hast du eine Aussage über die Beziehung zweier Reservierungen,

einen Schleifendurchlauf mit Vergleich aller bisherigen Reservierungen mit der neuen Reservierung mit Hilfe dieser Methode ist selbstverständlich

--------

wenn man groß alles krum und schief verbiegt, kann man das auch mit Comparable lösen, 
sehr wahrscheinlich ist das aber nicht so gedacht,
Comparable brauchst du fürs Datum

nicht aber für das Reservierungs-Überschneidungs-Problem in Reservierung

unabhängig davon kannst du in Reservierung dennoch Comparabe einbauen und die Reservierungen nach Anfangsdatum oder sonst ein Kriterium vergleichen,
für die Überschneidung hilft das aber nicht weiter


----------



## lalas (5. Jan 2009)

Danke für die Anwort, also Zitat aus der Aufgabe lautet:

"Um zu überprüfen, ob eine Reservierung vorgenommen werden kann, müssen häufiger Objekte der
Klasse Datum verglichen werden. Ergänzen Sie zu diesem Zweck die Klasse Datum und ggf. weitere
Klassen, so dass diese das Interface Comparable<T> implementieren."

Leider ist unser Dozent alles andere als eine Hilfe. Kann ich in die CompareTo-Methode nicht irgendwie die restlichen Bedingungen einbringen ?
Und was mir noch nicht ganz klar ist, muss ich in jeder Klasse in der ich Comparable einbinde, eine compareTo-Methode schreiben ?

edit:

Eine Sache noch, Du schreibst oben:

"Comparable brauchst du fürs Datum

nicht aber für das Reservierungs-Überschneidungs-Problem in Reservierung"

Wofür brauche ich denn Comparable beim Datum dann ? Ich meine was will ich denn bei Datum vergleichen, wenn ich die Reservierungen anders vergleiche ?


----------



## SlaterB (5. Jan 2009)

> muss ich in jeder Klasse in der ich Comparable einbinde, eine compareTo-Methode schreiben 

jede Klasse, die das Interface Comparable implementiert, muss auch eine Methode compareTo anbieten, 
sonst macht es doch keinen Sinn, das Interface zu implementieren..
(nein, Dozenten-Vorgaben zählen nicht direkt als 'Sinn')

andererseits kannst du in einer compareTo-Methode von Klasse X auch Datenfelder von anderen Klassen Y verwenden, 
ohne dass Y selber Comparable sein muss, falls du das meinst


-----

selbstverständlich kannst du die Bedingungen auch in compareTo einbauen,
z.B. liefert compareTo immer genau dann 0 wenn sich die Zeiträume überschneiden,

ist aber wirklich eine äußerst schreckliche Idee, das nochmal als Warnung


----------



## lalas (5. Jan 2009)

Okay, also vielleicht habe ich grad auch eine andere Vorstellung von Comparable 

Ich dachte, ich kann Comparable nutzen, indem ich es bei "Datum" und "Reservierung" implementiere und dann die Datumsangaben in Reservierungen zu vergleichen.
Die compareTo-Methode bei Datum hab ich nun mal wie folgt gemacht:


```
public int compareTo(Datum argument) {
		if (jahr+monat+tag < argument.jahr*365+argument.monat*12+argument.tag )
			return -1;
		if (jahr+monat+tag > argument.jahr*365+argument.monat*12+argument.tag )
			return 1;
		
		return 0;
	}
```

Jetzt ein doofe Frage, wie sollte compareTo bei Reservierung aussehen ?

Irgendwie hab ich wohl einen gewaltigen Denkfehler in dem ganzen

Gruss,
Lalas


----------



## Murray (5. Jan 2009)

Die Implementierung von compareTo in Datum sollte vielleicht besser so aussehen:

```
return (this.jahr*365+this.monat*12+this.tag) - (argument.jahr*365+argument.monat*12+argument.tag)
```


----------



## SlaterB (5. Jan 2009)

die compareTo von Datum ist ja schonmal ganz schön schlimm,
du kannst doch nicht nur die argument-Zahlen verändern und die eigenen so lassen

stell dir vor,  du vergleichst ein Datum 2008 (Rest egal) mit 2009

2008 < 365*2009
also wird -1 ausgegeben

vergleichst du dagegen andersrum, also 2009 mit 2008,
dann ist
2009 < 365*2008 und wieder wird -1 ausgegeben,

selbstverständlich musst du beide mit 365 multiplizieren:
2008*365 < 2009*365
und konsistenterweise 
2009*365 > 2008*365

> Jetzt ein doofe Frage, wie sollte compareTo bei Reservierung aussehen ? 

wie gesagt am besten völlig irrelevant zur Überscheidungsthematik wenn überhaupt nur die Anfangsdati vergleichen:

public int compareTo(Reserviereung argument) { 
      return getAnfang().compareTo(argument.getAnfang()); 
   }


wenns dir weiterhin um die Überschneidung geht, dann musst du nach wie vor

" Anfangsdatum GLEICH oder FRÜHER. 
Wenn FRÜHER, muss zusätzlich geschaut werden, ob das Enddatum dieser Reservierung FRÜHER oder GLEICH dem Anfangsdatum der neuen Reservierung ist. 

Wenn kein FRÜHERES Anfangsdatum bereits existiert (dafür SPÄTER), muss zusätzlich geprüft werden ob das vorhandene Enddatum FRÜHER oder GLEICH dem neuen Anfangsdatum ist. " 

in Code übersetzten,
das musst du schon alleine aus if und else zusammenbauen


----------



## lalas (5. Jan 2009)

Okay, ich schreibe mal eine boolean-Methode um die Reservierungen zu vergleichen, hab langsam auch Schnauze voll. Rätsel seit 2 Wochen rum, wie ich das mit Comparable einrichten soll....


Gruss,
Lalas


----------



## Murray (5. Jan 2009)

lalas hat gesagt.:
			
		

> Jetzt ein doofe Frage, wie sollte compareTo bei Reservierung aussehen ?


Evtl. so:


```
public int compareTo( Reservierung arg) {
  int comp = this.getBeginn().compareTo( arg.getEnde());
  if ( comp > 0) return comp; //--- Beginn nach Ende des Vergleichszeitraums -> grösser
  comp = this.getEnde().compareTo( arg.getBeginn());
  if ( comp < 0) return comp;  //-- Ende vor Beginn des Vergleichszeitraums -> kleiner
  return 0;  //--- Beginn und/oder Ende innerhalb des Vergleichszeitraums -> Überschneidung
}
```

Die Frage ist allerdings, ob das wirklich der "natürlichen Ordung" von Reservierungen entspricht - würde man eine Liste von Reservierungen genau so sortiert sehen wollen?


----------



## lalas (5. Jan 2009)

Danke für die Antwort.

Naja, es geht ja nur darum dass sich keine Reservierungen überschneiden und das alle Zimmer möglichst optimal ausgelastet werden.
Die Vorgabe vom Dozenten ist der Knackpunkt, ich würde auch am liebsten eine Methode schreiben, der ich 2 Reservierungen übergebe und dann einfach nur per if-Abfragen prüfe ob es zu Überschneidungen kommt.

Ich werde es mal so versuchen, dass ich eine solche Methode schreibe und nur den Vergleich des Datums per Comparable mache (sofern ich das hinbekomme  )

Dann sehe ich was der Dozent sagt....


----------



## lalas (5. Jan 2009)

So ich habe mal etwas weitergemacht und bin zu dem Schluss gekommen, dass ich mit mehreren Containern arbeiten muss/will.

Nun einige Fragen. Ich habe in einer Methode einen Container generiert, welcher eine Menge von reservierten Zimmern für einen Kunden enthält.
Nun nehme ich weitere Reservierungen in mehreren anderen Methoden vor (aber nur einzelne Reservierungen). Den Aufruf für eine solche Reservierung mache ich im Hauptprogramm.

Ich möchte nun diese Reservierungen alle zu einem Gesamt-Container zusammenfassen, wie stelle ich das am besten an ?
Muss ich da jedesmal mit einer temporären Variable arbeiten und dann hinzufügen:

```
Reservierung res_temp = new Resvorgang.buche(kunde, 2, beginn, ende);
gesamt_liste.add(res_temp);
```
Dazu muss ich noch sagen, dass ich ja immer noch die Überprüfung einbauen muss (bezgl. Überschneidungen in den Datumsangaben).
Das habe ich soweit fertig, innerhalb einer Methode. Ich weiss noch nicht so recht wie ich das global am besten einbinden soll.

Jetzt hatte ich die Idee, die Container in arrays umzukopieren und von dort wieder in einern neuen Container einzufügen.
Dabei bekomme ich Compiler-Fehler:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Lreservierung.Reservierung;
	at Hauptprogamm.main(Hauptprogamm.java:140)

Was mich stutzig macht, sind die "L" vor den Meldungen. 

Hier der code dazu:

```
Reservierung[] rese_list = (Reservierung[]) zimmer_liste.toArray()
```

Und hier mal der code vom Datum checken:

```
public ArrayList<Reservierung> res_testen (ArrayList<Reservierung> res_liste, Reservierung res_temp) {
		
		for (int i=0; i<res_liste.size(); i++) {
			
			int start_vergleich = checkStartDatum (res_temp, res_liste.get(i)); 
			
			int start_ende_vergleich = checkStartEnd (res_temp, res_liste.get(i));
			
			int ende_start_vergleich = checkEndStart (res_temp, res_liste.get(i));
			
					
			//Nun die Bedingungen damit gebucht werden kann
			if (start_vergleich == -1 && ende_start_vergleich <=0) {

				System.out.println("ZIMMER IST FREI - BUCHE...");
				res_liste.add(res_temp);
				System.out.println("RESERVIERUNG ERFOLGREICH");
				return res_liste;
			}

			if (start_vergleich >0 && start_ende_vergleich>=0) {
				
				System.out.println("ZIMMER IST FREI - BUCHE...");
				res_liste.add(res_temp);
				System.out.println("RESERVIERUNG ERFOLGREICH");
				return res_liste;
			}

			else System.out.println("ZIMMER IST NICHT FREI");

	
		}
		return res_liste;
		
	}
```


Gruss,
Lalas
[/code]


----------



## SlaterB (5. Jan 2009)

warum sollte toArray() ein Reservierung[] zurückgeben, wohl zu verwöhnt von Generics,
gibt nur Objekt[], welches du nicht auf Reservierung[] casten kannst, entweder neues Array erstellen oder vorgeben:

Reservierung[] rese_list = (Reservierung[]) zimmer_liste.toArray(new Reservierung[zimmer_liste.size()]);


----------



## lalas (6. Jan 2009)

Vielen Dank, damit klappt es.

Nun ist noch ein Problem aufgekommen. Ich kopiere die Elemente der ArrayList in ein array, anschliessend gebe ich das array auf dem Bildschirm aus, soweit in Ordnung.
Wenn ich nun die array-Inhalte in eine neue ArrayList kopiere und anschliessend die neue ArrayList ausgebe, erhalte ich kleine Fehler in der Ausgabedarstellung. Und zwar werden als erstes Zeichen und letztes Zeichen, eckige Klammern eingefügt.

Hier mal der code:

```
// ArrayList in array kopieren
Reservierung[] rese_array = (Reservierung[]) zimmer_liste.toArray(new Reservierung[zimmer_liste.size()]);

//array-Inhalte in eine neue ArrayList kopieren
		for (int i=0; i<rese_array.length; i++) {
			
			System.out.println(rese_array[i].toString());
			gesamt_liste.add(i,rese_array[i]);
			
		}


//Ausgabe der ArrayList
		System.out.println(gesamt_liste);
```

Der Fehler sieht wie folgt aus, ich poste mal Ausgabe des ersten Elementes der neuen Liste und das letzte Element:

AUSGABE DER GESAMTLISTE
=======================

[Kundennummer: 1001
Kundenname: Johnny Meier
Meierweg 33
22333 Hamburg
Deutschland
----------------------
Reservierte Zimmernr: 9001
Anzahl Betten: 2
Anreisedatum: 2.1.2009
Abreisedatum: 4.1.2009
Reservierungsnummer: 7

...

Kundennummer: 1001
Kundenname: Johnny Meier
Meierweg 33
22333 Hamburg
Deutschland
----------------------
Reservierte Zimmernr: 9004
Anzahl Betten: 2
Anreisedatum: 2.1.2009
Abreisedatum: 4.1.2009
Reservierungsnummer: 7] 


Solche Zeichen sind ja meist irgendwelche Reste von Adressen o.ä. (pointer, etc.), leider komme ich nicht dahinter wo der Fehler stecken könnte.
Wäre nett wenn da jemand eine Idee hat.

Gruss,
Lalas


----------



## SlaterB (6. Jan 2009)

mann oh meter,


```
public class Test
{
    public static void main(String[] args)
        throws Exception
    {
        List a = new ArrayList();
        a.add(Integer.valueOf(1));
        a.add(Integer.valueOf(2));
        System.out.println(a);
        a.clear();
        System.out.println(a);
        a.add("x");
        a.add("y");
        System.out.println(a);
    }
}
```
fällt dir was auf?


----------



## lalas (6. Jan 2009)

Lieber SlaterB,

ich poste hier bewusst im "Anfänger-Bereich", dank deinem code sehe ich das bei der Ausgabe einer Liste, diese anscheinend immer in eckige Klammern gefasst wird.
Ich war davon ausgegangen, dass die toString-Methode der enthaltenden Objekte genutzt wird und einfach Elementweise ausgegeben wird.

Falls meine Aussage so nicht stimmt und ich was anderes in deinem code entdecken soll, wäre ich über einen Hinweis sehr erfreut.

Falls Dich meine Fragen abnerven (Zitat: "mann oh meter"), dann solltest Du vielleicht besser mit den Profis diskutieren und nicht hier im Anfängerbereich.

Dennoch vielen Dank für den Beispiel-code

Gruss,
Lalas


----------



## SlaterB (6. Jan 2009)

außer mir würde dir keiner antworten, denn meine Geduld ist bekanntlich die Größte 

aber ich lasse es mir dann zumindest nicht nehmen, auch Kommentare der Form 'selbst für einen Anfänger unterirdisch' dazuzuposten


----------



## lalas (6. Jan 2009)

SlaterB hat gesagt.:
			
		

> außer mir würde dir keiner antworten, denn meine Geduld ist bekanntlich die Größte



Na ob dies Forum dann das Richtige ist.... ? 

Aber trotzdem vielen Dank für Deine Geduld (und das meine ich wirklich so!)

Meine Hemmschwelle eine Frage zu stellen ist nun um einiges höher wie zuvor, vielleicht lasse ich Dich nun in Ruhe

Gruss,
Lalas


----------



## 0x7F800000 (6. Jan 2009)

SlaterB hat gesagt.:
			
		

> außer mir würde dir keiner antworten, denn meine Geduld ist bekanntlich die Größte


hab mir jetzt die diskussion hier durchgelesen... :shock:^^ 
SlaterB, du hast vollkommen recht, deine Geduld ist echt der Hammer, nach dem 3. post hätte ich den thread nicht mehr besucht :applaus: :applaus: :applaus: hut ab :toll:

Und nein, aus dem Vorhandensein einer nichtleeren Schnittmenge lässt sich keine partielle Ordnungsrelation herstellen, da diese relation ja immer symmetrisch ist, egal was man macht.


----------

