I am novice to java. I have an ArrayList and I want to avoid duplicates on insertion. My ArrayList is
ArrayList<kar> karList = new ArrayList<kar>();
and the the field I want to check is :
kar.getinsertkar().
I have read that I can use HashSet or HashMap but I have no clue.
Whenever you want to prevent duplicates, you want to use a Set.
In this case, a HashSet would be just fine for you.
HashSet karSet = new HashSet();
karSet.add(foo);
karSet.add(bar);
karSet.add(foo);
System.out.println(karSet.size());
//Output is 2
For completeness, I would also suggest you use the generic (parameterized) version of the class, assuming Java 5 or higher.
HashSet<String> stringSet = new HashSet<String>();
HashSet<Integer> intSet = new HashSet<Integer>();
...etc...
This will give you some type safety as well for getting items in and out of your set.
A set is simply a collection that can contain no duplicates so it sounds perfect for you.
It is also very simple to implement. For example:
Set<String> mySet = new HashSet<String>();
This would provide you a set that can hold Objects of type String.
To add to the set is just as simple:
mySet.add("My first entry!");
By definition of a set, you can add whatever you want and never run into a duplicate.
Have fun!
EDIT : If you decide you are dead-set on using an ArrayList, it is simple to see if an object is already in the list before adding it. For example:
public void addToList(String newEntry){
if(!myList.contains(newEntry))
myList.add(newEntry);
}
Note: All my examples assume you are using String objects but they can easily be swapped to any other Object type.
Use a HashSet instead of an ArrayList. But, to really make the HashSet really work well, you must override the equals() and hashCode() methods of the class/objects that are inserted into the HashSet.
Foe example:
Set<MyObject> set = new HashSet<MyObject>();
set.add(foo);
set.add(bar);
public class MyObject {
#Override
public boolean equals(Object obj) {
if (obj instanceof MyObject)
return (this.id = obj.id)
else
return false;
}
// now override hashCode()
}
Please see the following documentation for overriding hashCode() and equals().
You can use LinkedHashSet, to avoid duplicated elements and keep the insertion order.
http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashSet.html
You need to use any Set implementation, e.g you can use HashSet.
If you want to add custom object kar into your HashSet, you need to override equals and hashcode method.
You can read more about equals and hashcode, see
You can implement own List which extends LinkedList and override its add methods:
public boolean add(E e)
public void add(int index, E element)
public boolean addAll(Collection collection)
public boolean addAll(int index, Collection collection)
An example removing repeated Strings in an ArrayList:
var list = new ArrayList<>(List.of(
"hello",
"java",
"test",
"hello"
));
System.out.println(list);
System.out.println(new ArrayList<>(new HashSet<>(list)));
Output:
[hello, java, test, hello]
[java, test, hello]
Related
I need a collection that keeps insertion order and has unique values. LinkedHashSet looks like the way to go, but there's one problem - when two items are equal, it removes the newest one (which makes sense), here's an example:
set.add("one");
set.add("two");
set.add("three");
set.add("two");
The LinkedHashSet will print:
one, two, three
But what I need is:
one, three, two
What would be the best solution here? Is there any collection/collections method that can do this or should I implement it manually?
Most of the Java Collections can be extended for tweaking.
Subclass LinkedHashSet, overriding the add method.
class TweakedHashSet<T> extends LinkedHashSet<T> {
#Override
public boolean add(T e) {
// Get rid of old one.
boolean wasThere = remove(e);
// Add it.
super.add(e);
// Contract is "true if this set did not already contain the specified element"
return !wasThere;
}
}
You can simply use a special feature of LinkedHashMap:
Set<String> set = Collections.newSetFromMap(new LinkedHashMap<>(16, 0.75f, true));
set.add("one");
set.add("two");
set.add("three");
set.add("two");
System.out.println(set); // prints [one, three, two]
In Oracle’s JRE the LinkedHashSet is backed by a LinkedHashMap anyway, so there’s not much functional difference, but the special constructor used here configures the LinkedHashMap to change the order on every access not only on insertion. This might sound as being too much, but in fact affects the insertion of already contained keys (values in the sense of the Set) only. The other affected Map operations (namely get) are not used by the returned Set.
If you’re not using Java 8, you have to help the compiler a bit due to the limited type inference:
Set<String> set
= Collections.newSetFromMap(new LinkedHashMap<String, Boolean>(16, 0.75f, true));
but the functionality is the same.
When initializing you're LinkedHashSet you could override the add method.
Set<String> set = new LinkedHashSet<String>(){
#Override
public boolean add(String s) {
if(contains(s))
remove(s);
return super.add(s);
}
};
Now it gives you:
set.add("1");
set.add("2");
set.add("3");
set.add("1");
set.addAll(Collections.singleton("2"));
// [3, 1 ,2]
even the addAll method is working.
All solution provided above are excellent but if we don't want to override already implemented collections. We can solve this problem simply by using an ArrayList with a little trick
We can create a method which you will use to insert data into your list
public static <T> void addToList(List<T> list, T element) {
list.remove(element); // Will remove element from list, if list contains it
list.add(element); // Will add element again to the list
}
And we can call this method to add element to our list
List<String> list = new ArrayList<>();
addToList(list, "one");
addToList(list, "two");
addToList(list, "three");
addToList(list, "two");
Only disadvantage here is we need to call our custom addToList() method everytime instead of list.add()
I have a POJO class SearchResults, that contains 4 Strings (title, number, date, status) and then all the getter and setter methods for it.
In another class I populate an ArrayList<SearchResults> results, is there a way I can go through that list results and erase any elements that have a duplicate number?
I've tried populating a new ArrayList by first passing results into a LinkedHashSet but that didn't work.
ArrayList<SearchResults> noDup;
noDup = new ArrayList<SearchResults>(new LinkedHashSet<SearchResults>(results));
I've also tried doing a .remove(indexof()) but that didn't work either.
if(noDup.contains(new SearchResults("-1","","",""))){noDup.remove(noDup.indexOf(new SearchResults("-1","","","")));}
Any suggestions?
Edit:
The equals() method in SearchResults (wonr refers to the number)
#Override
public boolean equals(Object object){
if(object == null){
return false;
}
if(getClass() != object.getClass()){
return false;
}
SearchResults result = (SearchResults) object;
if((this.wonr == null) ? (result.wonr == null): this.wonr.equals(result.wonr)){
return false;
}
return true;
}
The suggestions for implementing hashCode and equals are possible options, but does this single number value truly define what it means for these objects to be equivalent in the general case? If not, defining equals and hashCode that way seems to be a hack.
Without altering the definition of equivalence, if in just this case you want to elminiate values with the same number value, there are other approaches you can try. You didn't give us the API for your SearchResult class, so I'll assume there's an accessible field named number.
One quick way is to use a TreeSet which defines its idea of equivalence based on an underlying comparison operation. Write a custom Comparator that only looks at the number field and you're good to go:
Java 8
List<SearchResult> allResultsWithDuplicates = // ... populated list
Comparator<SearchResult> comparator =
(left, right) -> Integer.compare(left.number, right.number);
Set<SearchResult> uniqueNumbers = new TreeSet<>(comparator);
uniqueNumbers.addAll(allResultsWithDuplicates);
As JB Nizet mentioned, if your SearchResult class has a getNumber accessor method you can use a function reference and eliminate the lambda expression defining Comparator:
Comparator<SearchReult> comparator = Comparator.comparing(SearchResult::getNumber);
Java 5-7
In earlier versions of Java you must implement the Comparator class yourself. Then it plugs into the code given above in exactly the same way. This example assumes there is a int getNumber() accessor method on your SearchResult class:
Comparator<SearchResult> comparator =
new Comparator<SearchResult>() {
#Override
public int compare(SearchResult sr1, SearchResult sr2) {
// Optional support for null arguments is left as
// an exercise for the reader.
return Integer.compare(sr1.getNumber(), sr2.getNumber());
}
};
Another way you can do it with Java-8 is this way:
1) Create set of unique numbers,
2) Iterate over your list and filter by this set:
Set<Integer> numbers = new HashSet<>();
List<SearchResult> noDups = listWithDups.stream()
.filter(sr -> numbers.add(sr.getNumber()))
.collect(Collectors.toList());
If you implemented equals() and hashCode() so that they just look at Number property you could build a Set<SearchResult> instead of a ArrayList<SearchResult> and you will implicitly get no duplicates (this is one of the properties of sets - they don't contain duplicates). You can still iterate over the entries in the set so you should have all the functionality you need.
Do a stream of our list and use filter method and collect to an other list.
I have a List<MyClass> and there is an attribute named attribute1 in MyClass.
Now the question is, how i can get the attribute1 values from List<MyClass> as an array without looping through the list in traditional way?
You can use Guava's FluentIterable to collect your elements, here's an example assuming attribute1 is an Integer
//populated
List<MyClass> yourList;
List<Integer> listbyAttribute = FluentIterable.from(yourList)
.transform(new Function<MyClass, Integer>() {
public Integer apply(MyClass f) {
return f.getAttribute1();
}
}).toList();
More fun with this Guava class: here
You can create your own class which implements LIST interface. YOu should basically do the implementation which is exactly the same as one of the other implementation of list except;
1)you in your add method, every time you add a new element, append it in a string array which is a variable of your class.
2)and add an extra method, lets say giveMyAttribute1List and return the variable list I mentioned earlier.
you you basically have your answer.
List<MyClass> a = new myListIMpl<MyClass>();
a.giveMyAttribute1List();
If this is some kind of tricky question where you (yourself) are not allowed to use a for-each, an iterator nor an old fashioned for-loop (like being asked in a job interview), I would suggest using the following:
Collections.sort(myList, myComparator)
and create the comparator
public class MyClassComparator implements Comparator<MyClass> {
#Override
public int compare(final MyClass o1, final MyClass o2) {
// implement
}
}
and implement the compare method in such a way, that the attribute1 is either in front (or back, or somewhere you know). Then you can easily fetch the element from the list without manually looping through it.
But this answer applies only if this is some kind of "job interview question", otherwise looping through the list is most likely the only option you have.
If you want to get the value of an attribute of each element in a List, you have to visit each element of that List. In other words, you have to iterate over each element. Whether you visit the elements in order with a loop or randomly in some ridiculous way, you're looping/iterating.
What you are asking for is not possible.
I am trying to remove duplicate objects from an arraylist
see code below:
ArrayList<Customer> customers=new ArrayList<Customer>();
for(int i=0;i<accounts.size();i++){
customers.add(accounts.get(i).getCustomer());
}
for(int i=0;i<customers.size();i++){
for(int j=i+1;j<customers.size();j++){
if(customers.get(i).getSocialSecurityNo().compareTo(customers.get(j).getSocialSecurityNo())==0){
if(customers.get(i).getLastName().compareToIgnoreCase(customers.get(j).getLastName())==0){
if(customers.get(i).getFirstName().compareToIgnoreCase(customers.get(j).getFirstName())==0){
customers.remove(j);
}
}
}
}
}
However, it seems that the last object in the list is not being processed. Perhaps someone can pinpoint the error
Try adding j--; after removing an item. That will reindex for you and solve your issue.
The basic flaw is that since the ListArray is mutable, once you remove one element your indexes have to be readjusted.
if(customers.get(i).getFirstName().compareToIgnoreCase(customers.get(j).getFirstName())==0){
customers.remove(j--);
}
also try subtracting one from your i loop:
for(int i=0;i<customers.size()-1;i++){
for(int j=i+1;j<customers.size();j++){
public static void removeDuplicates(ArrayList list) {
HashSet set = new HashSet(list);
list.clear();
list.addAll(set);
}
override equals and hashcode appropriatley
custormers = new ArrayList(new HashSet(customers))
ensure the equals and hashmethod are correctly implemented
The code below worked for me. Give it a try. You can manipulate the compare method to suit your taste
ArrayList customers = .....;
Set customerlist = new TreeSet(new Comparator(){
#Override
public int compare(Customer c1, Customer c2) {
return c1.getSocialSecurityNo().compareTo(c2.getSocialSecurityNo());
}
});
customerlist.addAll(customers);
customers.clear();
customers.addAll(customerlist);
It's your int j=i+1 that causes trouble. You need to test with the last value of the customers list for each iteration.
Before you add them to the list in the above loop, why don't you check
if(!cutomers.contains(accounts.get(i).getCustomer())
{
//add them if it doesn't contain
}
It should save you from doing the second loop
Edit: Need to override the equals method.
So, about doing this right:
Your Customer objects should have an equals() and hashCode() method, which do the comparison. (Or you simply would have only one Customer object for each customer, which would mean your data model would have to be adjusted. Then the default hashCode/equals would do.)
If you have this, you can replace your three nested ifs with one:
if(customers.get(i).equals(customers.get(j)) {
customers.remove(j);
}
This would not yet solve your problem, but make it easier to have a clearer look on it. If
you look at which objects are compared to which others, you will see that after each removal
of an object from the list, the next one has the same index as the one which you just removed,
and you will not compare the current object to it. As said, j-- after the removal will solve this.
A more performant solution would be using a Set (which is guaranteed not to contain duplicates).
In your case, a HashSet<Customer> or LinkedHashSet<Customer> (if you care about the order)
will do fine.
Then your whole code comes down to this:
Set<Customer> customerSet = new HashSet<Customer>();
for(Account acc : accounts){
customerSet.add(acc.getCustomer());
}
List<Customer> customers = new ArrayList<Customer>(customerSet);
If you don't really need a list (i.e. indexed access), ommit the last line and simply
use the set instead.
My first thought was to use Sets, as others have mentioned. Another approach would be to use Java's version of the foreach, instead of using indexes. A general approach:
public static ArrayList removeDuplicates(ArrayList origList) {
ArrayList newList = new ArrayList();
for (Object m : origList) {
if (!newList.contains(m)) {
newList.add(m);
}
}
return newList;
}
In testing, I just used Strings; I'd recommend inserting Customer into the code where appropriate for type safety.
I am storing Integer objects representing an index of objects I want to track. Later in my code I want to check to see if a particular object's index corresponds to one of those Integers I stored earlier. I am doing this by creating an ArrayList and creating a new Integer from the index of a for loop:
ArrayList<Integer> courseselectItems = new ArrayList();
//Find the course elements that are within a courseselect element and add their indicies to the ArrayList
for(int i=0; i<numberElementsInNodeList; i++) {
if (nodeList.item(i).getParentNode().getNodeName().equals("courseselect")) {
courseselectItems.add(new Integer(i));
}
}
I then want to check later if the ArrayList contains a particular index:
//Cycle through the namedNodeMap array to find each of the course codes
for(int i=0; i<numberElementsInNodeList; i++) {
if(!courseselectItems.contains(new Integer(i))) {
//Do Stuff
}
}
My question is, when I create a new Integer by using new Integer(i) will I be able to compare integers using ArrayList.contains()? That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
I hope I didn't make this too unclear. Thanks for the help!
Yes, you can use List.contains() as that uses equals() and an Integer supports that when comparing to other Integers.
Also, because of auto-boxing you can simply write:
List<Integer> list = new ArrayList<Integer>();
...
if (list.contains(37)) { // auto-boxed to Integer
...
}
It's worth mentioning that:
List list = new ArrayList();
list.add(new Integer(37));
if (list.contains(new Long(37)) {
...
}
will always return false because an Integer is not a Long. This trips up most people at some point.
Lastly, try and make your variables that are Java Collections of the interface type not the concrete type so:
List<Integer> courseselectItems = new ArrayList();
not
ArrayList<Integer> courseselectItems = new ArrayList();
My question is, when I create a new Integer by using new Integer(i) will I be able to compare integers using ArrayList.contains()? That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
The short answer is yes.
The long answer is ...
That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
I assume you mean "... will that be the same instance as ..."? The answer to that is no - calling new will always create a distinct instance separate from the previous instance, even if the constructor parameters are identical.
However, despite having separate identity, these two objects will have equivalent value, i.e. calling .equals() between them will return true.
Collection.contains()
It turns out that having separate instances of equivalent value (.equals() returns true) is okay. The .contains() method is in the Collection interface. The Javadoc description for .contains() says:
http://java.sun.com/javase/6/docs/api/java/util/Collection.html#contains(java.lang.Object)
boolean contains(Object o)
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)).
Thus, it will do what you want.
Data Structure
You should also consider whether you have the right data structure.
Is the list solely about containment? is the order important? Do you care about duplicates? Since a list is order, using a list can imply that your code cares about ordering. Or that you need to maintain duplicates in the data structure.
However, if order is not important, if you don't want or won't have duplicates, and if you really only use this data structure to test whether contains a specific value, then you might want to consider whether you should be using a Set instead.
Short answer is yes, you should be able to do ArrayList.contains(new Integer(14)), for example, to see if 14 is in the list. The reason is that Integer overrides the equals method to compare itself correctly against other instances with the same value.
Yes it will, because List.contains() use the equals() method of the object to be compared. And Integer.equals() does compare the integer value.
As cletus and DJ mentioned, your approach will work.
I don't know the context of your code, but if you don't care about the particular indices, consider the following style also:
List<Node> courseSelectNodes = new ArrayList<Node>();
//Find the course elements that are within a courseselect element
//and add them to the ArrayList
for(Node node : numberElementsInNodeList) {
if (node.getParentNode().getNodeName().equals("courseselect")) {
courseSelectNodes.add(node);
}
}
// Do stuff with courseSelectNodes
for(Node node : courseSelectNodes) {
//Do Stuff
}
I'm putting my answer in the form of a (passing) test, as an example of how you might research this yourself. Not to discourage you from using SO - it's great - just to try to promote characterization tests.
import java.util.ArrayList;
import junit.framework.TestCase;
public class ContainsTest extends TestCase {
public void testContains() throws Exception {
ArrayList<Integer> list = new ArrayList<Integer>();
assertFalse(list.contains(new Integer(17)));
list.add(new Integer(17));
assertTrue(list.contains(new Integer(17)));
}
}
Yes, automatic boxing occurs but this results in a performance penalty. Its not clear from your example why you would want to solve the problem in this manner.
Also, because of boxing, creating the Integer class by hand is superfluous.