How to replace a specific element in a LinkedHashSet? - java

With an ArrayList I would do something like
myList.set(myList.indexOf(oldItemNumber), newItemNumber);
Is there an easy way to achieve this for LinkedHashSet?

Just use the put() method. It will update that specific entry.

From the documentation of LinkedHashMap:
Hash table and linked list implementation of the Map interface, with
predictable iteration order. This implementation differs from HashMap
in that it maintains a doubly-linked list running through all of its
entries. This linked list defines the iteration ordering, which is
normally the order in which keys were inserted into the map
(insertion-order). Note that insertion order is not affected if a key
is re-inserted into the map. (A key k is reinserted into a map m if
m.put(k, v) is invoked when m.containsKey(k) would return true
immediately prior to the invocation.)
So you can simply put() the new value in and it will keep it's current location in the ordering. If you want to add it to the end then remove the old value and then add the new one.

The question is meaningless for a Set. A new object added to the Set either equals an existing one (in which case nothing happens) or it doesn't (in which case it is added at the end).
There is no way to insert objects at specific locations without removing and re-adding everything.
You may want to look at TreeSet which allows you to keep an ordered set that will remain ordered by the provided comparator as you add and remove objects.

I solved this problem where a LinkedHashSet member object has a hash code which computes a value on only part of the object (ex its Name field). Yes this is unconventional but I also had a matching equals() method. Two different objects can have the same hash code and appear equal but internally they have some different values.
An example would be a database record which has key which has to be unique. You need to replace the record with a new set of values, so you find and replace the record with the matching key with new values.
// Rebuild Linked Hash Set in original order with new element.
ArrayList<Item> newSet = new ArrayList<>(oldSet);
newSet.add(newSet.indexOf(oldItem), newItem);
oldSet.clear();
oldSet.addAll(newSet);
The arrayList will have the old values in the same order and the new item will get added in front of the old. No need to remove the duplicate. When added to the old HashSet (LinkedHashSet) it only keeps the first occurrence of any duplicate hash codes.

use map.put(key ,value) if you will use same key it will replace previous value

Related

Efficient way to modify a list of objects using maps

I have a list of objects, these objects can shift position in the list or even be removed, this object that I am saving, each of them have an unique id, lets call this id "key". To quickly check membership in the list I have an additional map which is mapped as
<object.getKey(), object>
Currently I get a call to my method with this key, and I do a
map.contains(key)
// For delete
list.remove(map.get(key))
// For edit
Object oldObj = list.get(map.get(key))
// Modify the old obj, they are immutable so I need to set
// old position with new object
list.set(map.get(key), newObject)
For sure this code is shit, but I am not able to wrap my head around a clean solution for this, I want to avoid any O(n) searches on the list which is clearly happening in the case of the list.remove and list.get!
One of the solutions I could think of was to map the key to the list position but then in case the map has a delete operation or an insert operation I'd have to update the whole map from the position of change! Again O(n). Any suggestions on the most efficient way to do this?
You haven't specified what exactly you need to do with the list, but assuming you're looking for a collection with a predictable order, consider using a LinkedHashSet - it retains the order of insertion while stile allowing operations like add, remove or contains in O(1).

Is order of extracted values from same hash map same?

Does hashMapObj.values() return the list in the same order when called nth time as it returned the 1st time?
I understand that the order of values will be different than the insertion order. But will the order differ even when multiple calls are made to values() with no alteration to the hashMap ?
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
http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html#values()
You can't wait that values() will return the list in the same order, it could happen but is just not a good idea because it depends on the implementation
A HashMap makes no guarantee about the order its values are iterated when iterating a Collection through .values(). Only that they are in sync:
Returns a Collection view of the values contained in this map. The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa. If the map is modified while an iteration over the collection is in progress (except through the iterator's own remove operation), the results of the iteration are undefined. The collection supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Collection.remove, removeAll, retainAll and clear operations. It does not support the add or addAll operations.
docs
If your elements are comparable, and the speed penalty is acceptable. You can always sort the values into another collection and then obtain deterministic order.
The other answer talks about the order of the keys which is irrelevant here.
The answer depends on what happens in between of the two calls to values():
If no modifications are made to the HashMap<K,V>, the order would be the same, because the algorithm that is used to traverse the buckets of the hash map does not have a random component. The order is arbitrary with respect to the items inserted into the map, but it remains the same between iterations.
If you make modifications to the map, the order may change, even if the content of the map remains the same (e.g. because you remove and re-insert an item, or insert and then remove an item).
Deleting an re-inserting an item can change the order of items in the same hash bucket, so the order of iteration of values() will change accordingly.
Inserting an item may grow the number of cache buckets and re-hashing, which will not be undone when the same item is removed. That will change the order of values() as well.
No it is not guaranteed, you need a TreeMap :
https://docs.oracle.com/javase/8/docs/api/java/util/TreeMap.html

LinkedHashMap Implementation in Java

I cannot understand the use of HashFunction in LinkedHashMap.
In the HashMap implementation, the use of hashFunction is to find the index of the internal array, which can be justified, following the hashfunction contract (same key will must have same hashcode, but distinct key can have same hashcode).
My questions are:
1) What is the use of hashfunction in LinkedHashMap?
2) How does the put and get method works for LinkedHashMap?
3) Why does it maintains the doublylinkedlist internally?
Whats wrong in using the HashMap as internal implementation(just like HashSet) and maintain a separate Array/List of indexes of the Entry array in the sequence of insertion?
Appreciate useful response and references.
1) LinkedHashMap extends HashMap so the hashfunction is the same of HashMap (if you check the code the hash function is inherited from HashMap), i.e. the function computes a the hash of the object inserted and it use to store in a data structure together with the elements with the same key hash; the hasfunction is used in the get method to retrieve the object with the key specified as a param.
2)Put and Get method are behave the same way as HashMap plus the track the insertion order of the elements so when you iterate over the the keyset you get the key values in the order you inserted into the map (see here for more details)
3)the LinkedHashMap uses a double linked list instead of an Array because it's more compact; a double linked list is the the most efficient data structure for list where you insert and remove items; if you mostly insert/append elements then an array based implementation may be better. Since the map sematic is a key-value implementation and removing elements from the map could be a frequent operation a double linked list is a better fit. The internal implmentation could be made with a LinkedList but my opionion is that using a low level data stucture is more efficient and decouples LinkedHashMap from other classes.
A LinkedHashMap does use a HashMap (in fact it extends from it), so the hashCode is used to identify the right hash bucket in the array of hash buckets, just as for HashMap. put and get work just as for HashMap (except that the before and after references for iterating over the entries are updated differently for the two implementations).
The reason insertion order is not kept by keeping an Array or ArrayList is that addition or removal in the middle of an ArrayList is an O(n) operation because you have to move all subsequent items along one place. You could do this with a LinkedList because addition and removal in the middle of a LinkedList is O(1) (all you have to do is break a few links and make a few new ones). However there's no point using a separate LinkedList because you may as well make the Map.Entry objects reference the previous and next Entry objects, which is exactly how LinkedHashMap works.
LinkedHashMap is a good choice for a data structure where you want to be able to put and get entries with O(1) running time, but you also need the behavior of a LinkedList. The internal hashing function is what allows you put and get entries with constant-time.
Here is how you use LinkedHashMap:
Map<String, Double> linkedHashMap = new LinkedHashMap<String, String>();
linkedHashMap.put("today", "Wednesday");
linkedHashMap.put("tomorrow", "Thursday");
String today = linkedHashMap.get("today"); // today is 'Wednesday'
There are several arguments against using a simple HashMap and maintaining a separate List for the insertion order. First, if you go this route it means you will have to maintain 2 data structures instead of one. This is error prone, and makes maintaining your code more difficult. Second, if you have to make your data structure Thread-safe, this would be complex for 2 data structures. On the other hand, if you use a LinkedHashMap you only would have to worry about making this single data structure thread-safe.
As for implementation details, when you do a put into a LinkedHashMap, the JVM will take your key and use a cryptographic mapping function to ultimately convert that key into a memory address where your value will be stored. Doing a get with a given key will also use this mapping function to find the exact location in memory where the value be stored. The entrySet() method returns a Set consisting of all the keys and values in the LinkedHashMap. By definition, sets are not ordered. The entrySet() is not guaranteed to be Thread-safe.
Ans. 2)
when we call put(map,key) of linkedhashmap. Internally it calls createEntry
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
Ans 3)
To efficiently maintain a linkedHashmap, you actually need a doubly linked list.
Consider three entries in order
A ---> B ---> C
Suppose you want to remove B. Obviously A should now point to C. But unless you know the entry before B you cannot efficiently say which entry should now point to C. To fix this, you need entries to point in both the directions Like this
---> --->
A B C
<--- <---
This way, when you remove B you can look at the entries before and after B (A and C) and update so that A and C point to each other.
similar post in this link discussed earlier
why linkedhashmap maintains doubly linked list for iteration

Passing List to Set Constructor, does filtering happen in order?

I am retrieving a List from a JPA query, ordered by effectiveDate. There could be duplicate entries except for the date column, and I'll be ordering them most recent date first (desc). All I want in the set are all the entries with the newest effectiveDate; dupes with older effectiveDates are not allowed in the Set.
If I create a HashSet by passing this List into the constructor, does the new Set only contain the first entries in the List, only the ones with the "newest" effectiveDates?
In other words, are Sets initialized in List order when they are created from a List?
Thanks!
If you create a HashSet from a collection with duplicates, only the first of each duplicate will be added to the hashset.
It adds the items in the list in order, and the add method does not overwrite duplicates.
Although this behavior is not specified, it's highly unlikely to change.
By "duplicate entries", do you mean that there are distinct rows/object instances returned by your query, and that you have defined equals/hashCode in a way such that they do not include this effectiveDate field? In other words, if you say that list.get(i) and list.get(j) are duplicates except for the date, you mean that list.get(i) != list.get(j) and list.get(i).equals(list.get(j)).
If that's what you meant, then I believe the first one to get inserted into the set wins.
SLaks has already beaten me to the punch about HashSet, but if you have the option to use LinkedHashSet instead, that may be better.
My reading of the javadoc for LinkedHashSet indicates that the first duplicate is guaranteed to be preserved:
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.)
In terms of java Collection Framework,
If you want to preserve the order only during insertion HashSet will do and keeps first occurrences there by removing any duplicates. However if you want to preserve order during iterations as well use LinkedHashSet
In case of JPA
you may find select distinct query useful to filter duplicates, there by avoiding redundant Collection processing
select distinct a from ....
see How do you create a Distinct query in HQL

Maintaining order of items in java

What is the best way to do the following(make sure that items from List are following the same order as those in ListTwo):
List
harry~20
marry~22
peter~40
jerry~33
janice~20
ListTwo
harry
marry
peter
janice
Now the result should look like this
ListThree
harry
marry
peter
janice
jerry
Step by step :
For each item in List :
compare first part of the item to item in ListTwo
if they are equal add it to ListThree
if item exist in List but not in ListTwo dont do anything yet save it
somewhere
continue from step 1
you are at the end of the List add the item(s) you skipped before in step
3
I know this much(actually I don't, I think I know), there are better ways to do this I'm sure
Why did I get downvote, did I miss something ?
It may be easier if you reverse the roles (store the keys in the ArrayList, in order) and the key-value mappings in a SortedMap, such as TreeMap, or ConcurrentSkipListMap. The comparator for the sorted map can use List.indexOf as the basis for element comparison.
With this arrangement, the map defines the key/value mapping, which is natural for the map, and the list maintains the desired order, which is quite natural for a List.
Alternatively, use a regular Map, and not a sorted map, and use iteration over the list, and fetching values from the map. E.g.
ArrayList keysList;
Map keyValues;
for(String key: keysList) {
String value = keyValues.get(key);
}
EDIT: Commons collections has SetUniqueList - a list that ensures uniqueness like a Set. It also has has various types of OrderedMap, in particular a ListOrderedMap that maintains the key/value mappings in the order of a list. For generics support, see commons collections with generics.
Use LinkedHashMap
You can call something like
map.put(one,value1);
and later call
map.get(one);
which will return value1
also a hash map does not accept duplicate key, so if you call
map.put(one,value2);
after this the original value is replaced.
you can use
map.containsKey(one)
to check whether one already exists as a key
If you are only comparing the keys of element then you can store them in LinkedHashSet and use the contains method of linkedHashset to check whether the element exists in constant time O(1).
LinkeHashMap also serves the purpose, however it requires extra space to store the value and this is not required we are only interested in keys.
Refer : http://docs.oracle.com/javase/6/docs/api/java/util/LinkedHashSet.html

Categories