0

I have some problem with my Java code. I'm supposed to use loops and not any other method. Say that my ArrayList contains of

[Dog Cat Dog Dog Cat Dog Horse]

My goal is also to remove the copies of Dog and Cat so my final results equals

[Dog Cat Horse]

public void removeDouble(){

int counter = 0;
for (int i = 0 ; i < animals.size(); i++) { 
    for (int j = 1+i;  j < animals.size() ; j++)  
        //don't start on the same word or you'll eliminate it.
        if ( animals.get(j).equals( animals.get(i) )  ) {
            animals.remove(animals.get(j));
           counter++;

        }                                
    } 
}

It feels like the "logic" is correct but my code does not work very well. Can somebody help me a little?

1
  • 2
    It feels like the "logic" is correct but my code does not work very well. Can you elaborate on this. What is the output? What were you expecting it to be? It is not advisable to remove an element from a List while iterating it. Commented Feb 4, 2017 at 16:27

8 Answers 8

5

You can do like this.

 ArrayList<String>list=new ArrayList<>();
    list.add("A");
      list.add("B");
      list.add("C");
      list.add("A");
    System.out.println("Before "+list); // output[A,B,C,A]


    Set<String> listWithoutDuplicates = new LinkedHashSet<String>(list);
     list.clear();

    list.addAll(listWithoutDuplicates);
    System.out.println("list without duplicates : " + list);// output[A,B,C]
Sign up to request clarification or add additional context in comments.

Comments

2

The logic for the inner loop is incorrect.

You will skip items every time you have the same item appear consecutively in the list.

Say you had "dog", "dog", "dog", "cat". When you remove the "dog" at index 1, the list now becomes "dog", "dog", "cat".

The problem is that your "j" index is now incremented to 2 so the next test will access the "cat" item, not the "dog" item. So every time you remove an item you are skipping the next item in the list which is why you get inconsistent results.

The solution is to either:

  1. decrement the j variable every time you remove an item
  2. start the inner loop from the end of the list and count down backwards toward 0.

Comments

1

It would be simpler to start from the end of the list and decrement the counter. After removing the double at i, we can break without checking the whole string, because further doubles will be detected when i reaches j.

    for(int i=animals.size()-1; i>0; i--) {
        for(int j=i-1; j>=0; j--) {
            if(animals.get(i).equals(animals.get(j))) {
                animals.remove(i);
                break;
            }
        }
    }

Moving backwards avoids the problem as you move forward the indexes have changed because you removed earlier elements (and you failed to adjust the index to take that into account).

Another problem with your logic you were using remove(object) rather than remove(index), which causes the first matching object to be removed. However, based on expected output, you want to preserve the order of the first matching objects. So instead you should have removed the last matching object, via index.


If you want to move forward rather than backwards, but you don't wish to make adjustments to the index after a removal, it is possible to make use of iterator's remove method:

    for(int i=0; i<animals.size()-1; i++) {
        ListIterator<?> iter = animals.listIterator(i+1);
        while(iter.hasNext()) {
            if(animals.get(i).equals(iter.next())) {
                iter.remove();
            }
        }
    }

Unfortunately the outer loop here cannot use an iterator because that would result in a ConcurrentModificationException.


Finally, you could also use a subList to solve it with a single explicit loop:

    for(int i=0; i<animals.size()-1; i++) {
        animals.subList(i+1, animals.size()).removeIf(animals.get(i)::equals);
    }

Comments

1

In Java 8 we can use Stream API to remove duplicates, Like below snippet.

List<String> uniqueAnimal = animal.stream().distinct().collect(Collectors.toList()); 

Working Example.

import java.util.*;
import java.util.stream.Collectors;
public class MyClass {
    public static void main(String args[]) {
        List<String> animal = new ArrayList<>();
        animal.add("Dog");
        animal.add("Cat");
        animal.add("Dog");
        animal.add("Dog");
        animal.add("Cat");
        animal.add("Dog");
        animal.add("Horse");
        List<String> uniqueAnimal = animal.stream().distinct().collect(Collectors.toList());
        System.out.println("animal =>  " + animal);
        System.out.println("uniqueAnimal =>  " + uniqueAnimal);
    }
}

Comments

1

With Java 8 stream you can do as follows:

public class RemoveDuplicates {

public static void main(String[] args) {
    removeDuplicateElements(Arrays.asList("Dog","Cat","Dog","Dog","Cat","Dog","Horse"));
  }

private static void removeDuplicateElements(List<String> animalList)
  {
    animalList.stream().distinct().collect(Collectors.toList()).forEach(System.out::println);
  }

}

3 Comments

The use of Java 8 stream() is the same as the answer by RAGINROSE, except your answer doesn't collect the elements into a new list.
It does for your example. However, the Question states that there's already a list with elements in it.
Yes I agree but as you see I have used Arrays.asList which reduces memory usage.when you use add function and when array list exceeds capacity it will create new one copy elements from old array to new array.So my answer is memory efficient and .even Iam printing list in efficient way.
0

Your removing the items as you are iterating over them, have an array that holds indexes, and when you find a double, add the index to the indexes array. Iterate over the indexes array and delete from the animals arraylist.

Comments

0

Your current code -

for (int i = 0; i < animals.size(); i++) {
     for (int j = 1 + i; j < animals.size(); j++)
         if (animals.get(j).equals(animals.get(i))) {
             animals.remove(animals.get(j)); // this would remove the current element only if the previous element is same as the current element
             // since the index `j` would change post that 
         }
     }
}

A simple way to do this is

animals.stream().distinct().collect(Collectors.toList()).forEach(System.out::print);

Or using -

Set<String> distAnimal = new HashSet<>(animals);
System.out.println(Arrays.toString(distAnimal.toArray()));

1 Comment

I'm supposed to use loops and not any other method.
0

Thanks for all the answers. I still have a few problems, this is what i have came up with:

    int counter =0;
for(int i = 0 ; i < animals.size() ; i++){
    for(int j = animals.size() -1 ; j>i ; j--){
        if(animals.get(j).equals(animals.get(i))){
            counter++;

}

    }
}
    System.out.println(counter);
}

So now I'm starting the inner loop from the end of the ArrayList. Priority right now is only the get the loop working and then add remove etc.

Cheers!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.