Podział listy na podzbiory - interface Comparable

0

Muszę zaimplementować generowanie wykresów z pewnego zbioru danych.
Dane te będą pochodziły z bazy danych i będą miały postać:
SELECT column, count(column) from tabele where warunek1 group by column order by column;
SELECT column, count(column) from tabele where warunek2 group by column order by column;

Kolumnowy wykres będzie przedstawiał liczność w każdym z przedziałów. W praktyce
użytkownik może dodawać, usuwać oraz ustawiać każdy z poszczególnych przedziałów,
a zmiany na wykresie mają być nanoszone dynamicznie. Do rysowania wykresów
używam biblioteki JFreeChart. Teraz kawałek kodu:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test<E extends Comparable<E>> {
	List<Element<E>> data1 = new ArrayList<Element<E>>();
	List<Element<E>> data2 = new ArrayList<Element<E>>();

	List<E> divs = new ArrayList<E>();
	
	List<Integer> cardinality1 = new ArrayList<Integer>();
	List<Integer> cardinality2 = new ArrayList<Integer>();
	
	public void doIt() {
		Iterator<Element<E>> iter = data1.iterator();
		boolean overflow = false;
		
		a:
		for(int i = 0; i < divs.size(); i++) {
			while(iter.hasNext()) {
				Element<E> e = iter.next();
				if(e.element.compareTo(divs.get(i)) <= 0) {
					cardinality1.set(i, cardinality1.get(i) + e.cardinality);
				} else {
					/* W przypadku gdyby ostatni element nie okazał się końcowym */
					if(i + 1 >= cardinality1.size()) {
						overflow = true;
						break;
					}
					cardinality1.set(i + 1, e.cardinality);
					continue a;
				}
			}
			break;
		}
		
		/* Jesli jakies elementy zostaly to dodaj je na koniec */
		if(!overflow) {
			int size = 0;
			while(iter.hasNext()) {
				size += iter.next().cardinality;
			}
			int last = cardinality1.size() - 1;
			cardinality1.set(last, cardinality1.get(last) + size);
		}
		
		/* To samo dla drugiego przedziału ...*/
	}
	
	public static void main(String[] args) {
		Test<String> test = new Test<String>();
		test.data1.add(new Element<String>("ala", 3));
		test.data1.add(new Element<String>("alicja", 5));
		test.data1.add(new Element<String>("anna", 7));
		test.data1.add(new Element<String>("basia", 8));
		test.data1.add(new Element<String>("beata", 1));
		test.data1.add(new Element<String>("celina", 1));
		test.data1.add(new Element<String>("dagmara", 4));
		test.data1.add(new Element<String>("dominika", 9));
		test.data1.add(new Element<String>("elzbieta", 2));
		test.data1.add(new Element<String>("filipina", 5));
		
		/* Przedziały pochodzą ze zbioru wyżej */
		test.divs.add("ala");
		test.divs.add("beata");
		test.divs.add("dagmara");
		test.divs.add("filipina");
		
		/* Zerujemy każdy z przedziałów */
		test.cardinality1.add(0);
		test.cardinality1.add(0);
		test.cardinality1.add(0);
		test.cardinality1.add(0);
		
		/* Przykład działania */
		test.doIt();
		for(Integer i : test.cardinality1) {
			System.out.println(i);
		}
	}
}

class Element<E> {
	E element;
	int cardinality;
	
	public Element(E element, int cardinality) {
		this.element = element;
		this.cardinality = cardinality;
	}
}

Pierwsze pytanie : czy da się to zrobić prościej?
Oczywiście nie przeliczam wszystkiego za każdym razem tylko zmieniam poszczególne przedziały (kodu nie wrzucam).
Po każdym przeliczeniu liczności tworzę DefaultCategoryDataset i nanoszę go na wykres.

Drugie pytanie związane jest z samą javą:
Takie rozwiązanie ma jeden problem - nie każda klasa posiada interface Comparable<E>.
Głównie chodzi mi o klasę java.sql.Date. java.sql.Date dziedziczy po java.util.Date która implementuje Comparable<java.utli.Date>, jednak sama
nie może zaimplementować drugi raz interfacu Comparable (swoją drogą mogli by to wprowadzić kiedyś) i o
ile java.sql.Date nie dodaje żadnych pól u siebie i można by stosować bezpiecznie metode compareTo() z klasy java.util.Date to już Timestamp dodaje pole nanos.
Jak zrobić to bardziej przenośnym?
Mogę oczywiście dostarczać komparator do każdej klasy ale to jest kopiowanie kodu, który potrzebny jest tylko w paru przypadkach.

public class Test<E, T extends Comparator<E>> 

Mogę też nie przejmować się i traktować zarówno java.sql.Date oraz Timestamp jak java.util.Date, ale nie jestem pewien tego rozwiązania.

Pozdrawiam.

0

Wrzucam trochę zmodyfikowany kod aby bardziej naświetlić mój problem :

 
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class DataSet<E extends Comparable<E>> {
	List<Element<? extends E>> data1 = new ArrayList<Element<? extends E>>();
	List<Element<? extends E>> data2 = new ArrayList<Element<? extends E>>();

	List<E> divs = new ArrayList<E>();
	
	List<Integer> cardinality1 = new ArrayList<Integer>();
	List<Integer> cardinality2 = new ArrayList<Integer>();
	
	public void doIt() {
		/* Zerujemy każdy z przedziałów */
		cardinality1.clear();
		cardinality2.clear();
		for(int i = 0; i < divs.size(); i++) {
			cardinality1.add(0);
			cardinality2.add(0);
		}
		
		ListIterator<Element<? extends E>> iter = data1.listIterator();
		
		a:
		for(int i = 0; i < divs.size(); i++) {
			while(iter.hasNext()) {
				Element<? extends E> e = iter.next();
				if(e.element.compareTo(divs.get(i)) <= 0) {
					cardinality1.set(i, cardinality1.get(i) + e.cardinality);
				} else {
					iter.previous();
					continue a;
				}
			}
			break;
		}
		
		/* To samo dla drugiego przedziału ...*/
		iter = data2.listIterator();
		
		a:
		for(int i = 0; i < divs.size(); i++) {
			while(iter.hasNext()) {
				Element<? extends E> e = iter.next();
				if(e.element.compareTo(divs.get(i)) <= 0) {
					cardinality2.set(i, cardinality2.get(i) + e.cardinality);
				} else {
					iter.previous();
					continue a;
				}
			}
			break;
		}
	}
	
	public static void main(String[] args) {
		/* DataSet<B> ds = new DataSet<B>(); error*/
		DataSet<A> ds = new DataSet<A>();
		A a = new B(2);
		B b = new B(6);
		B b2 = new B(10);
		ds.data2.add(new Element<A>(a, 3));
		ds.data2.add(new Element<B>(new B(4), 3));
		ds.data2.add(new Element<B>(b2, 5));
		ds.data2.add(new Element<A>(new B(7), 5));
		ds.data2.add(new Element<B>(b, 5));
		ds.divs.add(a);
		ds.divs.add(b2);
		ds.divs.add(b);
		ds.doIt();
	}
}

class A implements Comparable<A> {
	Integer i;
	public A(Integer i) {
		this.i = i;
	}
	@Override
	public int compareTo(A o) {
		System.out.println("A compare to A");
		return i.compareTo(o.i);
	}
	
}

class B extends A /*  implements Comparable<B>  */{
	public B(Integer i) {
		super(i);
	}

	public int compareTo(A o) {
		/* ( 1 ) */
		if(o instanceof B) {
			return compareTo((B)o);
		}
		System.out.println("B compare to A");
		return i.compareTo(o.i);
	}
	
	public int compareTo(B o) {
		System.out.println("B compare to B");
		return i.compareTo(o.i);
	}
}

Gdy usuniemy if'a znajdującego się pod komentarzem nr ( 1 ) to "B comapre to B" nigdy nie zostanie wyświetlone - nie wiem czemu przeciążenie funkcji tutaj nie działa :/

0

Nie mam siły analizować całego kodu, ale o jakim nie występującym przeciążeniu Ty mówisz?

A a1 = new A(3);
B b1 = new B(3);
B b2 = new B(4);
System.out.println(b1.compareTo(b2)) //B compare to B  -1
System.out.println(a1.compareTo(b1)) //A compare to A   0
System.out.println(b1.compareTo(a1)) //B compare to A   0

Oczekujesz innych komunikatów?

0
bogdans napisał(a)

Nie mam siły analizować całego kodu, ale o jakim nie występującym przeciążeniu Ty mówisz?

A a1 = new A(3);
B b1 = new B(3);
B b2 = new B(4);
System.out.println(b1.compareTo(b2)) //B compare to B  -1
System.out.println(a1.compareTo(b1)) //A compare to A   0
System.out.println(b1.compareTo(a1)) //B compare to A   0

Oczekujesz innych komunikatów?

Komunikaty tą ok ale nie to jest istotą problemu. Nakreśle to inaczej. Masz klasę DataSet, A, B. I teraz jak z poziomu klasy dataset wywolac metode b.compareTo(b) bez ingerencji w kod klasy B. Nie mogę stworzyć DataSet<B> bo B nie implementuje bezpośrednio interfejsu Comparable, a druga raz też nie może go zaimplementować. Jedynie mogę stworzyć DataSet<A> i wrzucać tam obiekty typu B ale wtedy zostanie wywołana metoda compareTo z klasy A. Oto istota problemu.

0

Wydaje mi się, że żądasz niemożliwego. Klasa B dziedziczy po klasie A, w kolekcji jest element a z klasy A i elementy b1 i b2 z klasy B. Sortujemy, o ile Ciebie rozumiem, to chcesz by przy porównywaniu b1 i b2 został użyta metoda compareTo z klasy B. To jak posortować, jeżeli wg compareTo z klasy A kolejność jest taka:
b2>a>b1, a wg compareTo z klasy B b1>b2?
Bardzo łatwo do takiej sytuacji doprowadzić, sortujemy wg pewnej wartości liczbowej, w klasie A malejąco, a w klasie B rosnąco.

0

Nie zachodzi przechodniość przy klasach parametryzowanych. DataSet<A> nie ma nic wspólnego z DataSet<B> bez względu na to jakie są związki między A i B. W tym przypadku jeżeli B dziedziczy po A, to po konkretyzacji klasą A wszystkie parametry E są traktowane tak jakby były klasą A. Gdyby dziedziczenia nie było, to dostałbyś po prostu błąd kompilacji. Nie ma tu mowy o żadnym przeciążaniu bo żadnego być nie może.
Już pierwszy błąd w main nakierowuje Cię gdzie jest błąd: Masz problem z utworzeniem DataSet<B> ponieważ Twój komentarz w nagłówku klasy B (implements Comparable<B></code>) jest fałszywy. Chodzi o to, że o ile A jest typu <code>Comparable<A> (została w ten sposób skonkretyzowana), to klasa B dziedzicząca po A jest nadal Comparable<A></code>. Dlatego każde użycie B w miejscu A wykorzystuje własność dziedziczenia i robi downgrade B do A. Stąd i metoda compareTo jest skonkretyzowana i wymagana do implementacji dla typu A, a drugie <code>compareTo(B o) jest Twoją nigdy nie używaną nadgorliwością. :)

Co do Twojego właściwego pytania ("nie każda klasa posiada interface Comparable<E>") rozwiązaniem takich przypadków jest to co mówi każdy podręcznik do języków obiektowych: Tworzy się klasy adapterów, które mają własności oczekiwane przez resztę kodu. W tym wypadku powinieneś utworzyć sobie jakiś interfejs X, który będzie Comparable<X>, a następnie adaptery, które przyjmując obiekty różnych klas będą zawsze implementować X. Dla klas, które tego nie potrzebują dostarcz zawsze jakiś domyślny adapter implementujący X.

0

Rozumiem,
Dziękuje.

1 użytkowników online, w tym zalogowanych: 0, gości: 1