【问题标题】:Does each instantiated object that inputs an ArrayList use the same reference to that ArrayList?输入 ArrayList 的每个实例化对象是否使用对该 ArrayList 的相同引用?
【发布时间】:2016-10-11 14:00:11
【问题描述】:

我有一个平面文件阅读器类,它从 dat 文件中读取数据,创建存储在其唯一数组列表中的人员、客户和产品对象,我必须将其用于 getInvoice 方法。从发票 dat 文件输入新属性时,我只为我从发票 dat 文件中读取的产品创建一个新的产品列表。这似乎工作正常,但是每个发票对象上的某些产品属性正在更改。

当使用产品数组列表作为字段实例化新发票对象时,是否会创建对添加到下面代码中的产品列表的引用,或者创建该列表后的副本?如果它只是一个参考,为什么当我阅读每个发票对象中的产品列表时,每张发票的产品对象数量是正确的(而不是所有产品)?此外,如果我在创建发票对象(将产品列表作为字段)后清除新产品数组列表,那么我所有发票中的产品列表都是空的。为什么是这样?如果 arraylist 不起作用,我还能怎么做?谢谢,如果需要,我可以添加更多代码。

public ArrayList<Invoice> getInvoices() {
    readPersons();
    readCustomers();
    readProducts();
    Scanner sc = null;

    try {
        sc = new Scanner(new File("data/Invoices.dat"));
        sc.nextLine(); 

        while (sc.hasNextLine()) {
            ArrayList<Product> product = new ArrayList<Product>();
            String line = sc.nextLine(); 
            String data[] = line.split(";"); 
            String invoiceCode = data[0].trim();
            String customerCode = data[1].trim();
            Customer customer = null;
            for(Customer aCustomer: customerList) {
                if (customerCode.equals(aCustomer.getCustomerCode())) {
                    customer = aCustomer;
                    break;
                }
            }
            String personCode = data[2].trim();
            Person person = null;
            for(Person aPerson: personList) {
                if (personCode.equals(aPerson.getPersonCode())) {
                    person = aPerson;
                    break;
                }
            }
            String invoiceDate = data[3];
            String products[] = data[4].split(",");
                for (int i = 0; i < products.length; i++) {
                    String productData[] = products[i].split(":");
                    for(Product aProduct: productList) {
                        if (aProduct.getProductCode().equals(productData[0])) {
                            aProduct.setInvoiceDate(this.getDateTime(invoiceDate));
                            if (productData.length == 1) {
                                aProduct.setQuantity(1);
                                product.add(aProduct);
                            } else if (productData.length == 2) {
                                aProduct.setQuantity(Integer.parseInt(productData[1]));
                                product.add(aProduct);
                            } else if (productData.length == 3) {
                                aProduct.setQuantity(Integer.parseInt(productData[1]));
                                for(Product anotherProduct: product) {
                                    if (anotherProduct.getProductCode() == productData[2]) {
                                        aProduct.setParkingPassCount(anotherProduct.getQuantity());
                                        break;
                                    }
                                }
                                product.add(aProduct);
                            }
                            break;
                        }
                    }
                }

            // Creates an Invoice object
            Invoice invoice = new Invoice(invoiceCode, invoiceDate, customer, person, product);

            // Adds the Invoice object into Invoice ArrayList
            invoiceList.add(invoice);   

        }
        sc.close();
        return invoiceList;
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        return null;
    }
}
public DateTime getDateTime(String Date){
    DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
    DateTime dateTime = formatter.parseDateTime(Date);
    return dateTime;
} 

【问题讨论】:

  • 不,它没有,它使用您告诉他使用的参考。如果您指的是已经存在的产品并更改了该产品的某些值,这在逻辑上也会反映到存在该确切实例的其他集合的更改中,因为它是相同的。 (但目前还不清楚您要做什么以及您在哪里遇到问题)

标签: java arraylist reference


【解决方案1】:

不完全确定你在问什么,但让我们看看;我猜你的意思是:

您的班级有一些字段invoiceList);并且您的方法只是将新的 Invoice 对象追加到该列表中。

假设 invoiceList 对象没有被清除,或者其他一些代码调用

invoiceList = new ArrayList<>()

重复,那么您的代码总是使用 same ArrayList 对象。

乍一看,您的整体设计有待改进。你看,添加新对象到同一个列表意味着你依赖于副作用。但另一方面,您的方法也返回您处理的列表。你应该做一个另一个;但不是两者兼而有之。

所以,选项一 - 依赖使用一个字段,例如:

class InvoiceCollector {
   private final List<Invoice> invoices = new ArrayList<>();

   public void addNewInvoices() {
     ... reads data from file and adds new objects to invoices

   public List<Invoice> getCurrentInvoices() {
     return new ArrayList<>(invoices);

(返回该列表的副本可防止接收该列表引用的代码**在外部对其进行操作)

选项二:仅返回最后收集的发票(并避免在该类上完全使用 字段):

public List<Invoice> collectInvoices() {
  List<Invoice> invoices = ...
  ... all the code you have in your method
  return invoices;
}

最后:

  • 阅读单层抽象原则。您的糟糕方法是在一个地方做得太多。用许多更小的方法更好地分割它
  • 通常,一个名为“getSomething()”的方法不会做太多的“计算”。因此,您可能还应该查看您如何命名事物以避免让您的代码的读者感到惊讶!

编辑 - 解决您的评论(以及您问题中的“真实”意图):循环正文中的第一件事是创建一个名为 product 的新列表。那东西以某种方式被填充;最后;该列表对象被赋予一个新的 Invoice 对象。所以:所有创建的新发票对象都应该使用不同的列表

郑重声明:您的主要问题是您编写的代码非常难以阅读。它以您的变量名称开头:product(作为 LIST!)、products(作为数组)、productList(从天而降)等等)。所以,我个人的建议是:进入并重做您的代码以使其可读。因为那样也会明白正在做什么。

相对于创建混乱的代码;然后需要其他人向您解释您的代码在做什么。

【讨论】:

  • 感谢您的建议。但是,我的问题与我对产品列表中的产品进行更改然后将这些特定产品添加到新产品列表中的代码有关。每次代码循环(我为更新的产品创建一个新的产品列表)时,它是在创建一个新的数组列表吗?当我调试并查看每个产品列表中产品的属性时,它们都是相同的。
  • @walrath21 如果您从一个List 获得Product,请对其进行更改并将这些引用存储在另一个List 中,以确保您正在对这两种产品进行更改,因为它们正在引用到Product 的同一个实例(从技术上讲,您只更改一种产品)。如果您想根据另一个产品的值创建不同的产品,请包括复制构造函数 Product(Product) 并基于另一个创建 Product 的新实例。
  • @walrath21 我进一步增强了我的答案。问题是:您的代码是如此令人困惑,以至于很难理解您在做什么,为什么要这样做;以及出了什么问题。因此,正如所写:我认为您应该首先专注于将这种混乱变成可读的东西。老实说:你应该考虑把那东西扔掉,从头开始。或者至少:找一些同行,让他们审查你的代码。
  • @GhostCat 我认为它令人困惑的部分原因是因为我只发布了一个 sn-p 代码,但是感谢您对单层抽象的建议。我想我需要回去做那件事。关于使代码更具可读性,您有哪些建议?感谢您的意见。
  • 取决于您打算投资的时间;我推荐阅读 Robert Martin 的“清洁代码”。理想情况下,您与整个团队一起这样做。然后你开始练习。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-20
相关资源
最近更新 更多