Well, you don't mind I will write my "test" project;
First of all I had created class that implements AbstractMap.
public class TestClass <K, V> extends AbstractMap <K, V>
TestClass has the private LinkedList that take as a parameter (It is another class that implements Map.Entry:
private int size = 1000;
private LinkedList <InfoClass <K, V>> [] array = new LinkedList [size];
After, I had created the method that checks and replaces duplicates:
public V put (K key, V value){ // Void doesn't work, therefore we need to return any value;
V temp = null;
boolean found = false;
int index = Math.abs(key.hashCode()) % size;
if (array[index] == null)
array[index] = new LinkedList <InfoClass <K, V>> (); // If the specified member of array is null, create new member;
LinkedList <InfoClass <K, V>> list = array[index];
InfoClass <K, V> info = new InfoClass <K, V> (key, value);
ListIterator <InfoClass <K, V>> it = list.listIterator();
while (it.hasNext()){
InfoClass<K, V> temp2 = it.next(); // Create a temp instance;
if (temp2.getKey().equals(key)){ // If there is a duplicate of value, just replace by new one;
found = true;
temp = temp2.getValue();
it.set(info);
break;
}
}
if (!found) // if there is not a duplicate, add new one;
array[index].add(info);
return temp;
}
Next method returns a value, otherwise this returns null if a member of array doesn't exist:
public V get (Object key){ // The parameter K doesn't work;
int index = Math.abs(key.hashCode()) % size;
if (array[index] == null)
return null;
else
for (InfoClass <K, V> info : array[index])
if (info.getKey().equals(key))
return info.getValue();
return null;
}
This method forms a set of AbstractMap:
public Set<java.util.Map.Entry<K, V>> entrySet() {
Set <Map.Entry<K, V>> sets = new HashSet <Map.Entry<K, V>> ();
for (LinkedList <InfoClass<K, V>> temp1 : array){
if (temp1 == null)
continue;
else{
for (InfoClass<K,V> temp2 : temp1)
sets.add(temp2);
}
}
return sets;
}
Ok, create new object in main method:
public static void main (String [] args){
TestClass <Integer, String> TC = new TestClass <Integer, String> ();
TC.putAll(CollectionDataMap.newCollection(new Group.Ints(), new Group.Name(), 10));
System.out.println(TC);
System.out.println(TC.get(1));
TC.put(1, "Hello this world");
System.out.println(TC);
}
Hope I explained correctly. I have a question, what is difference between LinkedList and LinkedHashMap (HashMap) if they work the same way?
Thank you very much!
LinkedList can contain the same element multiple times if the same element is added multiple times.
HashSet can only contain the same object once even if you add it multiple times, but it does not retain insertion order in the set.
LinkedHashSet can only contain the same object once even if you add it multiple times, but it also retains insertion order.
HashMap maps a value to a key, and the keys are stored in a set (so it can be in the set only once). HashMap doesn't preserve insertion order for the key set, while LinkedHashMap does retain insertion order for the keys.
Related
I have a array of String which has few words and I'm trying to find the most frequent (number of occurrences) words of k from the given array. I also have a constraint that consider the most frequent words are of frequency 2 then I need to consider the one which comes first in alphabetical order then the next ones.
I created a TreeMap to hold the words vs it's frequency count. Now the keys (ie' words) in the map would be sorted alphabetically. Now I was trying to sort the map based on it's values with the following snippet.
public static <K, V extends Comparable<V>> Map<K, V>
sortByValues(final Map<K, V> map) {
Comparator<K> valueComparator =
new Comparator<K>() {
public int compare(K k1, K k2) {
int compare =
map.get(k2).compareTo(map.get(k1));
if (compare == 0)
return 1;
else
return compare;
}
};
Map<K, V> sortedByValues =
new TreeMap<K, V>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
}
Now I'm iterating the map in the following ways,
Method1
Map sortedMap = sortByValues(map);
ArrayList<String> result = new ArrayList<String>();
for (Map.Entry<String, Integer> entry : sortedMap.entrySet()) {
if (k-- <= 0) {
return result;
}
result.add(entry.getKey());
}
Method2
Map sortedMap = sortByValues(map);
ArrayList<String> result = new ArrayList<String>();
// Get a set of the entries on the sorted map
Set set = sortedMap.entrySet();
// Get an iterator
Iterator i = set.iterator();
// Display elements
while(i.hasNext()) {
if (k-- <= 0) {
return result;
}
Map.Entry me = (Map.Entry)i.next();
result.add((String) me.getKey());
}
Consider the following content in TreeMap initially,
// Put elements to the map
treemap.put("love", 2);
treemap.put("i", 2);
treemap.put("coding", 1);
Method1 returns ["coding","i"] as output whereas Method2 way of iterating gives me ["i","love"].
Doubts
I'm confused with the two ways of iterating the map?
If I sort the TreeMap which is already sorted based on keys, if i sort again based by values will the sorting by keys be retained for equal value elements?
I used LinkedHashMap<String, Double> . I want to take separate values from it. If it is Array we can use .get[2] ,.get[5] etc. for take 2nd and 5th value. But for LinkedHashMap<String, Double> how to do it. I used following code. But it print all the values contained in LinkedHashMap<String, Double>. I need to take separately.
Set set = mylist.entrySet();
Iterator i = set.iterator();
while(i.hasNext()) {
Map.Entry me1 = (Map.Entry)i.next();
System.out.print(me1.getKey());
System.out.println(me1.getValue());
You may use the LinkedHashMap#get(Object key) method
Which will return the value corresponding to the key parameter. Since your keys are String, you can not use an int to retrieve them.
Example
If your LinkedHashMap contains ["key", 2.5], calling
System.out.println(lnkHashMap.get("key"));
will print
2.5
Addition
If you're using java-8, there is a workaround using a Stream object.
Double result = hashmap.values()
.stream()
.skip(2)
.findFirst()
.get();
This will skip the two first values and get to the third one directly and return it.
If not, here is a solution
public <T> T getValueByIndex (Map<? extends Object, T> map, int index){
Iterator<T> it = map.values().iterator();
T temp = null;
for (int i = 0 ; i < index ; i++){
if (it.hasNext()){
temp = it.next();
} else {
throw new IndexOutOfBoundsException();
}
}
return temp;
}
It could be the case that you are using the wrong data structure for your purpose.
If you look closely to the LinkedHashMap API you will notice that it is indeed a Map and the only way to access a previously stored value is by providing its key.
But if you really think you need to access the ith value of the LinkedHashMap according to its insertion-order (or access-order) you can use a simple utility method like the following:
Java 8 Solution
private static <K, V> Optional<V> getByInsertionOrder(LinkedHashMap<K, V> linkedHashMap, int index) {
return linkedHashMap.values().stream()
.skip(index)
.findFirst();
}
Java 7 Soution
private static <K, V> V getByInsertionOrder(LinkedHashMap<K, V> linkedHashMap, int index) {
if (index < 0 || index >= linkedHashMap.size()) {
throw new IndexOutOfBoundsException();
}
Iterator<Entry<K, V>> iterator = linkedHashMap.entrySet().iterator();
for (int i = 0; i < index; i++) {
iterator.next();
}
return iterator.next().getValue();
}
I searched earlier for a way to sort TreeMaps according to the values, was able to find a function that does that, but now it seems something's wrong with the remove() method, tried various different ways to remove using a key but its not working, and to tell the truth I don't fully understand the sorting function, can someone explain what is the problem?
The Code:
public static void main(String[] args) {
TreeMap<String,Integer> theTree = new TreeMap<String,Integer>();
theTree.put("A", 5);
theTree.put("B", 3);
theTree.put("F", 1);
theTree.put("D", 5);
theTree.put("E", 6);
theTree.put("C", 7);
System.out.println(theTree.toString());
Map<String,Integer> sortedTree = new TreeMap<String,Integer>();
sortedTree = sortByValues(theTree);
System.out.println(sortedTree.toString());
for (Map.Entry<String, Integer> entry : sortedTree.entrySet())
{
if(entry.getKey().equals("A"))
sortedTree.remove(entry.getKey());
}
System.out.println(sortedTree.toString());
theTree = new TreeMap<String,Integer>(sortedTree);
theTree.remove("B");
System.out.println(theTree.toString());
}
public static <K, V extends Comparable<V>> Map<K, V> sortByValues(final Map<K, V> map) {
Comparator<K> valueComparator = new Comparator<K>() {
public int compare(K k1, K k2) {
int compare = map.get(k2).compareTo(map.get(k1));
if (compare == 0) return 1;
else return compare;
}
};
Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
}
The Output:
{A=5, B=3, C=7, D=5, E=6, F=1}
{C=7, E=6, A=5, D=5, B=3, F=1}
{C=7, E=6, A=5, D=5, B=3, F=1}
{A=5, C=7, D=5, E=6, F=1}
The A is still in the map
In your Comparator, you are removing any chance of it returning 0 (if (compare == 0)return 1) Thus when you go to remove, there is never any chance for your inputted value to equal a value in the Set and nothing will be removed. If there is no particular reason why you are removing the chance for them to be deemed equal, just remove that line from your comparator.
You can delete only via Iterator during map traversal:
Map<String,Integer> sortedTree = new TreeMap<String,Integer>();
System.out.println(sortedTree.toString());
Iterator<Entry<String,Integer>> itr = sortedTree.entrySet().iterator();
while(itr.hasNext())
{
Entry<String,Integer> entry = itr.next();
if(entry.getKey().equals("A"))
itr.remove();
}
map.remove would work fine while not under iteration else removal would throw ConcurrentModificationException if iteration is not happening via Iterator. That's the case with many other collections too.
This is what I meant as per TreeMap javadoc:
The iterators returned by the iterator method of the collections
returned by all of this class's "collection view methods" are
fail-fast: if the map is structurally modified at any time after the
iterator is created, in any way except through the iterator's own
remove method, the iterator will throw a
ConcurrentModificationException.
I'm having an issue sorting the values and keys from a map from least to greatest(integers and String's).
Here are the two methods, first the method for values:
public Collection<V> values(){
Collection<V> coll = new LinkedList<V>();
for(int i = 0; i < table.length; i++){
if(table[i] != null){
for(Entry<K, V> nextItem : table[i]){
if(nextItem.value != null){
if(!coll.contains(nextItem.value)){
coll.add(nextItem.value);
}
}
}
}
}
return coll;
}
Expected Output:
120 123 404 911 999
My Output (basically keeps order of wherever it was in the map):
911 999 123 120 404
The above method is used with dot notation for a hashTableChain (an array who's keys are sorted by their hashCode where the indices of the array are linkedLists), it returns the values for the given map. I tried sorting it with Collections.sort(coll) however this requires a List which is incompatible with Collection to my knowledge. Is there something compatible with Collection that is either already sorted or can be sorted in an easy way?
The method for the key's:
public Set<K> keySet(){
Set<K> coll = new HashSet<K>();
for(int i = 0; i < table.length; i++){
if(table[i] != null){
for(Entry<K, V> nextItem : table[i]){
coll.add(nextItem.key);
}
}
}
return coll;
}
Expected Output:
ABC ACTG HTML LOL OMG XYZ
My Output (basically keeps order of wherever it was in the map):
XYZ ABC ACTG HTML LOL OMG
Again I tried Collections.sort(coll) to no avail and couldn't find any way to sort it.
I'm fairly new to java and I'm sure I'm overlooking something, after searching the web for a while I figured I'd just ask.
Thanks in advance and I very much appreciate the help.
Added at request:
private static class Entry<K, V> implements Map.Entry<K, V> {
/** The key */
private K key;
/** The value */
private V value;
/**
* Creates a new key-value pair.
* #param key The key
* #param value The value
*/
public Entry(K key, V value) {
this.key = key;
this.value = value;
}
About first example you can use a List<V> for variable coll:
public Collection<V> values(){
List<V> coll = new LinkedList<V>();
// do stuff
Collections.sort(coll);
return coll;
}
and is correct, because LinkedList implements List. You have to choice the supertype compatible with your needs; if you need to return a sorted list, you can use a List in your function, sort them and return the list as Collection.
In second example the same, but use a
Set<K> coll = new TreeSet<K>();
TreeSet implements SortedSet, A Set that further provides a total ordering on its elements.
What you call a Collection happens to be a List in 100% of the cases. You just need to tell the compiler this by either:
Casting (List<V>)coll before calling sort, or
Changing the declaration List<V> coll= new LinkedList<V>() ;
If you have 123 as a value two times, I think it's correct it appears two times in values()'s result.
In the second case, I'd recommend to use a SortedSet instead of a HashSet.
Please consider the following code:
public class Sample<K extends Comparable<K>,V extends Comparable<V>> {
public static class Entry<A,B> implements Map.Entry<A,B> {
A key;
B value;
public Entry(A key,B value) {
this.key= key ;
this.value= value ;
}
public A getKey() {
return this.key ;
}
public B getValue() {
return this.value ;
}
public B setValue(B value) {
return this.value= value ;
}
}
LinkedList<Entry<K,V>>[] table;
public Collection<V> values(){
List<V> coll= new LinkedList<V>() ;
for(LinkedList<Entry<K, V>> e: table ) {
if( e != null ) {
for(Entry<K, V> nextItem : e ) {
if( nextItem.value != null ) {
coll.add(nextItem.value);
}
}
}
}
Collections.sort(coll);
return coll;
}
public Set<K> keySet(){
Set<K> coll= new TreeSet<K>() ;
for(LinkedList<Entry<K, V>> e: table ) {
if( e != null ) {
for(Entry<K, V> nextItem : e ) {
coll.add(nextItem.key);
}
}
}
return coll;
}
public static void main(String... args) {
Sample<String,Integer> test= new Sample<String,Integer>();
test.table= (LinkedList<Entry<String,Integer>>[])new LinkedList[1024] ;
test.table[467]= new LinkedList<Entry<String,Integer>>() ;
test.table[467].add( new Entry("XYZ",999) );
test.table[467].add( new Entry("ABC",123) );
test.table[678]= new LinkedList<Entry<String,Integer>>() ;
test.table[678].add( new Entry("ACTG",404) );
test.table[678].add( new Entry("HTML",120) );
test.table[678].add( new Entry("ACTG",404) );
test.table[678].add( new Entry("LOL",123) );
test.table[ 2]= new LinkedList<Entry<String,Integer>>() ;
test.table[ 2].add( new Entry("OMG",911) );
System.out.println( test.values() );
System.out.println( test.keySet() );
}
}
If you expect to create a generic class in which the values of the parametric types are going to be sorted, then they need to implement interface Comparable. And if they need to implement interface Comparable, then you need to say so when declaring your class. That's why the K extends Comparable<K> and V extends Comparable<V> while declaring Sample. This is particularly important when you call Collections.sort or you instantiate TreeSet. Both require that the parameters/parametric types are Comparables (or subtypes of).
The result of the test case I added are correct:
[120, 123, 123, 404, 404, 911, 999]
[ABC, ACTG, HTML, LOL, OMG, XYZ]
respectively.
hello
I need to implement a method that receives a HashMap and sorts (mergeSort) it's values by key (without using TreeMap, SortedMap or Collections.Sort or use any sort solutions from JAVA Packages).
my problem is dealing with the wildcard Types...
this is my implementation (that returns compilation errors because of wildcards use)
public HashMap<?, ?> mergeSort(HashMap<?, ?> map) {
if (map.size() < 1) {
return map;
}
// rounds downwards
int middle = map.size() / 2;
int location = 0;
HashMap<?,?> mapLeft = new HashMap<?, ?>();
HashMap<?,?> mapRight = new HashMap<?, ?>();
// splitting map
for (Iterator<?> keyIter = map.keySet().iterator(); keyIter.hasNext();) {
if (location < middle) {
mapLeft.put(keyIter, map.get(keyIter));
} else {
mapRight.put(keyIter, map.get(keyIter));
}
location++;
}
// recursive call
mapLeft = mergeSort(mapLeft);
mapRight = mergeSort(mapRight);
return merge(mapLeft, mapRight);
}
public HashMap<?, ?> merge(HashMap<?, ?> mapLeft, HashMap<?, ?> mapRight) {
HashMap<?, ?> result = new HashMap<?, ?>();
Iterator<?> keyLeftIter = mapLeft.keySet().iterator();
Iterator<?> keyRightIter = mapRight.keySet().iterator();
String keyLeft;
String keyRight;
while (keyLeftIter.hasNext()) {
keyLeft = keyLeftIter.next();
while (keyRightIter.hasNext()) {
keyRight = keyRightIter.next();
if (keyLeft.compareTo(keyRight) < 0) {
result.put(keyLeft, mapLeft.get(keyLeft));
keyLeft = keyLeftIter.next();
} else {
result.put(keyRight, mapRight.get(keyRight));
keyRight = keyRightIter.next();
}
}
}
return result;
}
I appreciate your help!
If all you have to do is meet a method contract, you can do this.
public HashMap<?, ?> mergeSort(HashMap<?, ?> map) {
return new LinkedHashMap(new TreeMap(map));
}
This will sort the keys and return a subclass of HashMap. The design of this method is broken, but sometimes you can't change things.
If you are sorting a map, you should be using a SortedMap like TreeMap. hashmap doesn't retain an order so using it for a merge sort is not possible. Using a merge sort for a TreeMap is redundant.
You cannot assume that ? is a Comparable. You can write something like.
public static <K extends Comparable<K>, V> SortedMap<K,V> sort(Map<K,V> map) {
return new TreeMap<K, V>(map);
}
As you can see this is shorter and simpler than your approach. Is this homework? Do you reall need to use a merge sort?
The problem you have is that you cannot return a HashMap as it doesn't keep the order, adn you cannot return a TreeMap because it will sort the keys for you making anything else you redundant. For this task you can only return a LinkedHashMap as it does retain order, without doing the sorting for you.
here is an example using LinkedHashMap. Note it doesn't create copies of Maps as it goes, it creates a single array and merge sorts portions of it until its completely sorted.
Note: I use TreeMap as a SortedMap to show its sorted correctly. ;)
public static void main(String... args) throws IOException {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i=0;i<100;i++)
map.put((int)(Math.random()*1000), i);
System.out.println("Unsorted "+map);
System.out.println("Sorted "+sort(map));
final String sortedToString = sort(map).toString();
final String treeMapToString = new TreeMap<Integer, Integer>(map).toString();
if (!sortedToString.equals(treeMapToString))
System.out.println(sortedToString+" != \n"+treeMapToString);
}
public static <K extends Comparable<K>, V> Map<K, V> sort(Map<K, V> map) {
return mergeSort(map);
}
// a very bad design idea, but needed for compatibility.
public static <K extends Comparable<K>, V> HashMap<K, V> mergeSort(Map<K, V> map) {
Map.Entry<K, V>[] entries = map.entrySet().toArray(new Map.Entry[map.size()]);
mergeSort0(entries, 0, entries.length);
HashMap<K, V> ret = new LinkedHashMap<K, V>();
for (Map.Entry<K, V> entry : entries)
ret.put(entry.getKey(), entry.getValue());
return ret;
}
private static <K extends Comparable<K>, V> void mergeSort0(Map.Entry<K, V>[] entries, int start, int end) {
int len = end - start;
if (len < 2) return;
int mid = (end + start) >>> 1;
mergeSort0(entries, start, mid);
mergeSort0(entries, mid, end);
// merge [start, mid) and [mid, end) to [start, end)
for(int p = start, l=start, r=mid; p < end && l < r && r < end; p++) {
int cmp = entries[l].getKey().compareTo(entries[r].getKey());
if (cmp <= 0) {
l++;
// the entry is in the right place already
} else if (p != r) {
// we need to insert the entry from the right
Map.Entry<K,V> e= entries[r];
// shift up.
System.arraycopy(entries, p, entries, p+1, r - p);
l++;
// move down.
entries[p] = e;
r++;
}
}
}
prints
Unsorted {687=13, 551=0, 2=15, 984=3, 608=6, 714=16, 744=1, 272=5, 854=9, 96=2, 918=18, 829=8, 109=14, 346=7, 522=4, 626=19, 495=12, 695=17, 247=11, 725=10}
Sorted {2=15, 96=2, 109=14, 247=11, 272=5, 346=7, 495=12, 522=4, 551=0, 608=6, 626=19, 687=13, 695=17, 714=16, 725=10, 744=1, 829=8, 854=9, 918=18, 984=3}
Like other commenters I would suggest reading up on the subject of generics in Java. What you did in merge is using wildcards on the result HashMap
HashMap<?, ?> result = new HashMap<?, ?>();
When you put wildcards on it, you are basically saying "I will only be reading from this". Later on you trying to push something in
result.put(keyLeft, mapLeft.get(keyLeft));
The compiler will say "Hey, you just told me you would only read and now you want to put something in... FAIL
Then it generates your compile time errors.
Solution
Don't put wildcards on collections you will modify.
Why are you always using ?. Give the kids a name like Key or Value.
Edit: You should work through this tutorial fist: Lesson: Generics
Here is a method that sorts a Map by its keys. It makes use of the Collections.sort(List, Comparator) method.
static Map sortByKey(Map map) {
List list = new LinkedList(map.entrySet());
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Comparable) ((Map.Entry) (o1)).getKey())
.compareTo(((Map.Entry) (o2)).getKey());
}
});
Map result = new LinkedHashMap();
for (Iterator it = list.iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry)it.next();
result.put(entry.getKey(), entry.getValue());
}
return result;
}