您的代码存在许多问题。
先看看你的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);
}
哦……我差点忘了:使用 aSet
来合并文件和内存列表的神奇之处在于你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
基于方法
如果您不能使用Collections
API,那么第二好的方法是编写自己的集合类型,该集合类型支持相同的操作,但由数组(或链表,如果您了解的话)支持。在您的情况下,它将是客户列表。我将调用类型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
以获取新的容量,但我至少会留下一些东西让你弄清楚;)