-6

An exercise demands that all people matching a given name parameter be removed from a List collection. The solution must use a for-each loop.


The solution presented below, does not throw any compile-time errors at me, but but it fails to pass one of the Unit tests that are run to verify the correctness of the solution.

Here is what I have tried so far:

public ArrayList<Person> people;
public void remove(String name){
    for(Person i : people){
        if (people.contains(name)){
            people.remove(i);
        }
    }
}
Adi
  • 5,089
  • 6
  • 33
  • 47
user695696
  • 155
  • 3
  • 6
  • 11
  • 3
    Homework again? [one](http://stackoverflow.com/questions/5811441/return-the-number-of-people-in-the-list-with-a-name-that-matches-the-parameter-us) [two](http://stackoverflow.com/questions/5810067/how-do-i-give-each-person-in-this-list-a-raise) [three](http://stackoverflow.com/questions/5809496/instantiating-a-new-arraylist-then-adding-to-the-end-of-it) – Ishtar Apr 27 '11 at 22:56
  • 1
    @user695696: I've added a 'homework' tag to this -- please feel free to remove that if it is inaccurate. – phooji Apr 27 '11 at 23:08
  • What test is it failing? – Chris Thompson Apr 27 '11 at 23:11
  • What is the test you are trying to make it pass? – Jeremy Apr 27 '11 at 23:19
  • 5
    You have posted 5 for-each loop questions in the last 2 hours. This isn't do-my-homework overflow. Ask questions about what you are failing to grasp about for-each loops. You will end up a better programmer in the longterm than you would if you keep getting all the answers from here. – Martin Apr 27 '11 at 23:23
  • What are you trying to do? The current code removes some of the `Person`s from `people`. Specifically, it removes the ones from the beginning up to and including `name` – Brad Apr 27 '11 at 23:23
  • This user is not putting much effort in to trying to understand or resolve the problem, instead opting for a cut, paste and answer my homework approach. – Martin Apr 28 '11 at 00:08

5 Answers5

4

Which test isn't passed?

But let's look at the code:

You get a param 'name'. Then you iterate over a list of Persons. The current person is i. In each step, you ask the list, whether it contains the name (which is a String). Hm.

contains asks the whole list, whether it contains something - this something should match the typ of the elements in the list, shouldn't it?

But while iterating, you could ask each Person (i), whether the persons name is 'name'.

if (i.name.equals (name)) // or 
if (i.getName ().equals (name))

Iterating an meanwhile removing is another issue, to take care of.

Since there is much rumor, and a lot of people seemed to oversee the problem, I have a different solution, than using an interator: Collect the candidates for removing, and substract the whole blacklist as a bunch in the end:

public void remove (String name) 
{
    List <Person> blacklist = new ArrayList <Person> ();
    for (Person p : people) 
    {
        if (p.getName ().equals (name))
        {
            blacklist.add (p);
        }
    }
    people.removeAll (blacklist);
}
user unknown
  • 35,537
  • 11
  • 75
  • 121
  • This http://stackoverflow.com/questions/5811823/removing-all-people-from-a-list-whose-names-match-parameter/5811884#5811884 duplicate question lead me here, and I prefer The Unknown User's answer to mine. It was ANZAC Day yesterday. "Lest we Forget." but let's do it with a chuckle, which hopefully brings people, which hopefully helps prevent wars starting in the first place. – corlettk Apr 28 '11 at 03:06
  • ANZAC day? (4 more to go - forum software plays politburo again). – user unknown Apr 28 '11 at 03:19
  • That's also a neat way to avoid ConcurrentModificationException. – Ярослав Рахматуллин Oct 10 '12 at 00:28
1

Don't use for-each loops if you about to remove from your list:

the for-each loop hides the iterator, so you cannot call remove. 

(from documentation)

Use standard iterator and its remove method instead.

phooji
  • 10,086
  • 2
  • 38
  • 45
MByD
  • 135,866
  • 28
  • 264
  • 277
1

This will require that you person class has a method named getName() of course. Or you could override equals method, however beware of drawbacks as stated in the comments of this answer.

public void remove(String name) {
for (Person person : people) {
    if (person.getName().equals(name)) {
        people.remove(person);
    }
}

Returns true if this collection contains the specified element. More formally, returns true if and only if this collection contains at least one element e such that (o==null ? e==null : o.equals(e)).

http://download.oracle.com/javase/1.5.0/docs/api/java/util/Collection.html#contains(java.lang.Object)

LuckyLuke
  • 47,771
  • 85
  • 270
  • 434
  • I wouldn't recommend overriding the equals method JUST for this operation, simply because you can have only ONE override. IF, however, the Person's `name` attribute is distinct and therefore suitable for establishing identity, I'd go ahead and override Object's equals, because I'd bet that it will be useful behaviour throughout the system. Does that make sense? – corlettk Apr 27 '11 at 23:40
  • @corlettk: Yes, it does, I would have done it as I shown by code but it is possible to fix the code he wrote by overriding equals, even though that not be the best way. I will change my answer. – LuckyLuke Apr 27 '11 at 23:48
1

EDIT:

My first answer (below the line) is actually WRONG, just like all the others posted so far. I did-up a little test (below)... We're ALL causing a ConcurrentModificationException in the iterator which underlies the foreach loop. Now there's a gotcha for ya! Sigh.

package forums;

import java.util.List;
import java.util.ArrayList;

class Person
{
  private final String name;
  private final int age;
  Person(String name, int age) {
    this.name=name;
    this.age=age;
  }
  public String getName() { return name; }
  public int getAge() { return age; }
  public String toString() { return name + " " + age; }
}

public class PeopleTest
{
  public List<Person> people;

  public void remove(String name)
  {
    for ( int i=0; i<people.size(); i++ ) {
      Person person = people.get(i);
      if (person.getName().equals(name)) {
        people.remove(person);
        i--;
      }
    }
  }

  public void badRemove(String name)
  {
    for ( Person person : people ) {
      if (person.getName().equals(name)) {
        people.remove(person);
      }
    }
  }

  public void printAll(String message) {
    System.out.println(message);
    for ( Person person : people ) {
      System.out.println("  " + person);
    }
  }


  public void run() 
  {
    // setup
    people = new ArrayList<Person>(5);
    Person dave, kelly, jack, jill, xavier;
    people.add(dave=new Person("Dave", 36));
    people.add(kelly=new Person("Kelly", 25));
    people.add(jack=new Person("Jack", 42));
    people.add(jill=new Person("Jill", 19));
    xavier = new Person("Xavier", 136);

    badRemove("Dave");

    // before image
    assert people.size() == 4;
    printAll("the before list");

    // operation 1
    assert !people.contains(xavier);
    remove("Xavier"); // a no-op.
    assert people.size() == 4;
    assert !people.contains(xavier);

    // operation 2
    assert people.contains(jill);
    remove("Jill"); // she smells!

    // after image
    printAll("the after list");
    assert people.size() == 3;
    assert people.contains(dave);
    assert people.contains(kelly);
    assert people.contains(jack);
    assert !people.contains(jill);
  }

  public static void main(String[] args) {
    try {
      new PeopleTest().run();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}

Output

C:\Java\home\src\forums>"C:\Program Files\Java\jdk1.6.0_16\bin\java.exe" -Xms4m -Xmx256m -enableassertions -cp c:\java\home\src;C:\Java\home\classes; forums.PeopleTest
java.util.ConcurrentModificationException
        at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
        at java.util.AbstractList$Itr.next(AbstractList.java:343)
        at forums.PeopleTest.badRemove(PeopleTest.java:36)
        at forums.PeopleTest.run(PeopleTest.java:62)
        at forums.PeopleTest.main(PeopleTest.java:89)

Dude,

THIS ANSWER IS WRONG, PLEASE IGNORE IT!!!

I suggest you use http://download.oracle.com/javase/6/docs/api/java/util/ArrayList.html#remove%28java.lang.Object%29 instead.

Something like:

public ArrayList<Person> people;

public void remove(String name) {
  for ( Person person : people ) {
    if (person.getName().equals(name)) {
      people.remove(person);
    }
  }
}

... presuming the Person has a getName mothod, which returns a string.

Cheers. Keith.

corlettk
  • 13,288
  • 7
  • 38
  • 52
  • 1
    Here http://stackoverflow.com/questions/5811608/removing-all-people-from-a-list-whose-names-match-parameter/5811720#5811720 is the same question and my blacklist-answer. :) – user unknown Apr 28 '11 at 02:59
-3

I dont know how do you store the name on the Person class but maybe you could do something like this. Remember that i stores ith element on the people arraylist so each iteration you need to check for the name of the current person to check if you need to remove it

public ArrayList<Person> people;

public void remove(String name){
        for(Person i : people){
            if (i.getName().equals(name)){
                people.remove(i);
            }
        }
    }
Dave
  • 532
  • 5
  • 8