I've a SortedMap which have items like:
1=abc
2=xyz
3=abc
Values can be duplicate.
I want to display the value-set on screen in the sorted manner. i.e.
myListMap
abc
abc
xyz
To sort the Map, I'm using comparator:
public class SortMapByValueComparator implements Comparator<String> {
Map<String, String> mInputMap;
/**
* Constructor.
*
* #param inputMap
*/
public SortMapByValueComparator(Map<String, String> inputMap) {
mInputMap = inputMap;
}
#Override
public int compare(String lhs, String rhs) {
String string1 = mInputMap.get(lhs);
String string2 = mInputMap.get(rhs);
return string1.compareToIgnoreCase(string2);
}
}
And then passing the Map to this comparator like:
SortMapByValueComparator sortMapByValueComparator = new SortMapByValueComparator(myListMap);
SortedMap<String, String> sortedMapByValue = new TreeMap<String, String>(sortMapByValueComparator);
sortedMapByValue.putAll(myListMap);
Now, issue is when I call SortMapByValueComparator, it removes duplicate values. How can I avoid it?
PS - I want to use Map only.
The problem is that compareToIgnoreCase() is returning 0 when two strings are equal, and returning zero is merging the keys as it is explained here. Implement your compare in a way that zero is not returned, it should work fine.
if(string1.compareToIgnoreCase(string2) >= 0)
return 1;
else
return -1;
I would change the whole approach and go with something simpler:
// initializing your Map
// not sure if key set is Integer or String but it doesn't really matter here
Map<Integer, String> tm = new TreeMap<Integer, String>();
tm.put(1, "abc");
tm.put(2, "xyz");
tm.put(3, "abc");
// getting the values as List (not Set, so duplicates allowed)
List<String>values = new ArrayList<String>(tm.values());
System.out.printf("Unsorted values: %s%n", values);
// sorting the values with natural (lexicographic) order...
Collections.sort(values);
System.out.printf("Sorted values: %s%n", values);
Output
Unsorted values: [abc, xyz, abc]
Sorted values: [abc, abc, xyz]
Related
I have a list of map objects in the following manner
List<Map<String, Object>> insurancePercentageDetails = dao.getinusrancePercentageDetails(age);
This gives me the output in the following way.
[{Age=42, Rate12=0.40, Rate24=0.63, Rate36=0.86, Rate48=1.12, Rate60=1.39, Rate72=1.67, Rate84=1.98, Rate96=2.31, Rate108=3.30, Rate120=3.84, Rate132=4.40, Rate144=5.00, Rate156=5.62, Rate168=6.28, Rate180=6.97, Rate192=7.34, Rate204=7.74, Rate216=8.15, Rate228=8.07, Rate240=8.33}]
My actual target is to have a map in the following sorted order
{12=0.4,24=0.63 ....}
For this I took a static list
private final static List<String> period = new ArrayList<>
(Arrays.asList("Rate12","Rate24","Rate36","Rate48","Rate60","Rate72","Rate84","Rate96","Rate108","Rate120",
"Rate132","Rate144","Rate156","Rate168","Rate180","Rate192","Rate204","Rate216","Rate228","Rate240"));
Then
TreeMap<String, Float> insuranceMatrixMap = new TreeMap<String, Float>();
for(String str : period) {
insuranceMatrixMap.put(str.replaceAll("Rate", ""), ((BigDecimal) (BBUtil.getInstance().getValue(insurancePercentageDetails, str))).floatValue());
}
This gives me the output
{108=3.3, 12=0.4, 120=3.84, 132=4.4, 144=5.0, 156=5.62, 168=6.28, 180=6.97, 192=7.34, 204=7.74, 216=8.15, 228=8.07, 24=0.63, 240=8.33, 36=0.86, 48=1.12, 60=1.39, 72=1.67, 84=1.98, 96=2.31}
Not in sorted order.
TreeMap should keep the keys in the sorted order, Isn't it?
Am I missing anything here?
You are right that TreeMap will sort based on the keys.
But in your case the Key is String rather than Integer.
And the result is sorted based on the String Value, i.e.
"108".compareTo("12") will be negative.
The String comparison is based on the Unicode value of each character.
You have to use TreeMap<Integer, Float> if you want to sort on the Integer value.
you can sort it before loop same this :
Collections.sort(period, new Comparator<String>() {
#Override
public int compare(final String o1, final String o2) {
return Integer.valueOf(o1.replaceAll("Rate", ""))
.compareTo(Integer.valueOf(o2.replaceAll("Rate", "")));
}
});
I have a program that reads a .txt file, creates a HashMap containing each unique string and its number of occurrences, and I would like to create an ArrayList that displays these unique strings in descending order in terms of their number of appearances.
Currently, my program sorts in descending order from an alphabetical standpoint (using ASCII values I assume).
How can I sort this in descending order in terms of their number of appearances?
Here's the relevant part of the code:
Scanner in = new Scanner(new File("C:/Users/ahz9187/Desktop/counter.txt"));
while(in.hasNext()){
String string = in.next();
//makes sure unique strings are not repeated - adds a new unit if new, updates the count if repeated
if(map.containsKey(string)){
Integer count = (Integer)map.get(string);
map.put(string, new Integer(count.intValue()+1));
} else{
map.put(string, new Integer(1));
}
}
System.out.println(map);
//places units of map into an arrayList which is then sorted
//Using ArrayList because length does not need to be designated - can take in the units of HashMap 'map' regardless of length
ArrayList arraylist = new ArrayList(map.keySet());
Collections.sort(arraylist); //this method sorts in ascending order
//Outputs the list in reverse alphabetical (or descending) order, case sensitive
for(int i = arraylist.size()-1; i >= 0; i--){
String key = (String)arraylist.get(i);
Integer count = (Integer)map.get(key);
System.out.println(key + " --> " + count);
}
In Java 8:
public static void main(final String[] args) throws IOException {
final Path path = Paths.get("C:", "Users", "ahz9187", "Desktop", "counter.txt");
try (final Stream<String> lines = Files.lines(path)) {
final Map<String, Integer> count = lines.
collect(HashMap::new, (m, v) -> m.merge(v, 1, Integer::sum), Map::putAll);
final List<String> ordered = count.entrySet().stream().
sorted((l, r) -> Integer.compare(l.getValue(), r.getValue())).
map(Entry::getKey).
collect(Collectors.toList());
ordered.forEach(System.out::println);
}
}
First read the file using the Files.lines method which gives your a Stream<String> of the lines.
Now collect the lines into a Map<String, Integer> using the Map.merge method which takes a key and a value and also a lambda that is applied to the old value and the new value if the key is already present.
You now have your counts.
Now take a Stream of the entrySet of the Map and sort that by the value of each Entry and then take the key. Collect that to a List. You now have a List of your values sorted by count.
Now simply use forEach to print them.
If still using Java 7 you can use the Map to provide the sort order:
final Map<String, Integer> counts = /*from somewhere*/
final List<String> sorted = new ArrayList<>(counts.keySet());
Collections.sort(sorted, new Comparator<String>() {
#Override
public int compare(final String o1, final String o2) {
return counts.get(o1).compareTo(counts.get(o2));
}
});
You haven't shown the declaration of your map, but for the purpose of this answer I'm assuming that your map is declared like this:
Map<String,Integer> map = new HashMap<String,Integer>();
You need to use a Comparator in the call to sort, but it needs to compare by the count, while remembering the string. So you need to put objects in the list that have both the string and the count.
One type that provides this capability, and that is easily available from the Map.entrySet method, is the type Map.Entry.
The last part rewritten with Map.Entry and a Comparator:
ArrayList<Map.Entry<String,Integer>> arraylist = new ArrayList<Map.Entry<String,Integer>>(map.entrySet());
Collections.sort(arraylist, new Comparator<Map.Entry<String,Integer>>() {
#Override
public int compare(Entry<String, Integer> e1, Entry<String, Integer> e2) {
// Compares by count in descending order
return e2.getValue() - e1.getValue();
}
});
// Outputs the list in reverse alphabetical (or descending) order, case sensitive
for (Map.Entry<String,Integer> entry : arraylist) {
System.out.println(entry.getKey() + " --> " + entry.getValue());
}
What would be the fastest way to get the common values from all the sets within an hash map?
I have a
Map<String, Set<String>>
I check for the key and get all the sets that has the given key. But instead of getting all the sets from the hashmap, is there any better way to get the common elements (value) from all the sets?
For example, the hashmap contains,
abc:[ax1,au2,au3]
def:[ax1,aj5]
ijk:[ax1,au2]
I want to extract the ax1 and au2 alone, as they are the most common values from the set.
note: not sure if this is the fastest, but this is one way to do this.
First, write a simple method to extract the frequencies for the Strings occurring across all value sets in the map. Here is a simple implementation:
Map<String, Integer> getFrequencies(Map<String, Set<String>> map) {
Map<String, Integer> frequencies = new HashMap<String, Integer>();
for(String key : map.keySet()) {
for(String element : map.get(key)) {
int count;
if(frequencies.containsKey(element)) {
count = frequencies.get(element);
} else {
count = 1;
}
frequencies.put(element, count + 1);
}
}
return new frequencies;
}
You can simply call this method like this: Map<String, Integer> frequencies = getFrequencies(map)
Second, in order to get the most "common" elements in the frequencies map, you simply sort the entries in the map by using the Comparator interface. It so happens that SO has an excellent community wiki that discusses just that: Sort a Map<Key, Value> by values (Java). The wiki contains multiple interesting solutions to the problem. It might help to go over them.
You can simply implement a class, call it FrequencyMap, as shown below.
Have the class implement the Comparator<String> interface and thus the int compare(String a, String b) method to have the elements of the map sorted in the increasing order of the value Integers.
Third, implement another method, call it getCommon(int threshold) and pass it a threshold value. Any entry in the map that has a frequency value greater than threshold, can be considered "common", and will be returned as a simple List.
class FrequencyMap implements Comparator<String> {
Map<String, Integer> map;
public FrequencyMap(Map<String, Integer> map) {
this.map = map;
}
public int compare(String a, String b) {
if (map.get(a) >= map.get(b)) {
return -1;
} else {
return 1;
} // returning 0 would merge keys
}
public ArrayList<String> getCommon(int threshold) {
ArrayList<String> common = new ArrayList<String>();
for(String key : this.map.keySet()) {
if(this.map.get(key) >= threshold) {
common.add(key);
}
}
return common;
}
#Override public String toString() {
return this.map.toString();
}
}
So using FrequencyMap class and the getCommon method, it boils down to these few lines of code:
FrequencyMap frequencyMap = new FrequencyMap(frequencies);
System.out.println(frequencyMap.getCommon(2));
System.out.println(frequencyMap.getCommon(3));
System.out.println(frequencyMap.getCommon(4));
For the sample input in your question this is the o/p that you get:
// common values
[ax1, au6, au3, au2]
[ax1, au2]
[ax1]
Also, here is a gist containing the code i whipped up for this question: https://gist.github.com/VijayKrishna/5973268
This is not basically how to sort the HashMap based on keys. For that I could directly use TreeMap without a wink :)
What I have at the moment is
Map<String, Object> favoritesMap = new HashMap<String, Object>();
and its contents can be
["Wednesdays" : "abcd"]
["Mondays" : "1234"]
["Not Categorized" : "pqrs"]
["Tuesdays" : "5678"]
I want to sort the HashMap based on keys and additional to this I need "Not Categorized" to be the last one to retrieve.
So expected while iterating over keySet is
["Mondays", "Tuesdays", "Wednesdays", "Not Categorized"] i.e. sorted on keys and "Not Categorized" is the last one
Thought of going for HashMap while creating and at the end add ["Not Categorized" : "pqrs"] but HashMap does not guarantee the order :)
Any other pointers for the solution?
Are you specifically excluding TreeMap for some external reason? If not you could obviously use TreeMap with a specially made Comparator.
Have you considered any of the other SortedMaps?
If TreeMap is definitely out I would extend HashMap and make it look like there is always one more entry but that is certainly not a trivial piece of work. You should have a very good reason not to use a SortedMap before going down this road.
Added
Here is an example of how you can make a particular entry always sort to the end using a TreeMap:
// This key should always appear at the end of the list.
public static final String AtEnd = "Always at the end";
// A sample map.
SortedMap<String, String> myMap =
new TreeMap<>(
new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return o1.equals(AtEnd) ? 1 : o2.equals(AtEnd) ? -1 : o1.compareTo(o2);
}
});
private void test() {
myMap.put("Monday", "abc");
myMap.put("Tuesday", "def");
myMap.put("Wednesday", "ghi");
myMap.put(AtEnd, "XYZ");
System.out.println("myMap: "+myMap);
// {Monday=abc, Tuesday=def, Wednesday=ghi, Always at the end=XYZ}
}
I wonder if you are looking for some variant of that?
You can achieve this by using LinkedHashMap as it guarantees to return results in the order of insertion.
Also check the following post to understand difference between map types.
Difference between HashMap, LinkedHashMap and TreeMap
Or just a create a custom class which holds a different key than the value. Sort according to the key of that class. For your case make the key same value as the day, and for "Not Categorized" case ensure that its key starts later than any of the other keys, for example make it "Z_Not Categorized".
public ComplexKey
{
String key;
String value;
}
ComplexKey monday = new ComplexKey("monday", "monday");
ComplexKey notCategorized = new ComplexKey("Z_Not Categorized", "Not Categorized");
Then you can write a custom comparator which sort the values according to the key of complexKey class.
In your case I would use a TreeMap:
Map<DayOfWeek, Object> favoritesMap = new TreeMap<>();
where DayOfWeek is a class you declare like:
class DayOfWeek implements Comparable<DayOfWeek> {
as it's not convenient to sort days of wooks as strings.
In fact, the keys are always sorted. If you output the map a couple of times, you will find that the result remains the same.
First I'll gossip again on hashing:
The reason is hashing. Each object has hashCode() method. The hash space is like a large array which contains all the possible hash values as indices. When a new element is inserted into a HashSet or a new pair is put into a HashMap, it is placed in the hash space according to its hash code. If two elements have the same hash code, they will be compared with equals() method, if unequal, then the new element will be placed next to it.
Then if you know what happens there, you can implement some code like below:
import java.util.*;
class MyString {
private String str;
public MyString (String str) {
this.str = str;
}
public String toString () {
return str;
}
public boolean equals (Object obj) {
if (obj.getClass().equals(MyString.class)) {
return obj.toString().equals(str);
}
return false;
}
public int hashCode () {
if (str.equalsIgnoreCase("Not Categorized")) {
return Integer.MAX_VALUE;
} else if (str.hashCode() == Integer.MAX_VALUE) {
return 0;
}
return str.hashCode();
}
}
public class Test {
public static void main (String args[]) {
Map<MyString, String> m = new HashMap<MyString, String>();
m.put(new MyString("a"), "a");
m.put(new MyString("c"), "c");
m.put(new MyString("Not Categorized"), "NC");
m.put(new MyString("b"), "b");
Set<MyString> keys = m.keySet();
for (MyString k : keys) {
System.out.println(m.get(k));
}
}
}
The result is "Not Categorized" always comes at last. The reason is simple: it's hash value is always the maximum of integer.
The reason I create a String wrapper class is String class is final, it can't be extended. So in this way, you would have your class structure a little change, but not much.
It is possible to use TreeMap, though it would be less efficient:
public static void main (String args[]) {
Map<String, String> m = new TreeMap<String, String>(new Comparator<String>() {
public int compare (String s1, String s2) {
if (s1.equals(s2)) {
return 0;
}
if (s1.equalsIgnoreCase("Not Categorized")) {
return 1;
}
if (s2.equalsIgnoreCase("Not Categorized")) {
return -1;
}
if (s1.hashCode() > s2.hashCode()) {
return 1;
} else if (s1.hashCode() < s2.hashCode()) {
return -1
} else {
return 0;
}
}
public boolean equals (Object obj) {
return false;
}
});
m.put("a", "a");
m.put("c", "c");
m.put("Not Categorized", "NC");
m.put("b", "b");
Set<String> keys = m.keySet();
for (String k : keys) {
System.out.println(m.get(k));
}
}
The result is the same. It will sort all the elements, but it won't change the hashing order of other strings, it only ensures "Not Categorized" always comes to be the largest one.
How to move a particular HashMap entry to Last position?
For Example, I have HashMap values like this:
HashMap<String,Integer> map = new HashMap<String,Integer>();
map= {Not-Specified 1, test 2, testtest 3};
"Not-Specified" may come in any position. it may come first or in the middle of the map. But i want to move the "Not-Specified" to the last position.
How can I do that?
To answer your question in one sentence:
Per default, Maps don't have a last entry, it's not part of their contract.
And a side note: it's good practice to code against interfaces, not the implementation classes (see Effective Java by Joshua Bloch, Chapter 8, Item 52: Refer to objects by their interfaces).
So your declaration should read:
Map<String,Integer> map = new HashMap<String,Integer>();
(All maps share a common contract, so the client need not know what kind of map it is, unless he specifies a sub interface with an extended contract).
Possible Solutions
Sorted Maps:
There is a sub interface SortedMap that extends the map interface with order-based lookup methods and it has a sub interface NavigableMap that extends it even further. The standard implementation of this interface, TreeMap, allows you to sort entries either by natural ordering (if they implement the Comparable interface) or by a supplied Comparator.
You can access the last entry through the lastEntry method:
NavigableMap<String,Integer> map = new TreeMap<String, Integer>();
// add some entries
Entry<String, Integer> lastEntry = map.lastEntry();
Linked maps:
There is also the special case of LinkedHashMap, a HashMap implementation that stores the order in which keys are inserted. There is however no interface to back up this functionality, nor is there a direct way to access the last key. You can only do it through tricks such as using a List in between:
Map<String,String> map = new LinkedHashMap<String, Integer>();
// add some entries
List<Entry<String,Integer>> entryList =
new ArrayList<Map.Entry<String, Integer>>(map.entrySet());
Entry<String, Integer> lastEntry =
entryList.get(entryList.size()-1);
Proper Solution:
Since you don't control the insertion order, you should go with the NavigableMap interface, i.e. you would write a comparator that positions the Not-Specified entry last.
Here is an example:
final NavigableMap<String,Integer> map =
new TreeMap<String, Integer>(new Comparator<String>() {
public int compare(final String o1, final String o2) {
int result;
if("Not-Specified".equals(o1)) {
result=1;
} else if("Not-Specified".equals(o2)) {
result=-1;
} else {
result =o1.compareTo(o2);
}
return result;
}
});
map.put("test", Integer.valueOf(2));
map.put("Not-Specified", Integer.valueOf(1));
map.put("testtest", Integer.valueOf(3));
final Entry<String, Integer> lastEntry = map.lastEntry();
System.out.println("Last key: "+lastEntry.getKey()
+ ", last value: "+lastEntry.getValue());
Output:
Last key: Not-Specified, last value: 1
Solution using HashMap:
If you must rely on HashMaps, there is still a solution, using a) a modified version of the above comparator, b) a List initialized with the Map's entrySet and c) the Collections.sort() helper method:
final Map<String, Integer> map = new HashMap<String, Integer>();
map.put("test", Integer.valueOf(2));
map.put("Not-Specified", Integer.valueOf(1));
map.put("testtest", Integer.valueOf(3));
final List<Entry<String, Integer>> entries =
new ArrayList<Entry<String, Integer>>(map.entrySet());
Collections.sort(entries, new Comparator<Entry<String, Integer>>(){
public int compareKeys(final String o1, final String o2){
int result;
if("Not-Specified".equals(o1)){
result = 1;
} else if("Not-Specified".equals(o2)){
result = -1;
} else{
result = o1.compareTo(o2);
}
return result;
}
#Override
public int compare(final Entry<String, Integer> o1,
final Entry<String, Integer> o2){
return this.compareKeys(o1.getKey(), o2.getKey());
}
});
final Entry<String, Integer> lastEntry =
entries.get(entries.size() - 1);
System.out.println("Last key: " + lastEntry.getKey() + ", last value: "
+ lastEntry.getValue());
}
Output:
Last key: Not-Specified, last value: 1
HashMap doesn't have "the last position", as it is not sorted.
You may use other Map which implements java.util.SortedMap, most popular one is TreeMap.
A SortedMap is the logical/best choice, however another option is to use a LinkedHashMap which maintains two order modes, most-recently-added goes last, and most-recently-accessed goes last. See the Javadocs for more details.
When using numbers as the key, I suppose you could also try this:
Map<Long, String> map = new HashMap<>();
map.put(4L, "The First");
map.put(6L, "The Second");
map.put(11L, "The Last");
long lastKey = 0;
//you entered Map<Long, String> entry
for (Map.Entry<Long, String> entry : map.entrySet()) {
lastKey = entry.getKey();
}
System.out.println(lastKey); // 11
move does not make sense for a hashmap since its a dictionary with a hashcode for bucketing based on key and then a linked list for colliding hashcodes resolved via equals.
Use a TreeMap for sorted maps and then pass in a custom comparator.
In such scenario last used key is usually known so it can be used for accessing last value (inserted with the one):
class PostIndexData {
String _office_name;
Boolean _isGov;
public PostIndexData(String name, Boolean gov) {
_office_name = name;
_isGov = gov;
}
}
//-----------------------
class KgpData {
String _postIndex;
PostIndexData _postIndexData;
public KgpData(String postIndex, PostIndexData postIndexData) {
_postIndex = postIndex;
_postIndexData = postIndexData;;
}
}
public class Office2ASMPro {
private HashMap<String,PostIndexData> _postIndexMap = new HashMap<>();
private HashMap<String,KgpData> _kgpMap = new HashMap<>();
...
private void addOffice(String kgp, String postIndex, String officeName, Boolean gov) {
if (_postIndexMap.get(postIndex) == null) {
_postIndexMap.put(postIndex, new PostIndexData(officeName, gov));
}
_kgpMap.put( kgp, new KgpData(postIndex, _postIndexMap.get(postIndex)) );
}
Find missing all elements from array
int[] array = {3,5,7,8,2,1,32,5,7,9,30,5};
TreeMap<Integer, Integer> map = new TreeMap<>();
for(int i=0;i<array.length;i++) {
map.put(array[i], 1);
}
int maxSize = map.lastKey();
for(int j=0;j<maxSize;j++) {
if(null == map.get(j))
System.out.println("Missing `enter code here`No:"+j);
}