【问题标题】:Modified LinkedList (2 integers per node) with ListIterator使用 ListIterator 修改 LinkedList(每个节点 2 个整数)
【发布时间】:2017-06-30 15:42:20
【问题描述】:

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

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());
        }
    }
}

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

我的问题是:

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

【问题讨论】:

  • 你应该使用.equals()来设置alreadyExist
  • delete(),一旦你找到你的Person,你在第一次找到某人并继续前进时将alreadyExist设置为true。因此,一旦设置好,它就会为每个人设置。如果您在位置 1 的列表中找到您的 Person,您将删除他,但随后您将删除下一个人,依此类推。你所有的while循环都需要说while (lIter.hasNext() &amp;&amp; !alreadyExists)
  • 您设计方法的方式存在太多问题。正如@River 所提到的,为您的 Person 类覆盖 equals 和 hashcode 并使用它来检查 LinkedList 中已经存在的案例。在删除方法中,您使用 remove() 没有任何参数。它只会删除链表的头部,而不是您要删除的 Person p。如果您覆盖 equals,您将能够使用 remove(Object o) 方法。
  • 不相关:你为什么有ListIterator作为一个字段?摆脱它,并在需要的地方声明为局部变量。

标签: java linked-list listiterator


【解决方案1】:

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

回答您的问题:

  1. 怎么了?删除错误的人的删除错误。

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

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

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

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

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

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

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

假设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
    }
}

【讨论】:

  • 太愚蠢了,您的(非常全面的)答案目前与一个明显不正确的答案联系在一起,即使我的 +1 也是如此。让我们希望其他人也有同样的感觉。使用toString也是一个很好的建议。
  • 非常感谢!没想到会收到这么详细的答复。我的印象是remove()add() 的工作方式彼此相似,因此不断尝试在我的load() 方法中查找错误。 breaktoString() 绝对是救命稻草。
  • @River 是的,我不是想交流我完全同意另一个答案(绝对不完全正确,但它在正确的轨道上),但只是我不能声称归功于这种方法。使用布尔值作为另一种条件并没有让我大吃一惊,但可能被认为比break 语句更干净,所以我觉得应该包括它。感谢您的 +1!
【解决方案2】:

delete() 中,一旦你找到你的人,你在第一次找到某人时将alreadyExist 设置为true 并继续前进。因此,一旦设置好,它就会为每个人设置。如果您在位置 1 的列表中找到您的 Person,您将删除他,但随后您将删除下一个人,依此类推。你所有的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;

【讨论】:

  • 此答案不正确,仅从列表中删除一项。
  • 建议的解决方案是错误的还是诊断?
  • 诊断。解决方案并不理想,但有效。错误诊断的正确解决方案没有多大价值。
  • Arggghhh。我想能够看到括号非常重要。
  • 感谢您的帮助!还要感谢@River 指出这一点,我一时糊涂了。
【解决方案3】:

来自Documentation

void remove() 从列表中删除返回的 LAST 元素 通过 next() 或 previous()

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

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

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

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

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

【讨论】:

  • 我没有否决你的答案,只是想确认文档提到 -> public E remove(): 检索并删除此列表的头部(第一个元素)。
  • @River 不开玩笑!你是谁来判断它没有增加任何有用的东西,你怎么知道我什至看了你的答案!?
  • @digidude 您说的是The Remove in LinkedList,而不是ListIterator..请打开我在回答中提供的文档链接。
  • @Yahya 不知道我是否应得的。谢谢。
  • @River 你的cmets充满了矛盾!我的评论是基于你的评论。但是在您的第二条评论中,您否定了您的第一条评论。不过,你需要留着头发!
【解决方案4】:

问题出在您的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


最后,这里完全没有必要创建/使用与参数p 重复的额外Person。只需使用p 而不是p1(在save)和p3(在delete)。

【讨论】:

  • 非常感谢您回答并编辑我的问题!很遗憾,但是 ojchase 对这 3 个问题都给出了明确的答案,所以我需要选择它们而不是你。
  • 没问题,反正他的回答更好了。
  • +1 反对票绝对是奇怪的。这个答案绝对正确,有很好的链接和解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-21
  • 2020-04-18
  • 1970-01-01
  • 1970-01-01
  • 2019-12-23
  • 2016-01-24
  • 1970-01-01
相关资源
最近更新 更多