ArrayList<T>: Problem mit contains() bei parametrisiertem T

Status
Nicht offen für weitere Antworten.

automatix

Aktives Mitglied
Hallo!

A a = new T();
A b = new T();
ArrayList<T> arrayList = new ArrayList<T>();
arrayList.add(a);
WENN a.equals(b) DANN arrayList.contains(b) is true

Dies ist die Logik von ArrayList#contains(Object). (Oder?) In meinem Programm funktioniert sie aber leider nicht, wenn T ein parametrisierter Datentyp ist.

Bei diesem Code:

Java:
		// PointS
		Point testPointA = new Point(1, 2);
		Point testPointB = new Point(1, 2);
		// PointPairS
		EqualPair<Point> testPointPairA = new EqualPair<Point>(testPointA, testPointB);
		EqualPair<Point> testPointPairB = new EqualPair<Point>(testPointA, testPointB);
		// PointLisS
		ArrayList<Point> testPointList = new ArrayList<Point>();
		testPointList.add(testPointA);
		// PointListPairS
		ArrayList<EqualPair<Point>> testPointPairList = new ArrayList<EqualPair<Point>>();
		testPointPairList.add(testPointPairA);
		// SysoS
		System.out.println(testPointA.equals(testPointB));
		System.out.println(testPointPairA.equals(testPointPairB));
		System.out.println(testPointList.contains(testPointB));
		System.out.println(testPointPairList.contains(testPointPairB));
erwarte ich die Ausgabe
true
true
true
true

und kriege stattdessen
true
true
true
false

Kann jemand das Phänomen erklären?

Danke
 
edit: noch in Arbeit, erste Antwort war zu schnell 😉

edit2:
System.out.println(testPointPairA.equals(testPointPairB)); liefert true,
aber auch
System.out.println(testPointPairB.equals(testPointPairA));
?
ein vollständiges Programm wäre hilfreich
 
Ne, die EqualPair#equals(...) ist OK und die liefert auch das gewünschte Ergebnis. Das Problem ist, dass die contains(...) die Object#equals(...) nimmt. Die contains(...) hat ja Object als Parametertyp und weiß gar nicht, was da genau als Input kommt, also nimmt sie die Object#equals(...).

OK, Phänomen erklärt. Mögliche Lösung: von ArrayList ableiten, contains(...) überschreiben und dort Typenerkennung integrieren.
 
es gibt nur eine equals-Methode die sich auch equals-Methode nennen darf,

dann hast du wahrscheinlich in EqualPair eine andere Methode equals(EqualPair) defininiert,
die ist tatsächlich relativ nutzlos

programmiere equals(Object), dann klappt es auch mit allen Listen und sonstigen APIs in der Java-Welt,
extra ne neue Liste zu schreiben wäre der falsche Weg
 
Hallo zusammen!

Erstmal vielen Dank für aufschlussreiches Feedback!

Ihr hattet Recht -- ich habe natürlich die equals(...) falsch implementiert. Jetzt hab ich den Fehler behoben und die Welt ist wieder in Ordnung. 🙂

Hier der Code:
- Pair
- EqualPair
- NonEqualPair
- ExtendedArrayList
- Main

Klasse Pair:
Java:
package lib;

/**
 * Stellt ein Paar aus zwei Objekten vom selben Typ.
 * @param <T>
 */
public class Pair<T> {
	
	/**
	 * Element A
	 */
	private T elementA;
	/**
	 * Element B
	 */
	private T elementB;
	
	/**
	 * Erzeugt ein neues Paar-Objekt.
	 * @param elementA
	 * @param elementB
	 */
	public Pair(T elementA, T elementB) {
		setElementA(elementA);
		setElementB(elementB);
	}
	
	/**
	 * @return the elementA
	 */
	public T getElementA() {
		return elementA;
	}
	/**
	 * @return the elementB
	 */
	public T getElementB() {
		return elementB;
	}
	/**
	 * @param elementA the elementA to set
	 */
	public void setElementA(T elementA) {
		this.elementA = elementA;
	}
	/**
	 * @param elementB the elementB to set
	 */
	public void setElementB(T elementB) {
		this.elementB = elementB;
	}
	
	/**
	 * Überschreibt {@link Object#toString()} und
	 * liefert das Objekt in Form von
	 * 	elementA + ":" + elementB
	 * .
	 */
	@Override
	public String toString() {
		return elementA + ":" + elementB;
	}
	
	/**
	 * Schreibt das Objekt mit Hilfe der {@link #toString()} auf die Konsole.
	 */
	public void toScreen() {
		System.out.print(toString());
	}
	
}

Klasse EqualPair
Java:
package lib;

/**
 * Repräsentiert ein Paar,
 * dessen Elemente "gleichberechtigt" sind.
 * D.h., dass zwei EqualPair-Objekte, die denselben Elemente als Attribut-Werte haben,
 * auch dann gleich sind,
 * 	wenn diese Elemente "vertauscht", also unterschiedlichen Attributen zugewiesen sind
 * .
 * Bsp.:
 * 	meinEqualPair.ElementA = 1
 * 	meinEqualPair.ElementB = 2
 * 	deinEqualPair.ElementA = 2
 * 	deinEqualPair.ElementB = 1
 * 	meinEqualPair ist gleich deinEqualPair
 * @param <T>
 */
public class EqualPair<T> extends Pair<T> {
	
	/**
	 * Erzeugt ein neues EqualPair-Objekt.
	 * @param elementA
	 * @param elementB
	 */
	public EqualPair(T elementA, T elementB) {
		super(elementA, elementB);
	}
	
//	Fehlerhafte Implementierung der equals(...) !!!
//	/**
//	 * Vergleicht das EqualPair-Objekt mit einem anderen Paar-Objekt.
//	 * IF
//	 * 	(this.elementA == anotherPair.elementA AND this.elementB == anotherPair.elementB) OR
//	 * 	(this.elementA == anotherPair.elementB AND this.elementB == anotherPair.elementA)
//	 * THEN
//	 * 	return true
//	 * @return boolean
//	 */
//	public boolean equals(Pair<T> anotherPair) {
////		System.out.println(123);
//		return (
//			(
//				getElementA().equals(anotherPair.getElementA()) &&
//				getElementB().equals(anotherPair.getElementB())
//			) || (
//				getElementA().equals(anotherPair.getElementB()) &&
//				getElementB().equals(anotherPair.getElementA())
//			)
//		);
//	}
	/**
	 * Vergleicht das EqualPair-Objekt mit einem anderen Paar-Objekt.
	 * IF
	 * 	anotherPair instanceof Pair AND (
	 * 		(this.elementA EQUALS anotherPair.elementA AND this.elementB EQUALS anotherPair.elementB) OR
	 * 		(this.elementA EQUALS anotherPair.elementB AND this.elementB EQUALS anotherPair.elementA)
	 * 	)
	 * THEN
	 * 	return true
	 * ELSE
	 * 	return false
	 * @return boolean
	 */
	@Override
	public boolean equals(Object anotherPair) {
		if (this == anotherPair) {
			return true;
		} else if (anotherPair == null) {
			return false;
		} else if (!(anotherPair instanceof Pair)) {
			return false;
		} else if (
			(
				getElementA().equals(((EqualPair) anotherPair).getElementA()) &&
				getElementB().equals(((EqualPair) anotherPair).getElementB())
			) || (
				getElementA().equals(((EqualPair) anotherPair).getElementB()) &&
				getElementB().equals(((EqualPair) anotherPair).getElementA())
			)
		) {
			return true;
		} else {
			return false;
		}
	}
	
}

Klasse NonEqualPair
Java:
package lib;

/**
 * Repräsentiert ein Paar,
 * dessen Elemente "nicht gleichberechtigt" sind.
 * D.h., dass zwei NonEqualPair-Objekte, die denselben Elemente als Attribut-Werte haben,
 * nur dann gleich sind,
 * 	wenn diese Elemente nicht "vertauscht", also den gleichen Attributen zugewiesen sind
 * .
 * Bsp. 1:
 * 	meinEqualPair.ElementA = 1
 * 	meinEqualPair.ElementB = 2
 * 	deinEqualPair.ElementA = 1
 * 	deinEqualPair.ElementB = 2
 * 	meinEqualPair ist gleich deinEqualPair
 * Bsp. 2:
 * 	meinEqualPair.ElementA = 1
 * 	meinEqualPair.ElementB = 2
 * 	deinEqualPair.ElementA = 2
 * 	deinEqualPair.ElementB = 1
 * 	meinEqualPair ist ungleich deinEqualPair
 * @param <T>
 */
public class NonEqualPair<T> extends Pair<T> {
	
	/**
	 * Erzeugt ein neues EqualPair-Objekt.
	 * @param elementA
	 * @param elementB
	 */
	public NonEqualPair(T elementA, T elementB) {
		super(elementA, elementB);
	}

//	Fehlerhafte Implementierung der equals(...) !!!
//	/**
//	 * Vergleicht das NonEqualPair-Objekt mit einem anderen Paar-Objekt.
//	 * IF
//	 * 	(this.elementA == anotherPair.elementA AND this.elementB == anotherPair.elementB)
//	 * THEN
//	 * 	return true
//	 * ELSE
//	 * 	return false
//	 * @return boolean
//	 */
//	public boolean equals(Pair<T> anotherPair) {
//		return (
//			getElementA().equals(anotherPair.getElementA()) &&
//			getElementB().equals(anotherPair.getElementB())
//		);
//	}
	
	/**
	 * Vergleicht das EqualPair-Objekt mit einem anderen Paar-Objekt.
	 * IF
	 * 	anotherPair instanceof Pair AND (
	 * 		(this.elementA EQUALS anotherPair.elementA AND this.elementB EQUALS anotherPair.elementB) OR
	 * 		(this.elementA EQUALS anotherPair.elementB AND this.elementB EQUALS anotherPair.elementA)
	 * 	)
	 * THEN
	 * 	return true
	 * ELSE
	 * 	return false
	 * @return boolean
	 */
	@Override
	public boolean equals(Object anotherPair) {
		if (this == anotherPair) {
			return true;
		} else if (anotherPair == null) {
			return false;
		} else if (!(anotherPair instanceof Pair)) {
			return false;
		} else if (
			(
				getElementA().equals(((NonEqualPair) anotherPair).getElementA()) &&
				getElementB().equals(((NonEqualPair) anotherPair).getElementB())
			)
		) {
			return true;
		} else {
			return false;
		}
	}
	
}

Klasse ExtendedArrayList (der relevante Teil)
Java:
package lib;

import java.util.ArrayList;

/**
 * Erweitert die ArrayList und
 * stellt zusätzliche Funktionalität zur Verfügung.
 * @param <T>
 */
public class ExtendedArrayList<T> extends ArrayList<T> {
	
	// ...
	
	/**
	 * Liefert die Liste aller aus den Elementen der bestehenden Liste generieten Paare.
	 * Ist ein Paar A:B bereits in der Output-Liste enthalten, wird das Paar B:A nicht mehr hinzugefügt.
	 * Es werden keine Paare A:A generiert.
	 * LängeDerOutputListe = (LängeDerOutputListe ^ 2 - LängeDerOutputListe) / 2
	 * Bsp.:
	 * 	Input-Liste: A, B, C
	 * 	Output-Liste: A:B, A:C, B:C
	 * @param singleList
	 * @return Liste aller aus den Elementen der bestehenden Liste generieten Paare
	 */
	public ExtendedArrayList<EqualPair<T>> singleList2EqualPairList(ArrayList<T> singleList) {
		ArrayList<T> tempList = (ArrayList<T>) singleList.clone();
		ExtendedArrayList<EqualPair<T>> pairList = new ExtendedArrayList<EqualPair<T>>();
		for (int i = 0; i < tempList.size(); i++) {
			for (int j = 0; j < tempList.size(); j++) {
				if (
					!tempList.get(j).equals(tempList.get(i)) &&
					!tempList.contains(new EqualPair<T>(tempList.get(i), tempList.get(j)))
				) {
					pairList.add(new EqualPair<T>(tempList.get(i), tempList.get(j)));
				}
			}
		}
		return pairList;
	}
	// ...
	
	/**
	 * Liefert die Liste aller aus den Elementen der Input-Liste generieten Paare.
	 * Es werden keine Paare A:A generiert.
	 * LängeDerOutputListe = (LängeDerOutputListe ^ 2 - LängeDerOutputListe)
	 * Bsp.:
	 * 	Input-Liste: A, B, C
	 * 	Output-Liste: A:B, A:C, B:A, B:C, C:A, C:B
	 * @return Liste aller aus den Elementen der Input-Liste generieten Paare
	 */
	public ExtendedArrayList<NonEqualPair<T>> singleList2NonEqualPairList(ArrayList<T> singleList) {
		ArrayList<T> tempList = (ArrayList<T>) this.clone();
		ExtendedArrayList<NonEqualPair<T>> pairList = new ExtendedArrayList<NonEqualPair<T>>();
		for (int i = 0; i < tempList.size(); i++) {
			for (int j = 0; j < tempList.size(); j++) {
				if (
					!tempList.get(j).equals(tempList.get(i))
				) {
					pairList.add(new NonEqualPair<T>(tempList.get(i), tempList.get(j)));
				}
			}
		}
		return pairList;
	}
	
}

Klasse Main (der relevante Teil)
Java:
package main;

import java.awt.Point;
import java.util.ArrayList;

public class Main {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
//		// TEST NonEqualPair
//		// PointS
//		Point testPointA = new Point(1, 2);
//		Point testPointB = new Point(2, 1);
//		// PointPairS
//		NonEqualPair<Point> testPointPairA = new NonEqualPair<Point>(testPointA, testPointB);
//		NonEqualPair<Point> testPointPairB = new NonEqualPair<Point>(testPointB, testPointA);
//		// PointLisS
//		ArrayList<Point> testPointList = new ArrayList<Point>();
//		testPointList.add(testPointA);
//		// PointListPairS
//		ArrayList<NonEqualPair<Point>> testPointPairList = new ArrayList<NonEqualPair<Point>>();
//		testPointPairList.add(testPointPairA);
//		// SysoS
//		System.out.println(testPointA.equals(testPointB));
//		System.out.println(testPointPairA.equals(testPointPairB));
//		System.out.println(testPointList.contains(testPointB));
//		System.out.println(testPointPairList.contains(testPointPairB));

//		// TEST EqualPair
//		// PointS
//		Point testPointA = new Point(1, 2);
//		Point testPointB = new Point(2, 1);
//		// PointPairS
//		EqualPair<Point> testPointPairA = new EqualPair<Point>(testPointA, testPointB);
//		EqualPair<Point> testPointPairB = new EqualPair<Point>(testPointB, testPointA);
//		// PointLisS
//		ArrayList<Point> testPointList = new ArrayList<Point>();
//		testPointList.add(testPointA);
//		// PointListPairS
//		ArrayList<EqualPair<Point>> testPointPairList = new ArrayList<EqualPair<Point>>();
//		testPointPairList.add(testPointPairA);
//		// SysoS
//		System.out.println(testPointA.equals(testPointB));
//		System.out.println(testPointPairA.equals(testPointPairB));
//		System.out.println(testPointList.contains(testPointB));
//		System.out.println(testPointPairList.contains(testPointPairB));
		
	}

}

Nochmal danke @all und einen schönen Freitagabend!
 
Wenn du in Zukunft ein @Override über jede Methode schreibst, die du überschreiben möchtest, dann meckert der Compiler sobald du etwas nicht genauso machst, wie vorgesehen.

Java:
@Override
public boolean equals(Object other){
    // ...
}
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben