Foro sobre Java SE > escribir y leer desde archivo sin duplicados
Guenas.
No lo he revisado a fondo pero creo que el problema esta en la clase Cliente.
Sobreescribe sus métodos hashCode() y equals() para que usen algo que identifique unívocamente al cliente como su DNI o algo así. Probablemente ahora usa la dirección de memoria
Un saludo,
Paposo
Hola Paposo,
Gracias por la respuesta, aunque no tiene nada que ver la respuesta en este caso, ya que los objetos Cliente proceden de un TreeMap indexados de manera inequívoca por teléfonos no repetidos.
Guenas.
Probablemente no te he entendido. Disculpa si es así.
Un HashSet utiliza el hashCode del objeto que pongas dentro, no el comparador.
Si no lo has modificado estará usando la posición de memoria como identificador.
Cuando añades un cliente lees primero el HashSet serializado anteriormente, creando objetos cliente en ciertas posiciones de memoria. Si el cliente que estas añadiendo ya existe, el HashSet no podrá saberlo porque estarán en distintas posiciones de memoria.
Es decir son dos objetos distintos aunque contengan exactamente los mismos datos.
Un saludo,
Paposo
Gracias por la respuesta. ¿Cómo puedo evitar eso entonces? He probado a hacer los metódos de otra manera y con ArrayList y el el resultado es el mismo, seguramente por lo que me estás diciendo, pero como se podría hacer al programa para que identifique los objetos como iguales? Gracias.
public static void EscribirFichero(Cliente cl) {
try {
if(FILE_NAME.exists()==false) {
FileOutputStream fos = new FileOutputStream(FILE_NAME);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(cl);
oos.flush();
if(oos!=null) {
try {
oos.close();
fos.close();
}catch (IOException ignored) {
}
}
System.out.println("Se ha generado el fichero " + FILE_NAME);
} else {
MiObjectOutputStream mos = new MiObjectOutputStream(new FileOutputStream(FILE_NAME,true));
mos.writeObject(cl);
mos.flush();
if(mos!=null) {
try {
mos.close();
} catch (IOException ignored) {
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void LeerFichero() throws FileNotFoundException,IOException {
// ArrayList<Cliente> clientesHS = new ArrayList<Cliente>();
// HashSet<Cliente> cliHS = new HashSet<Cliente>();
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(FILE_NAME);
ois = new ObjectInputStream(fis);
Cliente c;
while(true) {
c = (Cliente)ois.readObject();
cliHS.add(c);
}
}catch(EOFException e) {
System.out.println("Se han leido todos los registros");
}catch(ClassNotFoundException e) {
e.printStackTrace();
}finally {
if(ois!=null) {
ois.close();
fis.close();
}
Iterator it = cliHS.iterator();
while(it.hasNext()) {
System.out.println("Cliente: "+it.next());
}
}
}
Guenas.
Si dices que tu identificador es el teléfono, que entiendo que es un String, sobreescribe el método hashCode() de tu clase Cliente para que devuelva telefono.hashCode().
Para darle consistencia haz lo mismo con el metodo equals().
De todos modos guardar estos datos serializados no parece una idea demasiado buena. Estas leyendo y escribiendo todos los clientes cada vez que modificas tan solo uno de ellos.
Yo de ti me planteaba usar una BD y JDBC.
Un saludo,
Paposo
Wenas Paposo,
Tienes razón, tanto en la solución como en la sugerencia con BBDD, lo que ocurre es que se trata de una práctica para principiantes y sólo puedo utilizar ficheros. De momento lo he resuelto captando los String nombre y apellidos del Cliente y usar Buffered Reader/Writer en vez del Object output/input stream. No obstante voy a buscar algún ejemplo de sobreescritura hashCode y equals porque me parece una solución más eficiente. Gracias y un saludo.
Te parecerá una tontería pero la forma de guardarlos no tiene porque darte problemas.
Usa lo siguiente:
public class Cliente {
String telefono;
String nombre;
String apellidos;
@Override
public int hashCode() {
return (apellidos + nombre).hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Cliente other = (Cliente) obj;
if (!Objects.equals(this.nombre + this.apellidos, other.nombre + other.apellidos)) {
return false;
}
return true;
}
}
Wenas y gracias una vez más por la ayuda.
La siguiente línea tiene algún error:
if (!Objects.equals(this.nombre + this.apellidos, other.nombre + other.apellidos)) {
Sería Object.equals, sin la "s", una pequeña tontería.Por otra parte, dice que no es aplicable en el objeto los argumentos(String, String).
Corrígeme si me equivoco, pero si uso HashSet debería ser suficiente con sobreescribir el método HashCode. He probado sin sobreescribir el equals y aún así obtengo un excepción:
Exception in thread "main" java.lang.ClassCastException: cannot assign instance of java.io.ObjectStreamClass to field Pedido.c of type Cliente in instance of Pedido
Mi clase Cliente está compuesta por otras clases, como es el caso de Pedido, por lo que voy a probar el método de escritura con argumentos (String cliente.nombre, String cliente.apellidos) o bien crear un nuevo constructor que sólo tenga(String nombre,String apellidos).
Gracias y un saludo.
Pues efectivamente, funciona bien como he dicho anteriormente, pero sólo hasta que vuelvo a ejecutar el programa, ahora estaré haciendo algo mal en los métodos de escritura/lectura
public static void EscribirFichero(String nom,String ape) {
String cl;
cl = nom+" "+ape;
cliHS.add(cl);
try {
if(FILE_NAME.exists()==false) {
FileOutputStream fos = new FileOutputStream(FILE_NAME);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(cliHS);
oos.flush();
if(oos!=null) {
try {
oos.close();
fos.close();
}catch (IOException ignored) {
}
}
System.out.println("Se ha generado el fichero " + FILE_NAME);
} else {
MiObjectOutputStream mos = new MiObjectOutputStream(new FileOutputStream(FILE_NAME,true));
mos.writeObject(cliHS);
mos.flush();
if(mos!=null) {
try {
mos.close();
} catch (IOException ignored) {
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void LeerFichero() throws FileNotFoundException,IOException {
//ArrayList<Cliente> clientesHS = new ArrayList<Cliente>();
HashSet<String> cliHS = new HashSet<String>();
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(FILE_NAME);
ois = new ObjectInputStream(fis);
while(true) {
cliHS = (HashSet<String>)ois.readObject();
}
}catch(EOFException e) {
System.out.println("Se han leido todos los registros");
}catch(ClassNotFoundException e) {
e.printStackTrace();
}finally {
if(ois!=null) {
ois.close();
fis.close();
}
}
Iterator it = cliHS.iterator();
while(it.hasNext()) {
System.out.println("Cliente: "+it.next());
}
}
Rectificando el método como pongo a continuación y con la sobreescritura según las indicaciones de Paposo, el código queda funcionando como quería:
public static void EscribirFichero(String nom,String ape) throws IOException {
ObjectOutputStream objectOutput = null;
ObjectInputStream objectInput = null;
String cl;
cl = nom+" "+ape;
try {
if(FILE_NAME.exists()) {
objectInput = new ObjectInputStream(new FileInputStream(FILE_NAME));
clientesHS = (HashSet<String>) objectInput.readObject();
}
clientesHS.add(cl);
objectOutput = new ObjectOutputStream(new FileOutputStream(FILE_NAME));
objectOutput.writeObject(clientesHS);
System.out.println("Se ha generado el fichero " + FILE_NAME);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (objectOutput != null) {
try {
objectOutput.close();
} catch (IOException ignored) {
}
}
}
}
Guenas.
No. La firma es
public static boolean Objects.equals(Object a, Object b)
y dado que espera dos objetos debería tragar dos String.
En cuanto a:
Exception in thread "main" java.lang.ClassCastException: cannot assign instance of java.io.ObjectStreamClass to field Pedido.c of type Cliente in instance of Pedido
Aquí tienes alguna marranadilla. Para deserializar un objeto debería tener un constructor sin argumentos.
Un saludo,
Paposo
Buenas, en el siguiente código estoy intentado mediante dos métodos escribir y leer clientes que almaceno en archivo, pero a pesar de que uso un HashSet lo clientes se me duplican. ¿Alguna idea de dónde está mi error?
public static void EscribirFichero(Cliente cl) {
ObjectOutputStream objectOutput = null;
ObjectInputStream objectInput = null;
HashSet<Cliente> cliHS = new HashSet<Cliente>();
try {
if(FILE_NAME.exists()) {
objectInput = new ObjectInputStream(new FileInputStream(FILE_NAME));
cliHS = (HashSet<Cliente>) objectInput.readObject();
}
cliHS.add(cl);
objectOutput = new ObjectOutputStream(new FileOutputStream(FILE_NAME)); /// true
objectOutput.writeObject(cliHS);
System.out.println("Se ha generado el fichero " + FILE_NAME);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (objectOutput != null) {
try {
objectOutput.close();
} catch (IOException ignored) {
}
}
}
}
public static void LeerFichero() {
ObjectInputStream objectInput = null;
try {
objectInput = new ObjectInputStream(new FileInputStream(FILE_NAME));
Object actual = null;
HashSet<Cliente> cliHS = null;
while((actual = objectInput.readObject()) != null) {
cliHS = (HashSet<Cliente>) actual;
Iterator it = cliHS.iterator();
while(it.hasNext()) {
System.out.println("Datos cliente: "+it.next());
}
}
objectInput.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch(EOFException e) {
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (objectInput != null) {
try {
objectInput.close();
} catch (IOException ignored) {
}
}
}
}