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))
Related
After some research, I found out how to find the index of an item within a 2D Array. However, I'm after just one value, the row number and also what if the item you are looking for appeared more than once?
How would you store the row number of all those times?
for(int j = 0; j < size; j++)
{
if (arr[i][j] == 88)
{
return i; // The value i wanna store
break;
}
}
If the number 88 appears more than once, how can I store all the different locations and later retrieve it?
You could store the values you want in a List.
List<Integer> rows = new ArrayList<>();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (arr[i][j] == 88) {
rows.Add(i); // The value i wanna store
break; // exit inner loop and continue with next row
}
}
}
i'm after just one value, the row number
But if 88 appears more than once, how can I store all the different
locations and later retrieve it?
Considering you don't know how many duplicated copies of the value you're looking for there could be, I'd suggest using an ArrayList to store the indexes.
create this before the loops:
List<Integer> indexList = new ArrayList<>();
then within the if block simply add the index value for the value you've found to the ArrayList:
if (arr[i][j] == 88){
indexList.add(i);
break;
}
you can then return the ArrayList if your method requires returning the data:
return indexList; // after the loops have finished processing
However, if the method return type is void then you can simply ignore the return indexList;
I'm trying to test a multidimensional array to see if contains any duplicate values. If it does I would like the method to return false. Otherwise I would like it to return true.
Here is my current code. Where is my logic wrong?
public static boolean isUnique(int[][] array2, int num) {
for (int i = 0; i < array2.length - 1; i++) {
for (int j = i + 1; j < array2.length; j++) {
if (array2[i] == array2[j]) {
return false;
}
}
}
return true;
}
Your current implementation is checking whether two rows are the same (this is a reference based check, rather than a value.) This means that it asks 'Are array2[i] and array2[j] the same address in memory, rather than do they contain the same things.
If you want to see whether the rows are unique you'd use array2[i].equals(array2[j]) instead of array2[i] == array2[j].
If you wanted to check for unique elements (array[i][j] != array2[i+m][j+n] where !(m == n == 0)) you'd need to iterate through both levels in a
for (int i = 0; i < array2.length; i++) {
for (int j = 0; j < array2[i].length; j++) {
// compare array2[i][j] to all other array2[m][n] here.
}
}
Right now your code is checking whether any of the arrays inside array2 are the same. array2[i] and array2[j] are both referring to arrays, because array2 is an array of arrays.
Instead, you want to look at the values inside of each of those arrays. Since you want to fail on any repeated value anywhere in the grid, you're effectively trying to flatten the structure into one collection, and check for duplicates in that.
A HashSet is the best data structure to use in this case. Traverse the entire grid, row-by-row, adding values into your new struture. If you encounter a duplicate, return false:
public static boolean isUnique(int[][] array2) {
Set<Integer> values = new HashSet<>();
for (int i = 0; i < array2.length; i++) {
for (int j = 0; j < array2[i].length; j++) {
if (!values.add(array2[i][j])) {
return false;
}
}
}
return true;
}
Some things to note here:
The set's add method will return false if you attempt to add a duplicate to the collection, so that's wrapped in an if statement for a simple, fail-fast stopping condition.
The sizes of each of the inner arrays are completely independent from the size of the outer array, so you still want to loop from 0 to the length of the array (when you're using <, you don't need the length - 1).
I have this task, find the most commonly seen element in int[][] array and print the number and times repeated.I solved the problem.
A friend of mine said that having 4 for()s is a bad idea.So I decided to try optimising it to remove one or two, but couldn't figure a way.
So here it is:
int cnt, element = arr[0][0], numberRepeats = 0;//cnt-counter,what's the element ,how many times it's repeated
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {//those two for's are for the current element
cnt = 0;//counter is nullified
for (int j2 = i; j2 < arr.length; j2++) {
for (int k = 0; k < arr[j2].length; k++) {//and those two are the compared element
if (arr[i][j] == arr[j2][k]) {//if the current element is the same as the compared element,increase counter
cnt++;
}
}
if (cnt > numberRepeats) {//after the compared element is done comparing and the number of repeats of the current element is more then the lastly checked element
element = arr[i][j];//we get the element ,and how many times it's repeated
numberRepeats = cnt;
}
}
}
}
The only optimisation that I could think of was starting the counting from the current element till the end.
Please give me some more ideas. All the answers I could find were for the 1D array, and they were all pretty much the same code.
You can just iterate your whole array and count the occurrences of every element in a map.
Map<Integer, Integer> elementsCounts = new HashMap<>();
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
Integer count = elementsCounts.get(arr[i][j]])
if(count == null){
count = 0
}
elementsCounts.put(arr[i][j]], count+1)
}
}
Now all it gets is to find the key with the maximum value in the map.
We need to use Map and as well as Set (Set to identify the duplicate elements in a row), only using Map will overlook an edge case scenario where duplicate value in a row may result in incrementing the counter. And finally will be considered as a common value.
Summarizing in below steps:
Populate map with key as a the value of array [i][j] and Map's value as a counter.
IF element is absent in the Map then add key-value pair, Mpa's value as counter initializing from 1.
ELSE, update the element in the Map incrementing the counter with 1
Before incrementing the counter check for duplicate value in the row using Set
Finally, iterate the Map using entrySet() and print only those element whose counter is equal to length of array.
input e.g.:
Integer [][] array = {{1,2,3},{1,4,5,5},{1,5,7}};
output result:
{1}
I have the following method that accepts a generic object (When I use it, I pass an Integer object) and removes the object at that position in the array. Note that my
private currentSize;
private E[] list;
...
public E remove(E obj){
for (int i = 1; i < currentSize; i++)
if( ((Comparable<E>)obj).compareTo(list[i]) == 0){
for(int j = i; j < currentSize; j++)
list[j] = list[j+1];
currentSize--;
return obj;
}
return null;
}
I have two other methods that relate to my method above.
for(int i=1; i <= 10; i++)
list.addLast(i);
The code above adds 10 sequential ints to my array. (This works perfectly. Note that the addLast method parses only a primitive int, because I have it declared like so in addLast() )
for(int i=1; i <= 10; i++){
list.remove(new Integer(i));
}
The code above removes those 10 ints we added before. My remove() method kind of works, it removes all Integer objects from the array except 1.
My output when I call both addLast and then remove methods in another class:
"1 2 3 4 5 6 7 8 9 10
Now removing them all
Current size of list: (should be zero) 1"
That one in front of (should be zero) is not supposed to be there. Please note that I am trying to ignore the 0 index of the array. Also, I would like to know the way of implementing a backwards implementation of my inner-most for loop for(int j = i; j < currentSize; j++). Sort of like [1,2,3,4,5] ... if I want to "remove" #2 in the array, I would start from the end of the array instead of the beginning.
addLast method code:
public void addLast(E obj){
list[currentSize++] = obj;
//System.out.println(currentSize);
}
Print all the objects in array:
for(int x : list)
System.out.print(x + " ");
System.out.println("\n");
Arrays in java are zero bases. So you have to start at zero in your loops:
for(int i=0; i < 10; i++){
list.remove(new Integer(i));
}
And:
private currentSize;
private E[] list;
...
public E remove(E obj){
for (int i = 0; i < currentSize; i++)
if( ((Comparable<E>)obj).compareTo(list[i]) == 0){
for(int j = i; j < currentSize; j++)
list[j] = list[j+1];
currentSize--;
return obj;
}
return null;
}
Your loop in the remove method ignores the first element of the array (1 in your case), so it will never be removed :
for (int i = 1; i < currentSize; i++)
change it to
for (int i = 0; i < currentSize; i++)
Let's look at your addLast method :
list[currentSize++] = obj;
When the list is empty (i.e. currentSize is 0), currentSize++ returns 0 (since it's the postfix increment) and the first element is added to list[0]. Therefore when removing an element from the array you must consider all the indices from 0 to currentSize-1.
That one in front of (should be zero) is not supposed to be there.
Looks like your loop should be i <= currentSize. Ie you are probably not checking whether the item to remove is the last one. So when you ask to remove 1 it isn't found.
But you are not giving enough information.
How to create a Minimal, Complete, and Verifiable example.
I would like to know the way of implementing a backwards
implementation of my inner-most for loop for(int j = i; j < currentSize; j++).
Why? And what does this even mean? If you want to keep the elements in order then your current code puts each one directly into its correct place already. If you don't care about order then you could just move the last element to where the to-be-removed element is by list[i] = list[currentSize--] instead of looping on j.
But again you have not given us enough information about what the external behaviour of your operators is supposed to be or about how external state is represented by internal state.
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;
}