I am trying to remove duplicated words from an array, and I keep getting null values. I'm not allowed to use java sorting methods so I have to develop my own. Here's my code:
public class Duplicate{
public static void main(String[] args){
String[] test = {"a", "b", "abvc", "abccc", "a", "bbc", "ccc", "abc", "bbc"};
removeDuplicate(test);
}
public static String[] removeDuplicate(String[] words){
boolean [] isDuplicate = new boolean[words.length];
int i,j;
String[] tmp = new String[words.length];
for (i = 0; i < words.length ; i++){
if (isDuplicate[i])
continue;
for(j = 0; j < words.length ; j++){
if (words[i].equals(words[j])) {
isDuplicate[j] = true;
tmp[i] = words[i];
}
}
}
for(i=0;i<words.length;i++)
System.out.println(tmp[i]);
return tmp;
}
}
I tried doing
if(words == null)
words == "";
But it doesn't work. I also want to return the tmp array with a new size.
For example, test array length = 9, after removing the duplicates,I should get a new array with a length of 7.Thank you for your help.
EDIT:
result i get:
a
b
abvc
abccc
null
bbc
ccc
abc
null
You're getting nulls because the result array contains fewer words than the input array. However, you're constructing the arrays of the same length.
You don't have to sort to solve this problem. However, if you're not allowed to use the tools provided by java.utils, then this is either a poorly contrived test question or whomever told you not to use the Java utility classes is poorly informed.
You can solve without sorting by doing (assuming Java 1.5+):
public class Duplicate {
public static void main(String[] args) {
String[] test = {"a", "b", "abvc", "abccc", "a", "bbc", "ccc", "abc", "bbc"};
String[] deduped = removeDuplicate(test);
print(deduped);
}
public static String[] removeDuplicate(String[] words) {
Set<String> wordSet = new LinkedHashSet<String>();
for (String word : words) {
wordSet.add(word);
}
return wordSet.toArray(new String[wordSet.size()]);
}
public static void print(String[] words) {
for (String word : words) {
System.out.println(word);
}
}
}
The output will be:
a
b
abvc
abccc
bbc
ccc
abc
I would go for hashset to remove duplicates, it will remove duplicates since hash function for the same string will give same value, and duplicates will be eliminated. Then you can convert it to a string.
I would recommend doing this with a different approach. If you can use an ArrayList, why not just create one of those, and add the non-duplicate values to it, like this:
ArrayList<String> uniqueArrayList = new ArrayList<String>();
for(int i = 0; i < words.length; i++){
if(!uniqueArrayList.contains(words[i])){ // If the value isn't in the list already
uniqueArrayList.add(words[i]);
}
}
Now, you have an array list of all of your values without the duplicates. If you need to, you can work on converting that back to a regular array.
EDIT
I really think you should use the above option if you can, as there is no clean or decently efficient way to do this only using arrays. However, if you must, you can do something like this:
You can use the code you have to mark values as null if they are duplicates, and also create a counter to see how many unique values you have, like this:
int uniqueCounter = 0;
for(int i = 0; i < isDuplicate.length; i++){
if(!isDuplicate[i]){
uniqueCounter++;
}
}
Then, you can create a new array of the size of unique items, and loop through the words and add non-duplicate values.
String[] uniqueArray = new String[uniqueCounter];
int uniqueIndex = 0;
int wordsIndex = 0;
while(index < uniqueArray.length){
// Check if words index is not a duplicate
if(!isDuplicate[wordsIndex]){
// Add to array
uniqueArray[uniqueIndex] = words[wordsIndex];
uniqueIndex++; // Need to move to next spot in unique.
}
// Need to move to next spot in words
wordsIndex++;
}
Again, I HIGHLY recommend against something like this. It is very poor, and pains me to write, but for the sake of example on how it could be done using an array, you can try it.
I don't have the time to write functioning code, but I would reccomend to first sort the array using Arrays.sort(stringArray) and then loop throug the array coparing one string to the previous. Strings that match the previous one are duplicates.
Note: This method is probably not the fastest one and though only should be used on small arrays or in tasks where performance does not matter.
What about this approach?
public static String[] removeDuplicate(String[] words){
// remember which word is a duplicate
boolean[] isDuplicate = new boolean[words.length];
// and count them
int countDuplicate = 0;
for (int i = 0; i < words.length ; i++){
// only check "forward" because "backwards checked" duplicates have been marked yet
for(int j = i + 1; j < words.length ; j++){
if (words[i].equals(words[j])) {
isDuplicate[j] = true;
countDuplicate++;
}
}
}
// collect non-duplicate strings
String[] tmp = new String[words.length - countDuplicate];
int j = 0;
for (int i = 0; i < isDuplicate.length; i++) {
if (isDuplicate[i] == false) {
tmp[j] = words[i];
j++;
}
}
// and return them
return tmp;
}
Related
I've scoured a couple of the SOF threads but can't seem to find the answer I'm looking for. Most of them provide an answer with code that's beyond the scope of what I have learned thus far.
I've tried quite a few different things and can't get this to work the way I need it to.
The program is supposed to take the given array, read it, find the given toRemove item, and re-print the array without the toRemove item.
I believe my issue is within the removeFromArray method
public static void main(String[] args)
{
String[] test = {"this", "is", "the", "example", "of", "the", "call"};
String[] result = removeFromArray(test, "the");
System.out.println(Arrays.toString(result));
}
public static String[] removeFromArray(String[] arr, String toRemove)
{
int newLength = 0;
for(int i = 0; i < arr.length; i++)
{
if(arr[i].contains(toRemove))
{
newLength++;
}
}
String[] result = new String[arr.length-newLength];
for(int i = 0; i < (result.length); i++)
{
if(arr[i].contains(toRemove))
{
}
else
{
result[i] = arr[i];
}
}
return result;
}
This is an assignment in my java class and we have not learned Lists (one of the answers I stumbled upon in my googling) yet so that is not an option for me.
As it is now, it should be outputting:
[this, is, example, of, call]
Currently it is outputting: [this, is, null, example, of]
Any and all help will be much appreciated!
You need 2 indices in the second loop, since you are iterating over two arrays (the input array and the output array) having different lengths.
Besides, newLength is a confusing name, since it doesn't contain the new length. It contains the difference between the input array length and the output array length. You can change its value to match its name.
int newLength = arr.length;
for(int i = 0; i < arr.length; i++)
{
if(arr[i].contains(toRemove))
{
newLength--;
}
}
String[] result = new String[newLength];
int count = 0; // count tracks the current index of the output array
for(int i = 0; i < arr.length; i++) // i tracks the current index of the input array
{
if(!arr[i].contains(toRemove)) {
result[count] = arr[i];
count++;
}
}
return result;
There's the error that #Eran pointed out in your code, which can solve your problem. But I'm going to discuss another approach.
For now, you're first iterating over the entire array to find the number of occurrences to remove, and then, you're iterating over the array to remove them. Why don't you just iterate over the array, just to remove them. (I know, your first loop is helping you to determine the size of the output array, but you don't need that if you use some List like ArrayList etc.)
List<String> resultList = new ArrayList<String>();
for(int i = 0; i < arr.length; i++)
{
if(!arr[i].contains(toRemove))
{
resultList.add(arr[i]);
}
}
And you can return the resultList, but if you really need to return an array, you can convert the resultList to an array like this:
String [] resultArray = resultList.toArray(new String[resultList.size()]);
And then return this array. See this approach live here on ideone.
Try this Java8 version
List<String> test = Arrays.asList("this", "is", "the", "example", "of", "the", "call");
test.stream()
.filter(string -> !string.equals("the"))
.collect(Collectors.toList())
.forEach(System.out::println);
You can use Java Stream instead, it will give you the expected result and also your code will be clearer and really smaller.
See the method below I wrote that solves your problem.
public static String[] removeFromArray(String[] arr, String toRemove) {
return Arrays.stream(arr)
.filter(obj -> !obj.equals(toRemove))
.toArray(String[]::new);
}
If you're unfamiliar with java Stream, please see the doc here
The following code removes all occurrences of the provided string.
Note that I have added few lines for validate the input, because if we pass a null array to your program, it would fail. You should always validate the input in the code.
public static String[] removeFromArray(String[] arr, String toRemove) {
// It is important to validate the input
if (arr == null) {
throw new IllegalArgumentException("Invalid input ! Please try again.");
}
// Count the occurrences of toRemove string.
// Use Objects.equals in case array elements or toRemove is null.
int counter = 0;
for (int i = 0; i < arr.length; i++) {
if (Objects.equals(arr[i], toRemove)) {
counter++;
}
}
// We don't need any extra space in the new array
String[] result = new String[arr.length - counter];
int resultIndex = 0;
for (int i = 0; i < arr.length; i++) {
if (!Objects.equals(arr[i], toRemove)) {
result[resultIndex] = arr[i];
resultIndex++;
}
}
return result;
}
This question already has answers here:
Create ArrayList from array
(42 answers)
Closed 6 years ago.
I would like to convert an Array of Strings into an ArrayList of ArrayList, where the inner ArrayList has a dynamic number of elements. Who can help ? Thanks in advance
String[] sentences = {"hello","how are you","i am fine","and you ?","thank you"}
//Output with number of elements = 2
["hello","how are you"]
["i am fine","and you ?"]
["thank you"]
//Output with number of elements = 3
["hello","how are you","i am fine"]
["and you ?","thank you"]
public static void main(String[] args) {
String[] sentences = {"hello", "how are you", "i am fine", "and you ?", "thank you"};
System.out.println(split(2,sentences));
System.out.println(split(3,sentences));
}
public static List<List<String>> split(int numberOfElements, String[] sentences) {
List<List<String>> lists = new ArrayList<List<String>>();
int index = 0;
for (String sentence : sentences) {
if (index % numberOfElements == 0) {
lists.add(new ArrayList<String>());
}
lists.get(index / numberOfElements).add(sentences[index]);
index++;
}
return lists;
}
Output:
[[hello, how are you], [i am fine, and you ?], [thank you]]
[[hello, how are you, i am fine], [and you ?, thank you]]
public static void main(String[] args) {
String[] sentences = { "hello", "how are you", "i am fine", "and you ?", "thank you" };
List<List<String>> convertIntoList = convertIntoList(sentences, 2);
System.out.println(convertIntoList);
convertIntoList = convertIntoList(sentences, 3);
System.out.println(convertIntoList);
}
private static List<List<String>> convertIntoList(String[] sentences, int nbElement) {
List<List<String>> listOfListTarget = new ArrayList<List<String>>();
int currentIndex = 0;
while (currentIndex < sentences.length) {
int nextIndex = currentIndex + nbElement;
if (nextIndex > sentences.length) {
nextIndex = sentences.length;
}
final String[] copyOfRange = Arrays.copyOfRange(sentences, currentIndex, nextIndex);
List<String> subList = new ArrayList<String>();
subList.addAll(Arrays.asList(copyOfRange));
listOfListTarget.add(subList);
currentIndex+=nbElement;
}
return listOfListTarget;
}
Is this is a homework?
So you have an array of strings, and you want to create a List> with that, with each inner List containing at most x number of elements.
To get x number of elements and put them in a List, you can do a simple for loop.
String[] myStringArray = { ... };
List<String> myListOfString = new ArrayList<>();
for(int i=0; i<x; i++) {
myListOfString.add(myStringArray[i]);
}
So for example if you have these values
String[] myStringArray = {"a", "b", "c", "d", "e"};
x = 2;
You'll get the following list using the above loop:
["a", "b"]
Great! But we need to get all the contents of the myStringArray! How do we do that? Then let's do the first step, we iterate through all the contents of the array. We can do that like this.
int i=0;
while(i < myStringArray.length) {
System.out.println(myStringArray[i]);
i++;
}
Which will output:
a
b
c
d
e
This doesn't solve the problem... but at least we know how to iterate the whole thing. The next step is to get x of them. Sounds simple right? So basically we need to create a list of x from the contents. Maybe we can use the logic we created a few examples back to solve the problem.
// Create list of list of string here
int i = 0;
while(i < myStringArray.length) {
// Create list of string here
for(int j=0; j<x; j++) {
// Add myStringArray[j] to list of string here
}
// Add the list of string to the list of list of string here
i++;
}
Easy right? No. This gives the following lists:
["a", "b"]
["a", "b"]
["a", "b"]
["a", "b"]
["a", "b"]
Why? In the first loop, we are iterating up to how many is in the array. In the second loop, we are adding element 0 and 1 to a list. Obviously it wouldn't work. The second loop needs to be aware that it should not add previously added elements, and at the same time the first loop needs to be aware of what the second loop is doing. So you might think, maybe we can use the int i to indicate where the second loop should start?
int i = 0;
while(i<myStringArray.length) {
while(i<x) {
// add myStringArray[i];
i++;
}
i++;
}
Unfortunately, using the same values as previous, this will only give the following list
["a", "b"]
Because i is iterating through the whole array. So when it goes from 0 to length, whatever the value of i is used on the second array. When it loops again, i becomes 1, so the start of the second loop is at 1.
We need a separate variable to do the counting, while still keeping in mind where we currently are in the second loop.
int i = 0;
while(i<myStringArray.length) {
int count = 0;
while(count < x) {
// Add myStringArray[count+i] to list of string
count++;
}
// Add to list of list of string
i += count + 1; // Need to be aware of how much we have processed
}
This will do what we want, but unfortunately we can get in trouble at certain values. Say x is 10 and myStringArray is only of length 2. This will throw an exception because when it reaches the point of count+i = 3, that index doesn't exist anymore. The second loop also needs to be aware of how much is still remaining.
Finally we'll have the following code
int i = 0;
while(i<myStringArray.length) {
int count = 0;
while(count < x && count+i < myStringArray.length) {
// Add myStringArray[count+i] to list of string
}
// Add to list of list of string
i += count; // Need to be aware of how much we have processed
}
Which will give
["a", "b"]
["c", "d"]
["e"]
Edit: Next time try to put some code that you tried something.
I have an input like this in an ArrayList<String>:
cat eats mouse
mouse eats cheese
cheese is tasty
(blank lines should be ignored since I will be reading this input from a file)
and I want to convert it into a 2-d array of String which will have dimensions [no. of elements in ArrayList][3].
The no. 3 is fixed i.e. each sentence will have 3 words.
like this:
"cat" "eats" "mouse"
"mouse" "eats" "cheese"
"cheese" "is" "tasty"
here's what I have tried:
public static int processData(ArrayList<String> array)
{
String str[]=new String[array.size()];
array.toArray(str);
String str1[][]=new String[str.length][5];
for(int i=0;i<str.length;i++)
{
str1[i][]=str.split("\\s+"); //i want to do something like this, but this is showing errors.
}
return 0; //this is temporary, I will be modifying it
}
Tell me if I am not clear.
You are close. In Java, you can't put new elements at the end of an array by using empty brackets []. The following code does the thing. Note that number of elements in the second array is limited by 5. So, after the first 5 words, the rest of the line will be ignored. If the line is shorter, there will be nulls in the end of the array.
public static int processData(ArrayList<String> array) {
String[] str = new String[array.size()];
array.toArray(str);
String[][] str1 = new String[str.length][3];
for(int i=0; i < str.length; i++) {
String[] parts = str[i].split("\\s+");
for(int j = 0; j < parts.length || j < 3; j++) {
str1[i][j] = parts[j];
}
}
// do something next
}
A shorter, and slightelly more efficient version:
static int processData(ArrayList<String> array)
{
String str[][] = new String[array.size()][3];
for(int i = 0; i < str.length; ++i) {
str[i] = array.get(i).split("\\s+");
}
return 0;
}
There is no reasion for the first array called str in your code, since you cann access the Strings directly from the ArrayList.
Also you can don't have to copy the Strings, you can just put the arrays of Strings into the array of arrays, like in my code
Plus, if you have a fixed size of 3, and don't need to add any more to the arrays, why do you allocate space for 5 strings?
As you mentioned "arraylist" in Subject:
try{
BufferedReader br = new BufferedReader(new FileReader("filename"));
Arraylist<String[]> l = new ArrayList<String[]>();
String line;
while((line = br.readline) != null)
l.add(line.split("\\s+");
br.close();
}catch(Exception e){e.printStackTrace();}
Change your for loop to:
String str1[][] = new String[str.length][3];
for(int i = 0; i < str.length; i++) {
str1[i] = str[i].split("\\s+");
}
You don't need to have 5 elements if you know that you have only 3 words, do not waste your resources.
str is a String[], str[i] is a String, str[i].split() is a String[] and so is str1[i]. The types match.
Also, this way the code is clearer and easier to understand. I agree with Ongy to remove str if you do not need it, but I can't tell now because you said you are going to change this method later (at least the return value)
Bonus: Btw, the names array, str, str1 are not the best choice for that piece of code, it is really easy to be confused what is what. Try finding better name such as lines, linesArray, words or something like that
Let's say I got this array:
String[][]array = new String[5][5];
array[2][2] = desperate;
Would it be possible to find whether
String s = "desperate"; - equals any array element without using a for loop, and without having to manually enter the row column combination of the array assigned the value "desperate"?
while loop instead of for loop
int i = 0;
int j = 0;
while (i < n)
{
while (j < m)
{
if (array[i][j].equals("..."))
{
///
}
j++;
}
i++;
}
Use enhanced-for loop: -
String [][] array = new String[2][2];
array[1][1] = "desperate";
array[0][1] = "despee";
array[1][0] = "despete";
array[0][0] = "dete";
for (String[] innerArr: array) {
for (String value: innerArr) {
if (value.equals("desperate")) {
System.out.println(value + " == desperate");
}
}
}
Output: - desperate == desperate
A better way that I would suggest is to use ArrayList<String> to store your items.. Then you can just call contains() method to check whether the list contains that element..
List<String> listString = new ArrayList<String>();
listString.add("desperate");
listString.add("despe");
if (listString.contains("desperate")) {
System.out.println("True");
}
Output: - True
Assuming that you can't (for any reasons) change your array to another collection type:
String[][]array = new String[5][5];
array[2][2] = "desperate";
public boolean contains(String str){
return new HashSet<String>((List<String>)Arrays.asList(array)).contains(str);
}
Better than transforming it to a List since HashSet's contains() method is O(1) and the one from List is O(n).
The only way to avoid using a loop (and it not clear why you would want to) is to use a Map which you pre-build with all the strings and indexes.
I'm working on a program that uses an ArrayList to store Strings. The program prompts the user with a menu and allows the user to choose an operation to perform. Such operations are adding Strings to the List, printing the entries etc. What I want to be able to do is create a method called removeDuplicates(). This method will search the ArrayList and remove any duplicated values. I want to leave one instance of the duplicated value(s) within the list. I also want this method to return the total number of duplicates removed.
I've been trying to use nested loops to accomplish this but I've been running into trouble because when entries get deleted, the indexing of the ArrayList gets altered and things don't work as they should. I know conceptually what I need to do but I'm having trouble implementing this idea in code.
Here is some pseudo code:
start with first entry;
check each subsequent entry in the list and see if it matches the first entry;
remove each subsequent entry in the list that matches the first entry;
after all entries have been examined, move on to the second entry;
check each entry in the list and see if it matches the second entry;
remove each entry in the list that matches the second entry;
repeat for entry in the list
Here's the code I have so far:
public int removeDuplicates()
{
int duplicates = 0;
for ( int i = 0; i < strings.size(); i++ )
{
for ( int j = 0; j < strings.size(); j++ )
{
if ( i == j )
{
// i & j refer to same entry so do nothing
}
else if ( strings.get( j ).equals( strings.get( i ) ) )
{
strings.remove( j );
duplicates++;
}
}
}
return duplicates;
}
UPDATE: It appears that Will is looking for a homework solution that involves developing the algorithm to remove duplicates, rather than a pragmatic solution using Sets. See his comment:
Thx for the suggestions. This is part of an assignment and I believe the teacher had intended for the solution to not include sets. In other words, I am to come up with a solution that will search for and remove duplicates without implementing a HashSet. The teacher suggested using nested loops which is what I'm trying to do but I've been having some problems with the indexing of the ArrayList after certain entries are removed.
Why not use a collection such as Set (and an implementation like HashSet) which naturally prevents duplicates?
You can use nested loops without any problem:
public static int removeDuplicates(ArrayList<String> strings) {
int size = strings.size();
int duplicates = 0;
// not using a method in the check also speeds up the execution
// also i must be less that size-1 so that j doesn't
// throw IndexOutOfBoundsException
for (int i = 0; i < size - 1; i++) {
// start from the next item after strings[i]
// since the ones before are checked
for (int j = i + 1; j < size; j++) {
// no need for if ( i == j ) here
if (!strings.get(j).equals(strings.get(i)))
continue;
duplicates++;
strings.remove(j);
// decrease j because the array got re-indexed
j--;
// decrease the size of the array
size--;
} // for j
} // for i
return duplicates;
}
You could try this one liner to take a copy of the String preserving order.
List<String> list;
List<String> dedupped = new ArrayList<String>(new LinkedHashSet<String>(list));
This approach is also O(n) amortized instead of O(n^2)
Just to clarify my comment on matt b's answer, if you really want to count the number of duplicates removed, use this code:
List<String> list = new ArrayList<String>();
// list gets populated from user input...
Set<String> set = new HashSet<String>(list);
int numDuplicates = list.size() - set.size();
List<String> lst = new ArrayList<String>();
lst.add("one");
lst.add("one");
lst.add("two");
lst.add("three");
lst.add("three");
lst.add("three");
Set se =new HashSet(lst);
lst.clear();
lst = new ArrayList<String>(se);
for (Object ls : lst){
System.out.println("Resulting output---------" + ls);
}
I've been trying to use nested loops to accomplish this but I've been running into trouble because when entries get deleted, the indexing of the ArrayList gets altered and things don't work as they should
Why don't you just decrease the counter each time you delete an entry.
When you delete an entry the elements will move too:
ej:
String [] a = {"a","a","b","c" }
positions:
a[0] = "a";
a[1] = "a";
a[2] = "b";
a[3] = "c";
After you remove your first "a" the indexes are:
a[0] = "a";
a[1] = "b";
a[2] = "c";
So, you should take this into consideration and decrease the value of j ( j--) to avoid "jumping" over a value.
See this screenshot:
public Collection removeDuplicates(Collection c) {
// Returns a new collection with duplicates removed from passed collection.
Collection result = new ArrayList();
for(Object o : c) {
if (!result.contains(o)) {
result.add(o);
}
}
return result;
}
or
public void removeDuplicates(List l) {
// Removes duplicates in place from an existing list
Object last = null;
Collections.sort(l);
Iterator i = l.iterator();
while(i.hasNext()) {
Object o = i.next();
if (o.equals(last)) {
i.remove();
} else {
last = o;
}
}
}
Both untested.
Assuming you can't use a Set like you said, the easiest way of solving the problem is to use a temporary list, rather than attempting to remove the duplicates in place:
public class Duplicates {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("one");
list.add("two");
list.add("three");
list.add("three");
list.add("three");
System.out.println("Prior to removal: " +list);
System.out.println("There were " + removeDuplicates(list) + " duplicates.");
System.out.println("After removal: " + list);
}
public static int removeDuplicates(List<String> list) {
int removed = 0;
List<String> temp = new ArrayList<String>();
for(String s : list) {
if(!temp.contains(s)) {
temp.add(s);
} else {
//if the string is already in the list, then ignore it and increment the removed counter
removed++;
}
}
//put the contents of temp back in the main list
list.clear();
list.addAll(temp);
return removed;
}
}
You could do something like this, must of what people answered above is one alternative, but here's another.
for (int i = 0; i < strings.size(); i++) {
for (int j = j + 1; j > strings.size(); j++) {
if(strings.get(i) == strings.get(j)) {
strings.remove(j);
j--;
}`
}
}
return strings;
Using a set is the best option to remove the duplicates:
If you have a list of of arrays you can remove the duplicates and still retain array list features:
List<String> strings = new ArrayList<String>();
//populate the array
...
List<String> dedupped = new ArrayList<String>(new HashSet<String>(strings));
int numdups = strings.size() - dedupped.size();
if you can't use a set, sort the array (Collections.sort()) and iterate over the list, checking if the current element is equal to the previous element, if it is, remove it.
Using a set is the best option (as others suggested).
If you want to compare all elements in a list with eachother you should slightly adapt your for loops:
for(int i = 0; i < max; i++)
for(int j = i+1; j < max; j++)
This way you don't compare each element only once instead of twice. This is because the second loop start at the next element compared to the first loop.
Also when removing from a list when iterating over them (even when you use a for loop instead of an iterator), keep in mind that you reduce the size of the list. A common solution is to keep another list of items you want to delete, and then after you finished deciding which to delete, you delete them from the original list.
public ArrayList removeDuplicates(ArrayList <String> inArray)
{
ArrayList <String> outArray = new ArrayList();
boolean doAdd = true;
for (int i = 0; i < inArray.size(); i++)
{
String testString = inArray.get(i);
for (int j = 0; j < inArray.size(); j++)
{
if (i == j)
{
break;
}
else if (inArray.get(j).equals(testString))
{
doAdd = false;
break;
}
}
if (doAdd)
{
outArray.add(testString);
}
else
{
doAdd = true;
}
}
return outArray;
}
You could replace the duplicate with an empty string*, thus keeping the indexing in tact. Then after you've completed you can strip out the empty strings.
*But only if an empty string isn't valid in your implementation.
The problem you are seeing in your code is that you remove an entry during iteration, thus invalidating the iteration location.
For example:
{"a", "b", "c", "b", "b", "d"}
i j
Now you are removing strings[j].
{"a", "b", "c", "b", "d"}
i j
The inner loop ends and j is incremented.
{"a", "b", "c", "b", "d"}
i j
Only one duplicate 'b' detected...oops.
best practice in these cases is to store the locations that have to be removed, and remove them after you have finished iterating through the arraylist. (One bonus, the strings.size() call can be optimized outside of the loops by you or the compiler)
Tip, you can start iterating with j at i+1, you've already checked the 0 - i!
The inner for loop is invalid. If you delete an element, you cannot increment j, since j is now pointing at the element after the one you deleted, and you will need to inspect it.
In other words, you should use a while loop instead of a for loop, and only increment j if the elements at i and j do not match. If they do match, remove the element at j. size() will decrease by 1 and j will now be pointing at the following element, so there is no need to increase j.
Also, there is no reason to inspect all elements in the inner loop, just the ones following i, since duplicates before i have already been removed by prior iterations.
public <Foo> Entry<Integer,List<Foo>> uniqueElementList(List<Foo> listWithPossibleDuplicates) {
List<Foo> result = new ArrayList<Foo>();//...might want to pre-size here, if you have reliable info about the number of dupes
Set<Foo> found = new HashSet<Foo>(); //...again with the pre-sizing
for (Foo f : listWithPossibleDuplicates) if (found.add(f)) result.add(f);
return entryFactory(listWithPossibleDuplicates.size()-found.size(), result);
}
and then some entryFactory(Integer key, List<Foo> value) method. If you want to mutate the original list (possibly not a good idea, but whatever) instead:
public <Foo> int removeDuplicates(List<Foo> listWithPossibleDuplicates) {
int original = listWithPossibleDuplicates.size();
Iterator<Foo> iter = listWithPossibleDuplicates.iterator();
Set<Foo> found = new HashSet<Foo>();
while (iter.hasNext()) if (!found.add(iter.next())) iter.remove();
return original - found.size();
}
for your particular case using strings, you may need to deal with some additional equality constraints (e.g., are upper and lower case versions the same or different?).
EDIT: ah, this is homework. Look up Iterator/Iterable in the Java Collections framework, as well as Set, and see if you don't come to the same conclusion I offered. The generics part is just gravy.
I am bit late to join this question, but I have come with a better solution regarding the same using GENERIC type. All the above provided solutions are just a solution. They are increasing a lead to the complexity of whole runtime thread.
RemoveDuplicacy.java
We can minimize it using a technique which should do the required , at the Load Time.
Example : For suppose when you are using a arraylist of the class type as :
ArrayList<User> usersList = new ArrayList<User>();
usersList.clear();
User user = new User();
user.setName("A");
user.setId("1"); // duplicate
usersList.add(user);
user = new User();
user.setName("A");
user.setId("1"); // duplicate
usersList.add(user);
user = new User();
user.setName("AB");
user.setId("2"); // duplicate
usersList.add(user);
user = new User();
user.setName("C");
user.setId("4");
usersList.add(user);
user = new User();
user.setName("A");
user.setId("1"); // duplicate
usersList.add(user);
user = new User();
user.setName("A");
user.setId("2"); // duplicate
usersList.add(user);
}
The Class for which is the base for the arraylist used above : User class
class User {
private String name;
private String id;
/**
* #param name
* the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param id
* the id to set
*/
public void setId(String id) {
this.id = id;
}
/**
* #return the id
*/
public String getId() {
return id;
}
}
Now in java there are two Overrided methods present of Object (parent) Class, which can help here in the means to serve our purpose better.They are :
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
You have to override these methods in the User class
Here is the complete code :
https://gist.github.com/4584310
Let me know if you have any queries.
You can add the list into a HashSet and then again convert that hashset into list to remove the duplicates.
public static int removeDuplicates(List<String> duplicateList){
List<String> correctedList = new ArrayList<String>();
Set<String> a = new HashSet<String>();
a.addAll(duplicateList);
correctedList.addAll(a);
return (duplicateList.size()-correctedList.size());
}
here it will return the number of duplicates. You can also use the correctList with all unique values
Below is the code to remove duplicate elements from a list without changing the order of the list,without using temporary list and without using any set variables.This code saves the memory and boosts performance.
This is a generic method which works with any kind of list.
This was the question asked in one of the interviews.
Searched in many forums for the solution but could not find one,so thought this is the correct forum to post the code.
public List<?> removeDuplicate(List<?> listWithDuplicates) {
int[] intArray = new int[listWithDuplicates.size()];
int dupCount = 1;
int arrayIndex = 0;
int prevListIndex = 0; // to save previous listIndex value from intArray
int listIndex;
for (int i = 0; i < listWithDuplicates.size(); i++) {
for (int j = i + 1; j < listWithDuplicates.size(); j++) {
if (listWithDuplicates.get(j).equals(listWithDuplicates.get(i)))
dupCount++;
if (dupCount == 2) {
intArray[arrayIndex] = j; // Saving duplicate indexes to an array
arrayIndex++;
dupCount = 1;
}
}
}
Arrays.sort(intArray);
for (int k = intArray.length - 1; k >= 0; k--) {
listIndex = intArray[k];
if (listIndex != 0 && prevListIndex != listIndex){
listWithDuplicates.remove(listIndex);
prevListIndex = listIndex;
}
}
return listWithDuplicates;
}