[Java]Classe generica MinMax

di il
10 risposte

[Java]Classe generica MinMax

Si definisca una classe generica MinMax che contiene due campi min, max del tipo generico. Poi scrivere un metodo generico che appartenente ad un'altra classe, che data in input una collezione, restituisce una elemento di tipo MinMax contenente il minimo e il massimo tra gli oggetti della collection fornita.

Questa è la mia soluzione.

=== CLASSE MINMAX ===

public class MinMax <T> {

	private T min, max;

	public MinMax(T min, T max){
		this.min = min;
		this.max = max;
	}
	
	public void print(){
		System.out.println("min:"+min+" max:"+max);
	}
}

=== METODO GETMINMAX (2 versioni) ===

public static <E extends Comparable<? super E>> MinMax<T> getMinMax1(List<E> l){
		
		Optional<E> max = l.stream().max(Comparator.naturalOrder());
		Optional<E> min = l.stream().min(Comparator.naturalOrder());
		
		 return (max!=null && min!=null) ? new MinMax(min,max) : null;
		
	}

public static <E extends Comparable<? super E>> MinMax<T> getMinMax2(List<E> l){
		
		l.sort(Comparator.naturalOrder());
		E min = l.get(0);
		E max = l.get(l.size()-1);
		
		return new MinMax(min,max);
	}
Nel I getMinMax ho utilizzato gli stream e il metodo max che necessariamente torna un tipo Optional, il che consente di prevenire l'eccezione nel caso in cui la lista sia vuota, controllando se il valore di ritorno è null. C'è da dire che la complessità di questo caso è O(n+n) dato che per due volte cerco prima il min e poi il max.

Nel II getMinMax, ordino dapprima la lista tramite l'ordinamento naturale degli elementi che implementeranno Comparable, poi estrapolo il primo elemento che sarà il min e l'ultimo il max. La complessità è solo O(n),leggermente più rapido ma comunque lineare come il precedente, ma non tiene conte dell'ArrayIndexOutOfBoundsException nel caso in cui la lista sia vuota.

Esiste un metodo migliore? Se utilizzassi un tipo che non implementa Comparable cosa succede?
Sono i miei primi approcci ai generici, oramai fondamentali in Java, ed accetto qualsiasi consiglio e delucidazione.

Saluti e buona giornata.


Update: o avrei dovuto direttamente nella definizione della classe, specificare che il tipo utilizzato deve essere ti tipo Comparable?

public class MinMax <T extends Comparable<? super T> {...}

10 Risposte

  • Re: [Java]Classe generica MinMax

    Paolovox ha scritto:


    Nel II getMinMax, ordino dapprima la lista tramite l'ordinamento naturale degli elementi che implementeranno Comparable, poi estrapolo il primo elemento che sarà il min e l'ultimo il max. La complessità è solo O(n),leggermente più rapido ma comunque lineare come il precedente, ma non tiene conte dell'ArrayIndexOutOfBoundsException nel caso in cui la lista sia vuota.
    No, non è più rapido. sort non è una banale scansione lineare! E oltretutto quel sort di List prima tira fuori un array con gli elementi, poi ordina l'array (con Arrays.sort) e poi risetta gli elementi nel List usando un ListIterator (non il banale Iterator).

    Per trovare min/max di una sequenza infatti di norma NON serve affatto "ordinare" la sequenza.

    Paolovox ha scritto:


    Esiste un metodo migliore?
    Sì, banale scansione lineare tenendosi il min e max e aggiornandoli man mano.

    Paolovox ha scritto:


    Se utilizzassi un tipo che non implementa Comparable cosa succede?
    Avendo messo il "bound" <E extends Comparable<? super E>> non puoi passare un List parametrizzato di un tipo non Comparable.
    Comunque il tuo primo getMinMax1 è pure sbagliato/dubbio:

    public static <E extends Comparable<? super E>> MinMax<T> getMinMax1(List<E> l){

    hai messo un <T> ... cosa è?

    E poi istanzi new MinMax senza parametrizzazione, che dovrebbe essere in teoria di Optional<E>. E poi siccome max()/min() di Stream non restituiscono mai null, le due condizioni per != null non hanno neanche granché senso.
  • Re: [Java]Classe generica MinMax

    Questo avrebbe senso (e sarebbe tutto type-safe):
    public static <E extends Comparable<? super E>> MinMax<Optional<E>> getMinMax(List<E> l) {
        Optional<E> max = l.stream().max(Comparator.naturalOrder());
        Optional<E> min = l.stream().min(Comparator.naturalOrder());
          
        return new MinMax<Optional<E>>(min, max);
    }
  • Re: [Java]Classe generica MinMax

    Nel I getMinMax ho utilizzato gli stream e il metodo max che necessariamente torna un tipo Optional, il che consente di prevenire l'eccezione nel caso in cui la lista sia vuota, controllando se il valore di ritorno è null. C'è da dire che la complessità di questo caso è O(n+n) dato che per due volte cerco prima il min e poi il max.
    Un semplice
    
    if(l.isEmpty()) {...}
    
    va più che bene per controllare se la lista è vuota.

    Inoltre per ottenere il minimo ed il massimo non c'è bisogno di stream(), ti basta usare i metodi
    Collections.min(Collection<? extends T> coll, Comparator<? super T> comp);
    Collections.max(Collection<? extends T> coll, Comparator<? super T> comp);
    c'è anche la versione overloaded che ti permette di usare solo i Comparable.
    Inoltre come vedi dalla signature dei due metodi puoi trovare il massimo ed il minimo tra gli elementi di una collezione anche se questi non implementano Comparable.

    Questi metodi sono molto efficienti.

    Nel II getMinMax, ordino dapprima la lista tramite l'ordinamento naturale degli elementi che implementeranno Comparable, poi estrapolo il primo elemento che sarà il min e l'ultimo il max. La complessità è solo O(n),leggermente più rapido ma comunque lineare come il precedente, ma non tiene conte dell'ArrayIndexOutOfBoundsException nel caso in cui la lista sia vuota
    In questo caso ti sbagli, perchè sort() nel caso medio ordina la lista con una variante del quick sort (il tim sort se non sbaglio) con complessità O(n*logn) (nel caso peggiore è O(n^2))
  • Re: [Java]Classe generica MinMax

    Posto che ordinare per prendere massimo e minimo è semplicemente demenziale, nella soluzione proposta la complessità è doppia, perchè ci sono due chiamate (una per il min, l'altra per il max), rispetto ad un singolo ciclo.
    Chiaramente tra cast impliciti-espliciti-nascosti-sotto-il-tappeto il tempo di esecuzione può anche essere addirittura migliore, ma per le qualità dell'implementazione (forse).

    operativamente è del tutto normale fare una funzione minmax che setta due "variabili" interne e due funzioni getmin e getmax (della classe intendo) che pigliano i rispettivi valori.
  • Re: [Java]Classe generica MinMax

    Grazie a tutti per le precisazioni.

    Penso che così possa andare, come ha suggerito xneo. Ho letto che il metodo Collections.max | min itera su tutti gli elementi, quindi il tempo è proporzionale al numero di elementi.
    
    public <E extends Comparable<? super E>> MinMax<E> getMinMax3(List<E> l){
    		
    		if(l.isEmpty()) return null;
    		
    		E max = Collections.max(l);
    		E min = Collections.min(l);
    		
    		return new MinMax<E>(min,max);	
    	}
    
    Così all'oggetto MinMax viene inferito il tipo della collezione e non un Optional col tipo della collezione.
  • Re: [Java]Classe generica MinMax

    @Paolovox
    Si definisca una classe generica MinMax che contiene due campi min, max del tipo generico. Poi scrivere un metodo generico che appartenente ad un'altra classe, che data in input una collezione, restituisce una elemento di tipo MinMax contenente il minimo e il massimo tra gli oggetti della collection fornita.
    Nota che il metodo per trovare il massimo ed il minimo non necessariamente deve prendere in input una lista, il metodo funzionerebbe con qualsiasi tipo di collection.
  • Re: [Java]Classe generica MinMax

    Ok generalizzato al massimo sarà allora:
    
    public <E extends Comparable<? super E>> MinMax<E> getMinMax4(Collection<E> l){
    		
    		if(l.isEmpty()) return null;
    		
    		E max = Collections.max(l);			
    		E min = Collections.min(l);			
    		
    		return new MinMax<E>(min,max);
    		
    	}
    
    
  • Re: [Java]Classe generica MinMax

    Posso vincolare il tipo di parametro generico della classe con un upper bound all'interfaccia Comparable.
    
    public class MinMax <T extends Comparable< ? super T>> {
    	
    		private T min, max;
    		
    		public MinMax(){}
    		
    		public MinMax(T min, T max){
    			this.min = min;
    			this.max = max;
    		}
    		
    	
    		public void print(){
    			System.out.println("min:"+min+" max:"+max);
    		}
    	
    	
    	public MinMax<T> getMinMax(Collection<T> l){
    		if(l.isEmpty()) return null;
    		
    		T max = Collections.max(l);			
    		T min = Collections.min(l);			
    		
    		return new MinMax<T>(min,max);
    	}
    	
    	public static void main(String[] args) {
    		
    		MinMax<Integer> mm1 = new MinMax<>();
    		MinMax<String>  mm2 = new MinMax<>();
    		mm1.getMinMax(Arrays.asList(23,42,77,8,3)).print();	
    		mm2.getMinMax(Arrays.asList("aaa","ccc","b","a","ccd","dffg")).print();
    		
    	}
    }
    
  • Re: [Java]Classe generica MinMax

    Paolovox ha scritto:


    Posso vincolare il tipo di parametro generico della classe con un upper bound all'interfaccia Comparable.
    Certo che puoi farlo. Ma questo restringe solo una parametrizzazione concreta di MinMax. Ovvero puoi istanziare un MinMax<String> ma non un MinMax<Number> (perché Number NON è Comparable).

    E tieni anche presente una cosa. Nel tuo ultimo codice il getMinMax l'hai messo dentro la classe MinMax. Questo vuol dire che il T di getMinMax è quello della classe, che è bounded. E quindi va bene per i max()/min().

    Ma se getMinMax lo mettessi in un'altra classe distinta da MinMax, allora non puoi avere solamente

    public <T> MinMax<T> getMinMax(Collection<T> l)

    devi comunque rimettere il bound su T, anche se MinMax è già bounded.
  • Re: [Java]Classe generica MinMax

    Certo così:
    
    public static <T extends Comparable<? super T>> MinMax<T> getMinMaxx(Collection<T> l){
    		if(l.isEmpty()) return null;
    		
    		T max = Collections.max(l);			
    		T min = Collections.min(l);			
    		
    		return new MinMax<T>(min,max);	
    	}
    
    Ti ringrazio
Devi accedere o registrarti per scrivere nel forum
10 risposte