コードにいくつかの問題があります。
あなたの方法を最初に見てくださいreadFromFile
:
- メソッドが見つけたすべてのレコードで埋めている配列を渡しています。配列内のスペースよりも多くの顧客がファイル内にある場合はどうなりますか? (ヒント:
ArrayIndexOutOfBoundsException
物です)
- ファイルから文字列として読み取った整数を解析しています。ファイルが壊れていて、読み取った行が整数でない場合はどうなりますか?
- 読み取るファイルの名前はハードコーディングされています。これは、定数または構成オプションである必要があります。メソッドを書く目的では、それをパラメーターにするのが最善です。
- ファイルを開き、メソッドで読み取ります。単体テストのために、これを別々のメソッドに分割する必要があります。
Collections
一般に、オブジェクトのリストを保持するには、配列ではなくクラスを使用する必要があります。
Customer
メソッドで属性に直接アクセスしていますreadFromFile
。アクセサーメソッドを使用する必要があります。
Collections
に基づくアプローチ
Collections
これは、 APIの使用に基づいて提案された書き直しです。
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;
}
これは、このメソッドを使用して実際にコンテンツを読み取ります。
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;
}
に対して同様のアプローチを使用するとwriteToFile
、次のようになります。
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);
}
しかし、私たちはあなたの主な懸念事項にまだ対処していません。を呼び出すときに、メモリ内の顧客とファイルの内容をマージしたいようですwriteToFile
。代わりに、この目的のために新しい方法を導入することをお勧めします。これにより、既存のメソッドがより単純になります。
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);
}
ああ...私はほとんど忘れていました:ファイルとメモリ内リストをマージするために a を使用する魔法は、クラスにメソッドSet
を実装することに依存しています。上書きする場合は、 も上書きする必要があります。例えば:equals()
Customer
equals()
hashCode()
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
に基づくアプローチ
APIを使用できない場合Collections
、2 番目に良い方法は、同じ操作をサポートする独自のコレクション型を作成することですが、配列 (または、それを学習した場合はリンク リスト) によってサポートされます。あなたの場合、それは顧客のリストになります。タイプを呼び出しますCustomerList
:
既存のコードを分析すると、add
メソッドを実装するクラスと、リストをトラバースする方法が必要になります。を無視しIterators
て、後者を agetLength
と a getCustomer
(インデックスによる) で実現します。同期のために、顧客がリストに含まれているかどうかを確認する方法も必要なので、contains
メソッドを追加します。
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;
}
}
これを実装すると、メソッドの書き直しwriteToFile
はまったく同じになりますが、CustomerList
代わりにを使用し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();
}
writeToStream
も非常に似ていますが、 を使用していないため、Iterator
手動でリストをトラバースする必要があります。
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);
}
同様にreadFromFile
-- リストタイプを除いてほとんど同じです:
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;
}
も、readFromStream
型を除いてほとんど同じです (で使用されるメソッドCustomerList
は、で使用されるものと同じ署名を持っています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;
}
最も異なる方法は です。重複がないことを保証syncToFile
するSet
タイプがないため、ファイルから顧客を挿入しようとするたびに手動で確認する必要があります。
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);
}
add
ここで注意すべきことは、新しい容量を取得するための追加のコンストラクターを用意することで操作を最適化できたということCustomerList
ですが、少なくとも何かを理解するために残しておきます;)