使用 ListIterator 修改 LinkedList(每个节点 2 个整数)

Modified LinkedList (2 integers per node) with ListIterator

我正在编写一个代码来保存、删除和加载一个人的身高和体重数据。我创建了 2 classes:

class Person {
    private int height;
    private int weight;

    public Person(int h, int w) {
        height = h;
        weight = w;
    }

    public int getHeight() {
        return height;
    }

    public int getWeight() {
        return weight;
    }

    public String getValues() {
        return ("Height "+height+" and weight "+weight);
    }
}


class DataModified {        //Problem in this class
    private LinkedList<Person> lList;
    private ListIterator<Person> lIter;

    public DataModified() {
        lList = new LinkedList<Person>();
        lIter = lList.listIterator();
    }

    public void save(Person p) {        
        Person p1, p2;                                  //p1: Data needed to be saved
        p1 = new Person(p.getHeight(), p.getWeight());  //p2: Data already on the list
        boolean alreadyExist = false;
        lIter = lList.listIterator();
        while(lIter.hasNext()) {        
            p2 = lIter.next();      
            if ((p2.getHeight() == p1.getHeight()) && (p2.getWeight() == p1.getWeight())) {
                alreadyExist = true;        
            }
        }
        if(alreadyExist) {
            System.out.println("Person: " + p1.getValues() + " had already been on the list.");
        }
        else {
            lIter.add(p1);
            System.out.println("Person: " + p1.getValues() + " is added to the list.");
        }
    }

    public void delete(Person p) {
        Person p3, p2;                                  //p3: Data needed to be deleted
        p3 = new Person(p.getHeight(), p.getWeight());  
        boolean alreadyExist = false;
        lIter = lList.listIterator();
        while(lIter.hasNext()) {        
            p2 = lIter.next();      
            if ((p2.getHeight() == p3.getHeight()) && (p2.getWeight() == p3.getWeight())) {
                alreadyExist = true;        
            }
        }
        if(alreadyExist) {
            lIter.remove();
            System.out.println("Person: " + p3.getValues() + " is deleted from the list.");
        }
        else {
            System.out.println("Person: " + p3.getValues() + " is not on the list.");
        }
    }

    public void load() {            //Problem
        lIter = lList.listIterator();
        Person p2;
        for(int i = 1; lIter.hasNext(); i++){
            p2 = lIter.next();
            System.out.println("Person "+i+" has "+p2.getValues());
        }
    }
}

我已经测试了classDataModified的这3种方法:我先保存3个人的数据,然后删除1个人并加载其余的。但是,最后一个方法打印的不是名单上的2个人,而是我之前删除的那个人。

我的问题是:

  1. 我的代码有什么问题?为什么 load() 方法会这样工作?
  2. 我注意到迭代后,我只能修改lIter。那么 lListlIter 是同一个列表还是两个分开的列表?如果它们不一样,我如何给 lList 来自 lIter 的数据?
  3. 有没有办法停止列表的迭代?

问题出在您的 delete 方法中:

在删除任何内容之前循环遍历整个列表。 ListIterator.remove only deletes the last item returned by next()。当您找到已存在的项目时需要中断:

while(lIter.hasNext()) {        
    p2 = lIter.next();      
    if ((p2.getHeight() == p3.getHeight()) && (p2.getWeight() == p3.getWeight())) {
        alreadyExist = true;   
        break;     
    }
}

此外,您比较 Person 对象的方式实际上应该在 equals() 方法中完成,如 described here.


最后,您的 creation/use 的额外 Person 是参数 p 的副本,在这里完全没有必要。只需使用 p 而不是 p1(在 save 中)和 p3(在 delete 中)。

delete() 中,一旦你找到你的人,你就会在第一次找到某人时将 alreadyExist 设置为 true 并继续前进。所以一旦设置好,它就为每个人设置好了。如果您在列表中的位置 1 找到您的人,您将删除他,然后删除下一个人,依此类推。你所有的 while 循环都需要说。

while (!alreadyExists && lIter.hasNext()) {        
    p2 = lIter.next();      
    if ((p2.getHeight() == p1.getHeight()) && (p2.getWeight() == p1.getWeight())) {
            alreadyExist = true;
            lIter.remove();
        }
    }

    // Delete the if/else code

你应该去掉成员变量:

private ListIterator<Person> lIter;

来自Documentation

void remove() Removes from the list the LAST element that was returned by next() or previous()

但是当你到达所需的Person对象时,你继续迭代下一个Person对象。

您需要的是 break while 循环一旦找到目标对象。

if ((p2.getHeight()==p3.getHeight())&&(p2.getWeight()==p3.getWeight())){
      alreadyExist = true;
      break;        
}

现在最后一个元素是所需的对象,您现在可以使用 remove()

if(alreadyExist) {lIter.remove();}

正如其他人所指出的,您的 delete 方法中肯定存在错误。如前所述,如果在列表中找到目标 Person,则列表中的最后一个 Person 将被删除,而不是目标。当你找到那个人并立即将其删除而不是继续循环时,你真的需要能够切出 while 循环。

回答您的问题:

  1. 怎么了? delete 中删除错误 Person 的错误。

  2. lListlIter是同一个列表吗?概念上是,技术上不是。 lList 是列表本身,lIter 是作用于该列表的迭代器。它本身不是一个列表。这是一个迭代器。但是它正在处理的数据肯定是同一个列表。

  3. 如何停止迭代?你有几个选择。当前编写代码的最简单方法是 break 语句。它停止执行当前循环并在块外恢复执行。在您的添加和删除方法中,在 alreadyExist 设置为 true 后立即中断是有意义的。 jiveturkey 首先建议的另一种选择是将 alreadyExist 作为条件添加到 while 循环中。然后,只有在有更多项目要迭代并且 alreadyExist 尚未设置为 true 时,您才会继续迭代。第三种选择是在找到 Person 后立即进行真正的工作(即删除),然后 return 完全从方法中删除。

除此之外,一些不请自来的一般性建议 :)

  • 您在多个方法中比较 Person 对象。随着时间的推移,这将变得难以维护,因此最好在一个地方定义比较。 Java 为此提供了一个 equals 方法。它在每个 Object 中,但默认实现对您没有帮助,因此您想覆盖它。在您的情况下,如果两个不同的 Person 对象的高度和重量相等,则您认为它们在概念上是相等的。因此,如果身高和体重相等,则将 Person 中的 equals() 方法重写为 return true。有关一些提示,请参阅 How to override equals method in java or http://users.csc.calpoly.edu/~gfisher/classes/102/info/howToOverrideEquals.html。如果覆盖equals,还需要覆盖hashCode.

  • 您正在复制 Person 参数。传入的确切对象不是从列表中添加或删除的实际对象;副本是。你可以只使用参数。在最好的情况下,您目前有不必要的性能损失(创建额外的对象)。在最坏的情况下,您会遇到错误。

  • lIter 在构造函数和每个方法中都设置了。如果你不需要跨方法调用存储它的当前状态,那么它应该只是一个局部变量,用于一个方法然后被丢弃。

  • getValues() 当前仅用于使对象易于阅读。这是一个常见问题,交给 toString() 的任务也在 Object 中定义,并且可以在您编写的任何 class 中覆盖。要利用它,您需要做的就是将 getValues 重命名为 toString。然后你可以直接在日志消息中使用它。下面的示例。

下面是我将如何重写 delete,假设 Person 中有一个好的 equals 方法,并且 getValues 重命名为 toString:

public void delete(Person p) {
    boolean alreadyExist = false;
    ListIterator definitelyNotLIter = lList.listIterator();
    while(definitelyNotLIter.hasNext()) {        
        Person current = definitelyNotLIter.next();      
        if (p.equals(current)) {
            alreadyExist = true;
            definitelyNotLIter.remove();

            // Option 1:
            break;  // next line to execute will be the if(alreadyExist) block

            // Option 2:
            // put your successful delete logging here
            // return;
            // and leave the failed delete logging outside the loop

            // Option 3:
            // Do nothing. The looping will continue, and you'd have a deleteAll method, where multiple items would get deleted if you managed to get duplicates in the list.
            // You actually wouldn't need alreadyExist any more.

            // I'd go with option 1, myself
        }
    }
    if(alreadyExist) {
        System.out.println("Person: " + p + " is deleted from the list."); // p.toString() will get called
    }
    else {
        System.out.println("Person: " + p + " is not on the list."); // p.toString() will get called
    }
}