Removing contiguous duplicates in ArrayList [Java] - java

I am doing a simple Java program and I need to remove all of the contiguous duplicates in a String ArrayList.
My String ArrayList is something like this:
list = [a,b,c,c,d,a,b,c,d]
My goal is removing all (and only!) the contiguous duplicates so that the result would be: [a,b,c,d,a,b,c,d]. As you can see, one of the two contiguous "c" has been removed.
I tried something like this:
for (int i = 0; i<list.size(); i++) {
if (list.get(i).compareTo(list.get(i+1))==0) {
positionToRemove.add(i);
}
}
Where positionToRemove will at the end contain all the position of the contiguous elements which I will then remove using list.remove() (still not done)
Unfortunately I get
java.lang.IndexOutOfBoundsException
I am quite positive there is a very simple way to achieve this but I can't remember it at the moment!

There is no need to store the indexes of the elements you need to remove. Just remove it directly by:
int size = list.size();
for (int i = size - 1; i >= 1; i--) {
if (list.get(i).compareTo(list.get(i - 1)) == 0) {
list.remove(i);
}
}

In the last iteration of the for loop, list.get(i+1) goes beyond the bounds of the list and thus the IndexOutOfBoundsException. In any array/arraylist, the maximum accessible index is always size/length - 1.
To fix that you need to change your logic a bit.
for (int i = 1; i<list.size(); i++) {
if (list.get(i-1).compareTo(list.get(i))==0) {
positionToRemove.add(i);
}
}

When you access the List using index + 1 or i+1, you overstep the bounds of the List on the last iteration. You can fix this by setting the conditional for your for loop to i < list.size() -1.
for (int i = 0; i < list.size() -1; i++) {
if (list.get(i).compareTo(list.get(i+1))==0) {
positionToRemove.add(i);
}
}

It should not reach the length of your list. You must shutdown the traverses at size -1.
for (int i = 0; i<list.size() - 1; i++) {
if (list.get(i).compareTo(list.get(i+1))==0) {
positionToRemove.add(i);
}
}

Where positionToRemove will at the end contain all the position of the
contiguous elements which I will then remove using list.remove()
(still not done)
Instead of storing each position, you could start from the end of the list and directly remove the current element if it's the same as its left neighbour. Using this, you don't need to create another list containing the indexes of the objects you want to remove.
List<String> list = new ArrayList<>(Arrays.asList("a","b","c","c","d","a","b","c","d"));
for(int i = list.size() - 1; i > 0; i--){
if(list.get(i).compareTo(list.get(i-1)) == 0){
list.remove(i);
}
}
System.out.println(list);
Which outputs:
[a, b, c, d, a, b, c, d]

You can do this with Java 7, using iterator.
Iterator<Integer> iterator = collection.values().iterator();
Integer previousValue = null;
while(iterator.hasNext()) {
Integer currentValue = iterator.next();
if(currentValue.equals(previousValue)){
iterator.remove();
}
previousValue = currentValue;
}

Related

Java Remove method to return new array removing occurences of integer (no arraylist)

I am trying to create a method (without using arraylist) to return a new array that removes all instances of some integer (call it x). (For example, b=[2,5,3,2,7] b.remove(2) would return [5,3,7]. This code I have been working on (one of several hours worth of different attempts) seems to work when there is one occurence of X, but not many. When there are many, it sizes the new array correctly, but does not copy the data correctly for at/after the second occurence of X.
What I am trying to do is set a counter for each time X occurs, then set a new array that has length (old array length - count variable). Then I need to shift all the data after any occurence of X left. Here's my current code:
public Sequence remove(int n) {
int count = 0;
int a = 0;
for (int z=0; z < this.values.length; z++) {
if (this.values[z] == n)
count++;
}
Sequence newSequence = new Sequence(this.values.length - count);
for (int b=0; b < this.values.length - count; b++) {
if (this.values[a] != n) {
newSequence.values[a] = this.values[a];
a++;
} else {
newSequence.values[a]=this.values[a+1];
}
}
return newSequence;
}
I think the logic for populating the new resized array should be something like this:
walk through the entire original array
if a given value be the one you want removed, do nothing
otherwise add it to the new array and also increment the index in the new array
int pos = 0; // keeps track of position in newSequence.values
for (int i=0; i < this.values.length; i++) {
if (this.values[i] != n) {
newSequence.values[pos] = this.values[i];
pos++;
}
}
To be honest I did not completely get what you trying to do. But I understood the problem and your code. I would follow these steps to solve this problem.
Iterate through the array and count the number of times n (assuming you want to remove n) occurs. This count is stored in count variable.
Create a new array with size values.length-count (here values is the array)
Copy numbers from values array to new array.
This gives a O(n) solution.

Gets the number of duplicates in the list - wrong output

How to count duplicates in ArrayList and count only once.
Here is what I have so far:
/**
* Gets the number of duplicates in the list.
* Get the next word. It is at index i. Does it match any of the words with index > i?)
* #return the number of duplicate words in the list
*/
public int countDuplicates() {
int duplicates = 0;
for (int i = 0; i < list.size(); i++) {
for (int j = i; j < list.size(); j++) {
if (list.get(i).equals(j)) duplicates++;
}
}
return duplicates;
}
Here is check output:
Actual: 0
Expected: 3
I am missing something very easy. However, couldn't find what exactly it is.
How to solve this trouble?
You don't get the jth element you just compare to j directly. And as a commenter points out, j should start at i+1 to avoid comparing an element to itself. Therefore, you need to write
public int countDuplicates()
{
int duplicates = 0;
for (int i = 0; i < list.size(); i++) {
for (int j = i+1; j < list.size(); j++) {
if (list.get(i).equals(list.get(j))) duplicates++;
}
}
return duplicates;
}
Should be:
public int countDuplicates()
{
int duplicates = 0;
// TODO: Write the code to get the number of duplicates in the list
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
if (list.get(i).equals(list.get(j))) duplicates++;
}
}
return duplicates;
}
Use two sets for this:
final Set<X> set = new HashSet<>();
final Set<X> dups = new HashSet<>();
int dupCount = 0;
for (final X x: list) {
if (set.add(x)) // first time the element is seen
continue;
// Dup; see whether it is the first time we see it
if (dups.add(x))
dupCount++;
}
return dupCount;
This relies on the fact that Set's .add() returns true if and only if the set has been modified as the result of the operation. And note that it traverses the list only once.
I can see three problems with your current code:
You are not comparing pairs of elements. You are actually comparing an element with an index.
Your inner loop is comparing element i and element i ... and that would result in a false "duplicate" count.
If you have more than 2 copies of any given element, then you will get too many duplicate counts. (To see why, try to "hand execute" with a list of (say) three identical elements.
In fact, you have to EITHER use an auxiliary data structure (e.g. 2 Sets or a Map) OR modify the input list to avoid counting duplicates more than once.
I would note that your statement of the problem is ambiguous. "... only count each duplicate once" could mean that '[1, 1, 1]' gives either 1 or 2. It depends whether you consider each individual 1 to be a duplicate to be counted once or that we have 1 as one of a set of duplicates ... that must only be counted once.
You are comparing index j value instead of value of list list.get(j).
Do
if (list.get(i).equals(list.get(j)))
instead of
if (list.get(i).equals(j))

Unique Integers From Array List

I am having problems with making a method that will return distinct integers of the array list. I really want to do it with removing the duplicates and then just display the array list. I cannot figure out what is the problem. When I test it out this is the output I get: [3, 11, 33, 10]
This is my code
package getUniques;
import java.util.ArrayList;
public class Uniques {
public static ArrayList<Integer> getUniques( ArrayList<Integer> list ){
int i = 0;
while(i < list.size() - 1){
for (int j = 0; j < list.size(); j++){
if (list.get(i) == list.get(j))
list.remove(i);
}
i++;
}
return list;
}
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(3);
list.add(3);
list.add(5);
list.add(11);
list.add(22);
list.add(33);
list.add(22);
list.add(10);
System.out.println(getUniques(list));
}
}
You can also get unique values by using Set. Insert the values in a Set and then put it back into an ArrayList like new ArrayList(theSet);
Changing the list as you iterate over it is always going to cause pain!
Say you remove item 3 (so the old 4th becomes the new 3) - then you do i++, so you are effectively skipping the "old 4th" element.
You can skip the i++ if you removed the item to get back on track, but a some other
solutions:
Use a Set or similar in the first place so you can't get duplicates.
Use a second list to hold the values (or indexes) of items you want
to remove (if using indexes, you can remove them highest to lowest
else you end up with the same issue: delete index 1, index 4 is now
index 3...)
Flip your search so you are going back towards 0, same principal
applies. You can remove high indexes without impacting lower ones.
Make your outer loop use an iterator so you can use the remove operation.
Your code has a few problems. Here's fixes for your existing code:
First you are removing the wrong index. You've identified the element at j as the duplicate; remove it instead of the element at i.
list.remove(j); // j not i
Next, you are removing all elements that are the same, and you aren't leaving the "original". To fix this, only test (and remove) those that are past i in the loop.
for (int j = i + 1; j < list.size(); j++){ // Start at i + 1, not 0.
Then you'll need to retry your j index once you've removed it, because the rest of the elements have been shifted backwards 1 spot. Instead of
if (list.get(i) == list.get(j))
list.remove(i);
Try
if (list.get(i) == list.get(j))
{
list.remove(j);
j--; // Try this j again next loop, once it's incremented again.
}
To remove items while iterating, you have to use an iterator, as it guarantees the order:
Iterator<Integer> iterator = list.iterator();
int i = 0;
List<Integer> listCopy = new ArrayList<Integer>(list);
while(iterator.hasNext()){
i++;
Integer value = iterator.next()
for (int j = i; j < listCopy.size(); j++){
if (value.equals(listCopy.get(j))) {
iterator.remove();
}
}
}
However, in this case, as you need to iterate twice through the same list, it's not the best solution. It might be faster putting everything into a sorted Set, as Set removes duplicates on its own.

Removing duplicates in an Array-based unsorted list without sets

Consider the operation removeAll, which removes all occurrences of element from a list.
The method returns the number of elements removed.
public int removeAll(E element)
Implement this operation for:
1. Array-based unsorted list
we cant use sets.
What i have started for now:
public int removeAll(T element) {
int duplicatesRemoved = 0;
for (int i = 0; i <= array.length - 1; i++) {
for (int j = i + 1; j <= array.length - 1; j++) {
if (array[i] == array[j]) {
}
}
I'm unable to get the rest done, any help please?
Dump the contents in a collection of some sort since you're not allowed to use Set, then pull out what's in the collection.
Arrays (the class, not primitive arrays) support a contains method, but you'll be iterating over the new collection everytime, making it inefficient.
Or if you can't use Array either, you can do it in a primitive array, just walking through looking for duplicates, over and over.
I suggest that you set the duplicate elements to something like null. After you've removed all duplicates, compact the array by swapping non-null elements at the end with the null elements in the middle.
Alternatively, create an empty array, and move non-duplicate elements to the new array.
Short answer
Use a Map.
More answer
Here is an algorithm, it is functional, but can be improved:
Create a Map<T, Boolean>. The second parameter type is whatever you want, I chose Boolean.
Create a new array named newArray; this will contain the unique values.
Iterate through the array. For each item perform the following:
Using the current arrayValue as the key, get the storedValue from the map.
if the storedValue is null (i.e. the Map.get() returned null) then this is a unique item. Insert the arrayValue, Boolean.TRUE into the map and add the arrayValue to the newArray.
if the storedValue is not null, then increment the duplicate count.
After iterating through the list, the newArray contains all non-duplicates and the duplicate count contains the count of duplicates.
1.
/**
* This method removes all duplicates from an array named "array"
* using a temporary List. So it converts the array into
* something like a Java set
*
* #return int number of duplicates removed
*/
public static int removeAll() {
int duplicates = 0;
List<Object> list = new ArrayList<>();
for(int i=0;i<array.length;i++){
Object element = array[i];
if(list.contains(element)) {
duplicates++;
}
else {
list.add(element);
}
}
array = list.toArray();
return duplicates;
}
2.
/**
* This method removes duplicates from an array named "array" using a
* temporary List.
* #param elementToBeRemoved
* #return int number of duplicates removed
*/
public static int removeAll(Object elementToBeRemoved) {
int duplicates = 0;
List<Object> list = new ArrayList<>();
for (int i = 0; i < array.length; i++) {
Object element = array[i];
if (list.contains(elementToBeRemoved)) {
duplicates++;
} else {
list.add(element);
}
}
array = list.toArray();
return duplicates;
}
As this seems homework, no explanation, but this puzzle:
int lengthWithValues = array.length;
for (int i = 0; i < lengthWithValues; i++) {
// Loop invariant: for all at < i array is sorted, unique and complete.
int valueToBeChecked = array[i];
for (int k = i + 1, int j = i + 1; k < lengthWithValues; j++) {
if (array[j] == valueToBeChecked) {
--lengthWithValues; // Remove duplicate
} else {
array[k] = array[j]; // Maintain differing from all at <= i.
++k;
}
}
}
duplicatesRemoved = array.length - lengthWithValues;
// array[0 .. length - duplicatesRemoved] is the unique array.
int[] uniqueArray = new int[lengthWithValues];
System.arrayCopy(array, 0, uniqueArray, 0, lengthWithValues);

Remove every 3rd element in arraylist

I am trying to loop through an arraylist and gradually remove an element every 3 indices. Once it gets to the end of the arraylist I want to reset the index back to the beginning, and then loop through the arraylist again, again removing an element every 3 indices until there is only one element left in the arraylist.
The listOfWords is an array with a length of 3 that was previously filled.
int listIndex = 0;
do
{
// just to display contents of arraylist
System.out.println(listOfPlayers);
for(int wordIndex = 0; wordIndex < listOfWords.length; wordIndex++
{
System.out.print("Player");
System.out.print(listOfPlayers.get(wordIndex));
System.out.println("");
listIndex = wordIndex;
}
listOfPlayers.remove(listOfPlayers.get(listIndex));
}
while(listOfPlayers.size() > 1);
I have tried to implement for several hours yet I am still having trouble. Here's what happens to the elements of the arraylist:
1, 2, 3, 4
1, 2, 4
1, 2
Then it throws an 'index out of bounds error' exception when it checks for the third element (which no longer exists). Once it reaches the last element I want it to wrap around to the first element and continue through the array. I also want it to start where it left off and not from the beginning once it removes an element from the arraylist.
Maybe I have just missed the boat, but is this what you were after?
import java.util.ArrayList;
import java.util.Random;
public class Test {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
Random r = new Random();
//Populate array with ten random elements
for(int i = 0 ; i < 4; i++){
numbers.add(r.nextInt());
}
while(numbers.size() > 1){
for(int i = 0; i < numbers.size();i++){
if(i%3 == 0){//Every 3rd element should be true
numbers.remove(i);
}
}
}
}
}
You could move every third element to a temporary list then use List#removeAll(Collection) to remove the items when you finish each loop...until the master list was empty...
Lets back up and look at the problem algorithmically.
Start at the first item and start counting.
Go to the next item and increment your count. If there is no next item, go to the beginning.
If the count is '3', delete that item and reset count. (Or modulo.)
If there is one item left in the list, stop.
Lets write pseudocode:
function (takes a list)
remember what index in that list we're at
remember whether this is the item we want to delete.
loop until the list is size 1
increment the item we're looking at.
increment the delete count we're on
should we delete?
if so, delete!
reset delete count
are we at the end of the list?
if so, reset our index
Looking at it this way, it's fairly easy to translate this immediately into code:
public void doIt(List<String> arrayList) {
int index = 0;
int count = 0;
while(arrayList.size() != 1) {
index = index + 1;
count = count + 1; //increment count
String word = arrayList.get(index);//get next item, and do stuff with it
if (count == 3) {
//note that the [Java API][1] allows you to remove by index
arrayList.remove(index - 1);//otherwise you'll get an off-by-one error
count = 0; //reset count
}
if (index = arrayList.size()) {
index = 0; //reset index
}
}
}
So, you can see the trick is to think step by step what you're doing, and then slowly translate that into code. I think you may have been caught up on fixing your initial attempt: never be afraid to throw code out.
Try the following code. It keeps on removing every nth element in List until one element is left.
List<Integer> array = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
int nth = 3;
int step = nth - 1;
int benchmark = 0;
while (array.size() > 1) {
benchmark += step;
benchmark = benchmark > array.size() - 1 ? benchmark % array.size() : benchmark;
System.out.println(benchmark);
array.remove(array.get(benchmark));
System.out.println(array);
}
You could use a counter int k that you keep incrementing by three, like k += 3. However, before you use that counter as an index to kick out any array element, check if you already went beyond and if so, subtract the length of this array from your counter k. Also make sure, to break out of your loop once you find out the array has only one element left.
int k = -1;
int sz = list.length;
while (sz > 1)
{
k += 3;
if (k >= sz)
{
k -= sz;
}
list.remove(k);
sz --;
}
This examples shows that you already know right away how often you will evict an element, i.e. sz - 1 times.
By the way, sz % 3 has only three possible results, 0, 1, 2. With a piece of paper and a cup of coffee you can find out what the surviving element will be depending on that, without running any loop at all!
You could try using an iterator. It's late irl so don't expect too much.
public removeThirdIndex( listOfWords ) {
Iterator iterator = listOfWords.iterator
while( iterator.hasNext() ){
iterator.next();
iterator.next();
iterator.next();
iterator.remove();
}
}
#Test
public void tester(){
// JUnit test > main
List listOfWords = ... // Add a collection data structure with "words"
while( listOfWords.size() < 3 ) {
removeThirdIndex( listOfWords ); // collections are mutable ;(
}
assertTrue( listOfWords.size() < 3 );
}
I would simply set the removed to null and then skip nulls in the inner loop.
boolean continue;
do {
continue = false;
for( int i = 2; i < list.length; i += 3 ){
while( list.item(i++) == null && i < list.length );
Sout("Player " + list.item(--i) );
continue = true;
}
} while (continue);
I'd choose this over unjustified shuffling of the array.
(The i++ and --i might seem ugly and may be rewritten nicely.)

Categories