Array di booleani, errore logico

di il
11 risposte

Array di booleani, errore logico

Salve a tutti, mi spiace essere sempre qui ma ho ancora bisogno di aiuto.
Ho un esercizio che mi chiede di scrivere un metodo che rimuova, in un array dato di char, tutti i duplicati.
Mi è stato dato inoltre un suggerimento: creare un array booleano della stessa dimensione dell'array inserito per tenere traccia dei caratteri da mantenere. I valori del nuovo array booleano determineranno la dimensione dell'array da restituire.

Questo è il codice che ho scritto

public static char[] rimuoviDuplicati(char[] in) {
		int count = 0;
		int lunghezza = in.length;
		boolean[] array = new boolean[in.length];
		for (int i = 0; i<in.length; i++) {
			for (int z = 1; z<in.length-1; z++) {
				if(in[i] == in[z]) {
					array[count] = true;
				}
			}
			count++;
		}
		
		System.out.println(array);    
		
		for (int i=0; i<array.length; i++) {
			if (array[i]) {
				lunghezza = lunghezza - 1;
			}
		}
		count = 0;
		char[] risultato = new char[lunghezza];
		for (int i = 0; i<in.length; i++) {
			if (array[i] != true) {
				risultato[count] = in[i];
			}
			count++;
		}
		return risultato;
	}
Il problema sta nell'array booleano chiamato array: quando lo stampo a schermo il risultato è il seguente : [Z@7852e922 Come è possibile? Perché non sono presenti solo valori true e false?

Oltre a questo, ovviamente se avete suggerimenti per una scorciatoia, sono ben accetti.
Grazie

11 Risposte

  • Re: Array di booleani, errore logico

    SrJustEasy ha scritto:


    Il problema sta nell'array booleano chiamato array: quando lo stampo a schermo il risultato è il seguente : [Z@7852e922 Come è possibile? Perché non sono presenti solo valori true e false?
    Quando stampi un array ottieni qualcosa di simile a quello che hai postato, una sequenza di caratteri ben poco informativi su dove viene salvato l'array etc.
    Ti aspettavi che passando al metodo print() un array venissero stampati gli elementi all'interno ? Se sì forse è perché hai già usato le liste, che ridefiniscono il metodo toString() proprio per ottenere questo comportamento.
    Ma ciò non vale appunto per gli array, quindi o cicli tu manualmente sull'array per stampare gli elementi (magari ti crei un tuo metodo che ti formatti la stampa come desideri) o, se vuoi, puoi utilizzare il toString della classe java.util.Arrays.

    SrJustEasy ha scritto:


    Oltre a questo, ovviamente se avete suggerimenti per una scorciatoia, sono ben accetti.
    Grazie
    Hai verificato che il codice fornisca il risultato giusto come output? A me non sembra possa funzionare a occhio.
    Intanto nel primo ciclo innestato parti da 1, cosa che va bene per il primo elemento per non essere confrontato con sé stesso, ma non va più bene per gli altri elementi. Anche confrontare fino a lunghezza - 1 non è corretto, se il primo e l'ultimo carattere fossero uguali non te ne accorgeresti.
    Ma soprattutto, l'errore più grande è che se due caratteri centrali (non il primo o l'ultimo visto gli indici che stai usando) fossero uguali li rimuoveresti tutti e due dal risultato, visto che array conterrebbe true in entrambe le posizioni.

    Rivedi bene questa parte!

    Per il resto, seguendo sempre il consiglio fornito dalla traccia, puoi comunque rimuovere un ciclo. Il secondo ciclo infatti, quello in cui decrementi lunghezza, è inutile se organizzi un po' meglio il codice.

    Nel primo ciclo non ha molto senso incrementare count ad ogni elemento dell'array, lo stai usando solo come indice di "array" (a proposito, usa nomi informativi per le variabili, in questo caso ti converrebbe chiamare il vettore "duplicati" se lo usi per tenere traccia di quali elementi sono ripetuti). Ma come indice di array puoi usare "i", perché il ciclo esterno lo stai facendo per ogni elemento!
    Quindi count potresti incrementarlo solo quando trovi un doppione, in questo modo puoi ricavare la lunghezza di risultato semplicemente facendo lunghezza - count.
  • Re: Array di booleani, errore logico

    Gli array NON ridefiniscono il metodo toString(). Resta quello "ereditato" da Object, che sa fornire SOLO quella forma.
    Se vuoi una rappresentazione utile di un array, o fai un ciclo e stampi tu gli elementi oppure puoi ottenere un String tramite Arrays.toString (java.util.Arrays)
  • Re: Array di booleani, errore logico

    Ansharja ha scritto:



    Quando stampi un array ottieni qualcosa di simile a quello che hai postato, una sequenza di caratteri ben poco informativi su dove viene salvato l'array etc.
    Ti aspettavi che passando al metodo print() un array venissero stampati gli elementi all'interno ? Se sì forse è perché hai già usato le liste, che ridefiniscono il metodo toString() proprio per ottenere questo comportamento.
    Ma ciò non vale appunto per gli array, quindi o cicli tu manualmente sull'array per stampare gli elementi (magari ti crei un tuo metodo che ti formatti la stampa come desideri) o, se vuoi, puoi utilizzare il toString della classe java.util.Arrays.

    andbin ha scritto:


    Gli array NON ridefiniscono il metodo toString(). Resta quello "ereditato" da Object, che sa fornire SOLO quella forma.
    Se vuoi una rappresentazione utile di un array, o fai un ciclo e stampi tu gli elementi oppure puoi ottenere un String tramite Arrays.toString (java.util.Arrays)
    Si mi aspettavo mi ritornasse gli elementi all'interno perché è ciò che succedeva con numeri, stringhe o char

    Comunque ho risolto ogni cosa, come detto da voi c'erano diversi errori, ma il più grande è il fatto che non avevo pensato ai casi in cui i coincide con z e i<z : nel primo caso è ovvio che nell'array booleano ogni valore, alla fine, diventi true; nel secondo caso mi restituirebbe true a tutti e due i valori, non solo ai doppioni, se non ponessi dei limiti.
    Il risultato è questo:
    
    public static char[] rimuoviDuplicati(char[] in) {
    		int contoDoppioni = 0;
    		int lunghezza = in.length;
    		boolean[] verifica = new boolean[in.length];
    		for (int i = 0; i<in.length; i++) {
    			for (int z = 1; z<in.length; z++) {
    				if (i!=z && i<z) {
    					if(in[i] == in[z]) {
    						verifica[z] = true;
    						contoDoppioni++;
    					}
    				}
    			}
    		}
    		lunghezza = lunghezza-contoDoppioni;
    		int indiceRisultato = 0;
    		char[] risultato = new char[lunghezza];
    		for (int i = 0; i<in.length; i++) {
    			if (verifica[i] != true) {
    				risultato[indiceRisultato] = in[i];
    			}
    			indiceRisultato++;
    		}
    		return risultato;
    	}
    
    Tutto a posto, no?
  • Re: Array di booleani, errore logico

    SrJustEasy ha scritto:


    Tutto a posto, no?
    No, purtroppo è ancora abbastanza sbagliato. Innanzitutto la logica della prima parte (quella con i 2 for innestati) sarebbe parecchio ottimizzabile in termini di numero di cicli da fare, semplicemente ragionando meglio su come far progredire i due indici.

    Poi comunque a fronte di un array es. { 'A', 'C', 'B', 'A', 'D', 'B', 'A' } il contoDoppioni risulta 4 ma non ha senso poiché invece sarebbero 3 gli elementi da eliminare (quelli marcati in rosso), ottenendo quindi 4 elementi unici e non solo 3.

    Infine nel for finale è comunque sbagliato far incrementare indiceRisultato ad ogni ciclo.
  • Re: Array di booleani, errore logico

    andbin ha scritto:


    SrJustEasy ha scritto:


    Tutto a posto, no?
    No, purtroppo è ancora abbastanza sbagliato. Innanzitutto la logica della prima parte (quella con i 2 for innestati) sarebbe parecchio ottimizzabile in termini di numero di cicli da fare, semplicemente ragionando meglio su come far progredire i due indici.

    Poi comunque a fronte di un array es. { 'A', 'C', 'B', 'A', 'D', 'B', 'A' } il contoDoppioni risulta 4 ma non ha senso poiché invece sarebbero 3 gli elementi da eliminare (quelli marcati in rosso), ottenendo quindi 4 elementi unici e non solo 3.

    Infine nel for finale è comunque sbagliato far incrementare indiceRisultato ad ogni ciclo.
    Aaah perché non me ne accorgo mai di questi errori
    Comunque sono arrivato a questo punto, ho cercato di snellire più che potevo la parte con tutti quei cicli, ma ciò mi ha portato ad utilizzare un break, e non so perché ma non mi sembra l'opzione migliore. Avete qualcosa da consigliarmi?
    
    public static char[] rimuoviDuplicati(char[] in) {
    		int contoDoppioni = 0;
    		int lunghezza = in.length;
    		boolean[] verifica = new boolean[in.length];            
    		for (int i = 0; i<in.length; i++) {
    			for (int z = i + 1; z<in.length; z++) {
    				if(in[i] == in[z]) {
    					verifica[z] = true;        
    					contoDoppioni++;
    					break;
    				}
    			}
    		}
    		lunghezza = lunghezza-contoDoppioni;
    		int indiceRisultato = 0;
    		char[] risultato = new char[lunghezza];
    		for (int i = 0; i<in.length; i++) {
    			if (verifica[i] != true) {             
    				risultato[indiceRisultato] = in[i];
    				indiceRisultato++;
    			}
    		}
    		return risultato;
    	}
    
  • Re: Array di booleani, errore logico

    SrJustEasy ha scritto:


    Comunque sono arrivato a questo punto, ho cercato di snellire più che potevo la parte con tutti quei cicli, ma ciò mi ha portato ad utilizzare un break, e non so perché ma non mi sembra l'opzione migliore. Avete qualcosa da consigliarmi?
    Il break va bene, io quella parte la farei allo stesso modo. A me personalmente piacerebbe di più invertire la logica del ciclo innestato, anche se il risultato è uguale. Nel senso che mi viene da pensare che un carattere sia un doppione se prima ne ho già trovato uno uguale, non se se ne trova uno uguale tra quelli successivi. Ma ripeto, non cambia nulla a livello di risultato.

    Per il resto potresti solo migliorare qua e là l'uso dei nomi e poco altro. Ad esempio il nome verifica per l'array boolean potrebbe riferirsi sia al fatto che verifichi che il numero sia un duplicato, sia viceversa. Io userei duplicati, verificaDuplicati o qualcosa di simile (questo ti aiuta più che altro quando inizi ad avere codici molto corposi, ed è più difficile risalire subito al significato delle variabili).

    Come ultima cosa avevo visto che con i boolean avevi già usato una condizione del tipo if(verifica). Questo lo puoi fare anche per il viceversa, le due istruzioni :
    
    if(verifica[i] != true)
    if(!verifica[i])
    
    Sono del tutto equivalenti.
  • Re: Array di booleani, errore logico

    Il break va bene, io quella parte la farei allo stesso modo. A me personalmente piacerebbe di più invertire la logica del ciclo innestato, anche se il risultato è uguale. Nel senso che mi viene da pensare che un carattere sia un doppione se prima ne ho già trovato uno uguale, non se se ne trova uno uguale tra quelli successivi. Ma ripeto, non cambia nulla a livello di risultato.
    Sono del tutto equivalenti.
    Non capisco, come struttureresti il codice invertendo la logica?
  • Re: Array di booleani, errore logico

    SrJustEasy ha scritto:


    		for (int i = 0; i<in.length; i++) {
    			for (int z = i + 1; z<in.length; z++) {
    Ecco, questo è più appropriato ed è proprio quello a cui mi riferivo prima sul "ottimizzabile". Non serve che il secondo for innestato riparta da 0 o 1, può tranquillamente partire da i+1 !

    Ma si può ancora andare oltre, prendiamo l'array di esempio che ho detto prima: { 'A', 'C', 'B', 'A', 'D', 'B', 'A' }

    Siamo con i = 0, quindi z andrà da 1 a 6 inclusi e la logica troverà le altre due 'A'. Pertanto l'array dei boolean conterrà dopo il primo ciclo esterno:

    { false, false, false, true, false, false, true }

    I due true sono le due 'A' che sono "doppioni".

    Quando con i arrivi a 3 (e poi 6 anche se non c'è nulla oltre), allora NON serve fare il ciclo interno, perché quella lettera è già marcata come doppione, non devi andarla a cercare oltre.
  • Re: Array di booleani, errore logico

    andbin ha scritto:


    SrJustEasy ha scritto:


    		for (int i = 0; i<in.length; i++) {
    			for (int z = i + 1; z<in.length; z++) {
    Ecco, questo è più appropriato ed è proprio quello a cui mi riferivo prima sul "ottimizzabile". Non serve che il secondo for innestato riparta da 0 o 1, può tranquillamente partire da i+1 !

    Ma si può ancora andare oltre, prendiamo l'array di esempio che ho detto prima: { 'A', 'C', 'B', 'A', 'D', 'B', 'A' }

    Siamo con i = 0, quindi z andrà da 1 a 6 inclusi e la logica troverà le altre due 'A'. Pertanto l'array dei boolean conterrà dopo il primo ciclo esterno:

    { false, false, false, true, false, false, true }

    I due true sono le due 'A' che sono "doppioni".

    Quando con i arrivi a 3 (e poi 6 anche se non c'è nulla oltre), allora NON serve fare il ciclo interno, perché quella lettera è già marcata come doppione, non devi andarla a cercare oltre.
    Ma per fare ciò dovrei utilizzare un if, no? Se all'indice i si restituisce true, allora non fare nulla. Ma non complicherebbe le cose?
  • Re: Array di booleani, errore logico

    SrJustEasy ha scritto:


    Ma per fare ciò dovrei utilizzare un if, no? Se all'indice i si restituisce true, allora non fare nulla.
    Non avevo visto prima (l'ho notato ora) che nell'ultimo codice hai messo break nel if(in == in[z]). Questo in effetti risolve la questione del contoDoppioni e in sostanza è "equivalente" come risultato al mettere un if per scartare completamente il ciclo interno come dicevo io.

    Come hai fatto tu la ricerca di più doppioni dello stesso carattere è semplicemente distribuita in più cicli interni fatti in momenti diversi. Mentre mettendo un if come dicevo io è fatta una volta sola in una unica passata.

    Come prestazioni, le due soluzioni dovrebbero essere molto "paragonabili". Quale sia la migliore chiaramente andrebbe verificato con dei microbenchmark e con un numero significativo di caratteri.

    SrJustEasy ha scritto:


    Ma non complicherebbe le cose?

    Non più di tanto.
  • Re: Array di booleani, errore logico

    andbin ha scritto:


    SrJustEasy ha scritto:


    Ma per fare ciò dovrei utilizzare un if, no? Se all'indice i si restituisce true, allora non fare nulla.
    Non avevo visto prima (l'ho notato ora) che nell'ultimo codice hai messo break nel if(in == in[z]). Questo in effetti risolve la questione del contoDoppioni e in sostanza è "equivalente" come risultato al mettere un if per scartare completamente il ciclo interno come dicevo io.

    Come hai fatto tu la ricerca di più doppioni dello stesso carattere è semplicemente distribuita in più cicli interni fatti in momenti diversi. Mentre mettendo un if come dicevo io è fatta una volta sola in una unica passata.

    Come prestazioni, le due soluzioni dovrebbero essere molto "paragonabili". Quale sia la migliore chiaramente andrebbe verificato con dei microbenchmark e con un numero significativo di caratteri.

    SrJustEasy ha scritto:


    Ma non complicherebbe le cose?

    Non più di tanto.


    Va bene, allora grazie di tutto
Devi accedere o registrarti per scrivere nel forum
11 risposte