Why do we not get the ordered sequence in HashSet - java

I am using the HashSet for adding the elements and retrieving them, I know that I will not retrieve the data in sequence in which I added them, but I want to know the exact reason why Is it happeing?
import java.util.HashSet;
import java.util.Iterator;
public class HS {
public static void main(String args[]) {
HashSet h=new HashSet();
h.add("Mayank");
h.add("Mayank");
h.add("Vashist");
h.add("Dinesh");
h.add("Vashist");
Iterator itr=h.iterator();
while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}

This is just the contract for a Set in java, from the javadoc
Returns an iterator over the elements in this set.
The elements are returned in no particular order (unless this set is an instance of some class that provides a guarantee). So an implementation of Set isn't required to maintain any order in the values.
In order to return values in order the Set needs to maintain the order. This has costs for speed and space.
A LinkedHashSet maintains insertion order.

HashSet does not preserve the element addition order. First it computes the object hash code that should stay constant but is difficult to predict, and then uses it to select a bucket that is a list of objects that have had the same bucket selected. As an Iterator just iterates over all buckets, the iteration order is largely unpredictable.
Use LinkedHashSet instead if you need to preserve the order. However LinkedHashSet maintains an additional linked list so needs more resources.

A HashSet uses what is referred to as a hash table to store items.
A hash table is made up of several "slots" into which your items are put. Deciding what slot to put an item into is determined by that item's hash code which generally has no relationship to the natural ordering of the item.
A TreeSet, on the other hand, stores items based on their natural ordering which allows an in-order traversal of its contents. This order will be based on the natural ordering of the objects and not the order in which they were inserted. Another difference between a TreeSet and HashSet is that a HashSet provides O(1) lookup, insertion and removal where as a TreeSet provides O(log(n)) lookup, insertion and removal.
A LinkedHashSet maintains insertion order of items by constructing links between the elements as they are inserted.

Because in HashSet there is a hash value calculated for each object and this hash value determines the array index of the particular object in the container. So the order of inserted elements are naturally not preserved.
This allows for accessing desired elements with O(1) complexity but it costs a lot of memory.
http://en.wikipedia.org/wiki/Hash_table

From the official documentation:
This class implements the Set interface, backed by a hash table
(actually a HashMap instance). It makes no guarantees as to the
iteration order of the set; in particular, it does not guarantee that
the order will remain constant over time. [...] The iterators returned
by this class's iterator method are fail-fast: if the set is modified
at any time after the iterator is created

Here is ordered version:
import java.util.Set;
import java.util.Iterator;
import java.util.Collections;
import java.util.LinkedHashSet;
public class HS {
public static void main(String args[]) {
Set<String> h=Collections.synchronizedSet(new LinkedHashSet<String>());
h.add("Mayank");
h.add("Mayank");
h.add("Vashist");
h.add("Dinesh");
h.add("Vashist");
Iterator<String> itr=h.iterator();
while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}

Related

How to add element at specific index/position in HashSet?

I have some HashSet collection of String elements, how I can add new element at specific position? I saw documentation and didn't find any method that can do it.
One of the aspects of a standard HashSet<T> is that it is unordered. So you cannot insert an element at a specific index. Or as is specified in the documentation:
This class implements the Set interface, backed by a hash table (actually a HashMap instance). It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time. This class permits the null element.
When you do an insertion, deletion, etc. it is possible that the HashSet<T> will do a rehashing. As a result the order of the elements in a for(...) loop can change completely.
There exists an extension of a Hashset<T>, the LinkedHashSet<T> which maintains the order of the elements in which they were inserted.
A TreeSet<T> on the other hand, uses a tree, and orders the elements according to an order relation (an object that is less than another object, is emitted before that element).
It would be weird if you could insert an element at a random index, since that would result in O(n) time for at least some operations with a specific index. Usually one uses a HashSet<T> to perform insertions, removal, etc. in O(1) (average time).
we can see pic use list to add at particular index and then add that list to set using object.addAll(listobject or setobject
Set<Integer> s=new HashSet<Integer>();
Set<Integer> s1=new HashSet<Integer>();
s1.add(6);
List<Integer> l=new ArrayList<Integer>();
l.add(5);
l.add(2,2);
l.add(0);
System.out.println(l);
S.addAll(l);//list
S.addAll(s1);//another set

Create a List of unique values in java

I have data of which the sequence is as important as its unique elements. Meaning if something has already been added it should not be added again and the sequence must be remembered.
Set does not remember the sequence in which it was added (either hash or sort), and List is not unique.
What is the best solution to this problem?
Should one have a list and loop through it to test for uniqueness - which I'm trying to avoid?
Or should one have two collections, one a List and one a Set - which I'm also trying to avoid?
Or is there a different solution to this problem altogether.
In the bellow code was your reference
LinkedHashSet<String> al=new LinkedHashSet<String>();
al.add("guru");
al.add("karthik");
al.add("raja");
al.add("karthik");
Iterator<String> itr=al.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
output
guru
karthik
raja
Use LinkedHashSet. It serves as both a List and a Set. It has the uniqueness quality of a set but still remembers the order in which you inserted items to it which allows you to iterate it by order of insertion.
From the Docs:
Hash table and linked list implementation of the Set interface, with predictable iteration order. This implementation differs from HashSet in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is the order in which elements were inserted into the set (insertion-order). Note that insertion order is not affected if an element is re-inserted into the set. (An element e is reinserted into a set s if s.add(e) is invoked when s.contains(e) would return true immediately prior to the invocation.)
You can use SortedSet
or LinkedHashSet
LinkedHashSet is the best possible way out

I know HashSet Internally work as HashMap and HashMap internally

I know HashSet internally work as HashMap and HashMap internally use LinkedList as FIFO etc. So my Question is when I insert values in StudentRecord class object in pattern like..
StudentsRecord record=new StudentsRecord(1, "Pramod", "UNA");
StudentsRecord record2=new StudentsRecord(2, "Pankaj","Lucknow");
StudentsRecord record3=new StudentsRecord(3, "Santosh","Chennai");
HashSet<StudentsRecord> set=new HashSet<StudentsRecord>();
set.add(record);
set.add(record2);
set.add(record3);
Iterator<StudentsRecord> iterator=set.iterator();
while(iterator.hasNext())
{
StudentsRecord result=(StudentsRecord)iterator.next();
System.out.println(result.getId()+","+result.getName()+","+result.getAddress());
}
After this why my result does not follow FIFO or LIFO order pattern?
My Output is:
3,Santosh,Chennai
1,Pramod,UNA
2,Pankaj,Lucknow
Use LinkedHashSet instead of HashSet. because LinkedHashSet maintains insertion order.
public static void main(String[] args) {
StudentsRecord record=new StudentsRecord(1, "Pramod", "UNA");
StudentsRecord record2=new StudentsRecord(2, "Pankaj","Lucknow");
StudentsRecord record3=new StudentsRecord(3, "Santosh","Chennai");
Set<StudentsRecord> set=new LinkedHashSet<StudentsRecord>();
set.add(record);
set.add(record2);
set.add(record3);
Iterator<StudentsRecord> iterator=set.iterator();
while(iterator.hasNext())
{
StudentsRecord result=(StudentsRecord)iterator.next();
System.out.println(result.getId()+","+result.getName()+","+result.getAddress());
}
}
LinkedHashMap can be used for this purpose.
It is same as HashMap, except that when you iterate over it, it presents the items in the insertion order.
Basically it preserves the insertion order.
from JavaDoc
This class makes no guarantees as to the order of the map; in
particular, it does not guarantee that the order will remain constant
over time.
LinkedHashSet will preserve insertion order for you.
Since none of the other answers actually answered "why":
HashMap stores objects into buckets, and then only uses the lists in the bucket. This is how it is able to find objects fast, as it can go straight to the correct bucket and then only needs to scan the entries inside that bucket.
In this example 3. has been added into an earlier bucket than 2 or 3 and as a result comes back in a different order.
HashMap and HashSet should never be relied upon to provide any particular ordering. You can use LinkedHashMap to retain insertion order or TreeMap to sort the entries.

Why set <Integer> is sorting added values?

When I start to add value into Set<Integer> I get sorting elements.
Please refer to this example:
Set<Integer> generated = new HashSet<Integer>();
generated.add(2);
generated.add(1);
generated.add(0);
Here I get sorting Set [0, 1, 2]. I would like to get value as I add to generated object.
A HashSet does not have a predictable order for elements. Use a LinkedHashSet to preserve insertion order of elements in a set:
Hash table and linked list implementation of the Set interface, with predictable iteration order.
Set<Integer> generated = new LinkedHashSet<Integer>();
generated.add(2);
generated.add(1);
generated.add(0);
Firstly it's just a co-incidence that you get sorted value first time. If you run that code multiple time, you'll see the output in some random order. That's because a HashSet doesn't enforce any ordering on elements you add.
Now to get the elements in the order you inserted, you can use LinkedHashSet, that maintains the insertion order.
The HashSet does not guarantee the order of the elements. From the JavaDoc:
It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.
So, in order to keep guarantee the order a LinkedHashSet can be used. From the JavaDoc:
Hash table and linked list implementation of the Set interface, with predictable iteration order.
This linked list defines the iteration ordering, which is the order in which elements were inserted into the set (insertion-order).
Simply instantiate your Set like this:
Set<Integer> generated = new LinkedHashSet<>();
First, regarding the title of your question, Set<Integer> is only the declaration type and its not responsible of any sorting / unsorting behavior, the main reason for using the Set interface is when caring about uniqueness — it doesn't allow duplicates, additional informations from Javadocs:
A Set is a Collection that cannot contain duplicate elements.
Second, it's pure concidence that you got sorted set, use HashSet when you don't care about order when iterating through it, more infos from javadocs:
It makes no guarantees as to the iteration order of the set; in
particular, it does not guarantee that the order will remain constant
over time. This class permits the null element.
Third, regarding what you are looking for:
I would like to get value as I add to generated object.
then you need to use LinkedHashSet which takes care of the order in which elements were inserted, again from javadocs:
This linked list defines the iteration ordering, which is the order in
which elements were inserted into the set (insertion-order). Note that
insertion order is not affected if an element is re-inserted into the
set
you may use it simply like this:
Set<Integer> generated = new LinkedHashSet<Integer>();
Fourth and Last, as additional information, another important collection that you need to be aware of it, is the TreeSetwhich guarantees that the elements will be sorted in ascending order, according to natural order, javadocs:
The elements are ordered using their natural ordering, or by a
Comparator provided at set creation time, depending on which
constructor is used

Unsorted HashSet in Java

Code.
Set<String> set = new HashSet<String>(3);
set.add("3 Lorem");
set.add("1 Lorem");
set.add("2 Lorem");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String type = (String) iterator.next();
System.out.println(type);
}
Output.
2 Lorem
3 Lorem
1 Lorem
This order looks strange to me. I add 3 Lorem, 1 Lorem, and then 2 Lorem. Why are they in a different order in the output?
Hash sets are not supposed to be sorted.
Technically they are sorted, but by the hash code (or a hash reduction thereof); and on hash collisions they may overflow into other buckets.
If you want an ordered set, use TreeSet instead. It usually is a bit slower, but sorted. If you want to retain the insertion order, use a List such as ArrayList or LinkedList.
There also is a hybrid, called LinkedHashSet, which allows fast contains operations, but maintains insertion order. Note that it won't have duplicates though.
From the JavaDocs:
This class implements the Set interface, backed by a hash table
(actually a HashMap instance). It makes no guarantees as to the
iteration order of the set; in particular, it does not guarantee that
the order will remain constant over time. This class permits the null
element.
if you intended to keep order better use TreeSet (but complexity will be log(n)
also check this post
Hashset vs Treeset
EDIT as pointed out by #Petar in order to maintain insertion order better use LinkedHashSet
and this Dzone Article demonstrates comparison between all three with nice suit of example and performance
Use a LinkedHashSet to maintain the insertion order.
Use TreeSet<String>(); or TreeSet<String>(String.CASE_INSENSITIVE_ORDER); if you want to Sort the elements. Use List instead of Set If you need to maintain the insertion order.

Categories

Resources