Sie haben eine Reihe von Problemen mit Ihrem Code meine Kopfschmerzen zu beenden.
Betrachtet man zunächst Ihre readFromFile
Methode:
- Sie sind in einem Array übergeben, die Ihre Methode füllt sich mit allen Aufzeichnungen es findet. Was passiert, wenn mehr Kunden in der Datei sind, als im Array Platz ist? (Hinweis:
ArrayIndexOutOfBoundsException
ist eine Sache)
- Sie analysieren eine Ganzzahl, die als Zeichenfolge aus der Datei gelesen wird. Was passiert, wenn die Datei beschädigt ist und die gelesene Zeile keine Ganzzahl ist?
- Der Name der Datei, aus der gelesen werden soll, ist fest codiert. Dies sollte eine konstante oder Konfigurationsoption sein. Um Methoden zu schreiben, ist es am besten, sie zu einem Parameter zu machen.
- Sie öffnen die Datei und lesen daraus in der Methode. Für Unit-Tests sollten Sie diese in separate Methoden aufteilen.
- Im Allgemeinen sollten Sie eine
Collections
Klasse anstelle eines Arrays verwenden, um eine Liste von Objekten zu speichern.
- Sie greifen auf die
Customer
Attribute direkt in der readFromFile
Methode zu. Sie sollten eine Zugriffsmethode verwenden.
Collections
-basierte Ansatz
Hier ist mein Vorschlag Rewrite basiert auf der Verwendung Collections
APIs:
public static List<Customer> readFromFile(String filename) throws IOException {
// set up file for reading
// br used to read from file
File inputFile = new File(filename);
FileInputStream fis = new FileInputStream(inputFile);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
List<Customer> customers = readFromStream(br);
br.close(); // end ReadFile class
return customers;
}
Diese verwendet diese Methode, um tatsächlich den Inhalt zu lesen:
public static List<Customer> readFromStream(BufferedReader br) throws IOException {
List<Customer> customerList = new LinkedList<>();
// Subtract AND assignment operator, It subtracts right operand from the
// left operand and assign the result to left operand
boolean moreCustomers = true;
while (moreCustomers) {
try {
Customer customer = new Customer();
customer.setName(br.readLine());
String sCustNo = br.readLine();
customer.setNumber(Integer.parseInt(sCustNo));
if (customer.getNumber() == 0) {
moreCustomers = false;
}
else {
customerList.add(customer);
}
}
catch (NumberFormatException x) {
// happens if the line is not a number.
// handle this somehow, e.g. by ignoring, logging, or stopping execution
// for now, we just stop reading
moreCustomers = false;
}
}
return customerList;
}
eine Verwendung ähnlicher Ansatz für writeToFile
, erhalten wir:
static void writeToFile(Collection<Customer> customers, String filename) throws IOException {
// set up file for output
// pw used to write to file
File outputFile = new File(filename);
FileOutputStream fos = new FileOutputStream(outputFile);
PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos));
writeToStream(customers, pw);
pw.flush();
pw.close();
}
static void writeToStream(Collection<Customer> customers, PrintWriter pw) throws IOException {
for (Customer customer: customers) {
pw.println(customer.getName());
pw.println(customer.getNumber());
}
pw.println(0);
pw.println(0);
}
Wir haben jedoch immer noch nicht Ihre Hauptsorge angesprochen. Anscheinend möchten Sie den Dateiinhalt mit den Kunden im Speicher zusammenführen, wenn Sie writeToFile
aufrufen. Ich schlage vor, dass Sie stattdessen eine neue Methode für diesen Zweck einführen. Dies hält die bestehenden Methoden einfacher:
static void syncToFile(Collection<Customer> customers, String filename) throws IOException {
// get a list of existing customers
List<Customer> customersInFile = readFromFile(filename);
// use a set to merge
Set<Customer> customersToWrite = new HashSet<>();
// first add current in-memory cutomers
customersToWrite.addAll(customers);
// then add the ones from the file. Duplicates will be ignored
customersToWrite.addAll(customersInFile);
// then save the merged set
writeToFile(customersToWrite, filename);
}
Oh ... ich fast vergessen: Die Magie eines Set
der Verwendung der Datei und im Speicher zu fusionieren Liste stützt sich auf Sie die equals()
Methode in der Klasse Customer
zu implementieren. Wenn Sie equals()
überschreiben, sollten Sie auch hashCode()
überschreiben. Zum Beispiel:
public class Customer {
@Override
public boolean equals(Object obj) {
return (obj != null) && (obj instanceof Customer) && (getNumber() == ((Customer)obj).getNumber());
}
@Override
public int hashCode() {
return getNumber()+31;
}
};
CustomerList
-basierte Ansatz
Wenn Sie Collections
APIs nicht verwenden können, die zweitbeste wäre eine eigene Kollektion Art zu schreiben, die die gleichen Operationen unterstützt, wird aber von einem Array gesichert (oder verknüpfte Liste, wenn Sie das gelernt haben).In Ihrem Fall wäre es eine Liste von Kunden. Ich rufe den Typ CustomerList
:
Analysieren unseres vorhandenen Codes, benötigen wir eine Klasse, die eine add
Methode und eine Möglichkeit zum Durchlaufen der Liste implementiert. Ignorieren Iterators
, erreichen wir das letztere mit einem getLength
und einem getCustomer
(per Index). Für die Synchronisation, müssen wir auch eine Möglichkeit zu überprüfen, ob ein Kunde in der Liste enthalten ist, so dass wir eine contains
Methode hinzufügen:
public class CustomerList {
private static final int INITIAL_SIZE = 100;
private static final int SIZE_INCREMENT = 100;
// list of customers. We're keeping it packed, so there
// should be no holes!
private Customer[] customers = new Customer[INITIAL_SIZE];
private int numberOfCustomers = 0;
/**
* Adds a new customer at end. Allows duplicates.
*
* @param newCustomer the new customer to add
* @return the updated number of customers in the list
*/
public int add(Customer newCustomer) {
if (numberOfCustomers == customers.length) {
// the current array is full, make a new one with more headroom
Customer[] newCustomerList = new Customer[customers.length+SIZE_INCREMENT];
for (int i = 0; i < customers.length; i++) {
newCustomerList[i] = customers[i];
}
// we will add the new customer at end!
newCustomerList[numberOfCustomers] = newCustomer;
// replace the customer list with the new one
customers = newCustomerList;
}
else {
customers[numberOfCustomers] = newCustomer;
}
// we've added a new customer!
numberOfCustomers++;
return numberOfCustomers;
}
/**
* @return the number of customers in this list
*/
public int getLength() {
return numberOfCustomers;
}
/**
* @param i the index of the customer to retrieve
* @return Customer at index <code>i</code> of this list (zero-based).
*/
public Customer getCustomer(int i) {
//TODO: Add boundary check of i (0 <= i < numberOfCustomers)
return customers[i];
}
/**
* Check if a customer with the same number as the one given exists in this list
* @param customer the customer to check for (will use customer.getNumber() to check against list)
* @return <code>true</code> if the customer is found. <code>false</code> otherwise.
*/
public boolean contains(Customer customer) {
for (int i = 0; i < numberOfCustomers; i++) {
if (customers[i].getNumber() == customer.getNumber()) {
return true;
}
}
// if we got here, it means we didn't find the customer
return false;
}
}
Damit implementiert, die Neufassung der writeToFile
Methode ist genau das gleiche, außer wir verwenden CustomerList
statt List<Customer>
:
static void writeToFile(CustomerList customers, String filename) throws IOException {
// set up file for output
// pw used to write to file
File outputFile = new File(filename);
FileOutputStream fos = new FileOutputStream(outputFile);
PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos));
writeToStream(customers, pw);
pw.flush();
pw.close();
}
die writeToStream
ist auch sehr ähnlich, mit der Ausnahme, da wir keine Iterator
verwenden, haben wir die Liste manuell zu durchqueren:
static void writeToStream(CustomerList customers, PrintWriter pw) throws IOException {
for (int i = 0; i < customers.getLength(); i++) {
pw.println(customers.getCustomer(i).getName());
pw.println(customers.getCustomer(i).getNumber());
}
pw.println(0);
pw.println(0);
}
Ähnliche für readFromFile
- so ziemlich das gleiche mit Ausnahme des Listentyp:
public static CustomerList readFromFile(String filename) throws IOException {
// set up file for reading
// br used to read from file
File inputFile = new File(filename);
FileInputStream fis = new FileInputStream(inputFile);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
CustomerList customers = readFromStream(br);
br.close(); // end ReadFile class
return customers;
}
Die readFromStream
auch das gleiche ist ziemlich viel, außer für den Typen (die auf CustomerList
verwendeten Methoden hat die gleiche Signatur wie die, die verwendeten List<Customer>
:
public static CustomerList readFromStream(BufferedReader br) throws IOException {
CustomerList customerList = new CustomerList();
// Subtract AND assignment operator, It subtracts right operand from the
// left operand and assign the result to left operand
boolean moreCustomers = true;
while (moreCustomers) {
try {
Customer customer = new Customer();
customer.setName(br.readLine());
String sCustNo = br.readLine();
customer.setNumber(Integer.parseInt(sCustNo));
if (customer.getNumber() == 0) {
moreCustomers = false;
}
else {
customerList.add(customer);
}
}
catch (NumberFormatException x) {
// happens if the line is not a number.
// handle this somehow, e.g. by ignoring, logging, or stopping execution
// for now, we just stop reading
moreCustomers = false;
}
}
return customerList;
}
die andere Methode der syncToFile
ist, wie wir die Set
Typ nicht haben, die keine Duplikate garantiert, haben wir manuell zu überprüfen jedes Mal versuchen wir einen Kunden aus der Datei einfügen:
static void syncToFile(CustomerList customers, String filename) throws IOException {
// get a list of existing customers
CustomerList customersInFile = readFromFile(filename);
// use a set to merge
CustomerList customersToWrite = new CustomerList();
// first add current in-memory customers
for (int i = 0; i < customers.getLength(); i++) {
customersToWrite.add(customers.getCustomer(i));
}
// then add the ones from the file. But skip duplicates
for (int i = 0; i < customersInFile.getLength(); i++) {
if (!customersToWrite.contains(customersInFile.getCustomer(i))) {
customersToWrite.add(customersInFile.getCustomer(i));
}
}
// then save the merged set
writeToFile(customersToWrite, filename);
}
Etwas zu beachten ist hier, dass wir, indem er einen zusätzlichen Konstruktor für CustomerList
, die die neue Kapazität nahm die add
Operationen optimiert haben könnte, aber ich werde verlassen mindestens etwas für Sie herauszufinden;)
Was genau ist falsch? Ihre Beschreibung des Problems ist ziemlich vage – dovetalk
der erste Code überschreiben die Daten existieren in, dh: wenn die Datei einen Namen0 und Nummer0 gespeichert hat .. dann geben Sie name2 und number2 die ersten Werte werden gelöscht und die zweiten Werte werden nehmen Sie es an Stelle ... wie für den zweiten Code liest es nicht die Werte, die bereits gespeichert haben, wenn Sie es nicht überschreiben .. ich hoffe, dass es Ihnen klar ist – jill
So wollen Sie 'writeToFile', um das zu verschmelzen Dateiinhalt mit den übergebenen Datensätzen? – dovetalk