I have a map: TreeMap<String, Integer> m = new TreeMap<>(); where I have a whole alphabet and values, which shows how many times each letter was found in my text.
I want to sort that map in descending count order; that is, the most frequent letter is on the first line, and the last line of output indicates the least frequent letter. If two letters have the same frequency, then the letter which comes first in the alphabet must appear first. How to make it?
I tried with Comparator:
public int compare(String a, String b) {
if (base.get(a) >= base.get(b) && a.compareToIgnoreCase(b) < 0) {
return -1;
} else {
return 1;
}
}
but still, its not it, the output is:
D 3
E 3
A 2
S 5
Guys ... Found this before, this didnt help at all. Good output should be:
S 5
D 3
E 3
A 2
Your comparator does not look right - this should work better:
public int compare(String a, String b) {
if (base.get(a) > base.get(b)) {
return -1;
} else if (base.get(a) < base.get(b)) {
return 1;
} else {
int stringCompare = a.compareToIgnoreCase(b);
return stringCompare == 0 ? 1 : stringCompare; // returning 0 would merge keys
}
}
As the natural sort has nothing in common with your sorting wish:
List<Map.Entry<String, Integer>> entries = new ArrayList<>(m.entrieSet());
Collections.sort(entries, new Comparator<Map.Entry<String, Integer>>() {
#Override
public int compare(Map.Entry<String, Integer >a, Map.Entry<String, Integer>b) {
if (a.getValue() < b.getValue()) { // Descending values
return 1;
} else if (a.getValue() > b.getValue()) {
return -1;
}
return -a.getKey().compareTo(b.getKey()); // Descending keys
}
});
Related
I'm looking for a quick and smart way to find up until where two lists are equal. In other words I need to find the smallest partition containing common elements in the same order of two or more lists.
It might sound a bit confusing but here is an example of what I want to achieve:
List 1: A, B, C, L, M Z
List 2: A, B, C, K, F
Output -> List 3: A, B, C
I need to use this in a recursive method which should be called with large inputs and all the solutions I've come up with are a bit too slow.
Thanks for your answers in advance
EDIT:
Please excuse me for being unclear. This is my first question and english is not my first language.
Let me explain the problem in a better way. I need to find the intersection of two or more lists starting from the first element of the lists. Please note that elements must be in the same order so it's not exactly an intersection but more like a partition.
the "recursive" thing was just to say that I need to include this in a recursive method which will run many times so I would like the solution to be as fast as possibile as to not lose a lot of time.
Working on an answer that appears to have been deleted I came up with my own solution:
List<String> list1 = new ArrayList<>(Arrays.asList("ciao", "come"));
List<String> list2 = new ArrayList<>(Arrays.asList("ciao", "come", "va"));
List<String> list3 = new ArrayList<>(Arrays.asList("ciao", "come", "va", "?", "tutto", "ok"));
List<List<String>> allLists = new ArrayList<>();
allLists.addAll(Arrays.asList(list1, list2, list3));
int min = Integer.MAX_VALUE;
int listIndex = 0;
for(List<String> list : allLists){
if(min > list.size()){
min = list.size();
listIndex = allLists.indexOf(list);
}
}
int index = 0;
boolean same = true;
while(index<min && same == true) {
String element = allLists.get(listIndex).get(index);
for(List<String> list : allLists){
if(!list.get(index).equals(element)){
same = false;
break;
}
element = allLists.get(listIndex).get(index);
}
if(same == true) ++index;
}
System.out.println("OUTPUT:" + allLists.get(listIndex).subList(0, index));
----> Output: ciao, come
EDIT2:
And also garnful's solution works like a charm and I find it way clearer than mine. Thanks everybody
This should do the work, and hopefully be quite okay regarding the performance:
public List<String> getEqualsPart(List<String>[] listsToCheck) {
if (listsToCheck.length == 0) {
return Collections.emptyList();
}
int minLength = getShortesListLength(listsToCheck);
if (minLength == 0) {
return Collections.emptyList();
}
return getEqualPartsForIndex(listsToCheck, 0, minLength, new ArrayList<String>());
}
private int getShortesListLength(List<String>[] listsToCheck) {
int min = Integer.MAX_VALUE;
for (List<String> currentList : listsToCheck) {
min = Math.min(min, currentList.size());
}
return min;
}
private List<String> getEqualPartsForIndex(List<String>[] listsToCheck, int index, int minLength,
List<String> result) {
if (index == minLength) {
return result;
}
Set<String> setForIndex = new HashSet<>();
Arrays.stream(listsToCheck).forEach(list -> setForIndex.add(list.get(index)));
if (setForIndex.size() > 1) {
return result;
} else {
result.add(setForIndex.iterator().next());
return getEqualPartsForIndex(listsToCheck, index + 1, minLength, result);
}
}`
Try:
public List<E> equalUntil(List<E> l1, List<E> l2) {
return equalUntilRec(l1.iterator(), l2.iterator(), new ArrayList<E>());
}
private int equalUntilRec(Iterator<E> it1, Iterator<E> it2, List<E> acc) {
if(!it1.hasNext() || !it2.hasNext()) {
return acc;
} else {
E e1 = it1.next();
E e2 = it2.next();
if(!e1.equals(e2)) {
return acc;
}
acc.add(e1);
return equalUntilRec(it1, it2, acc);
}
}
Is there a smart way to check if all keys map to the same value? So the hash table will be as below:
a=>2;
b=>2;
c=>2;
d=>2;
So a,b,d,c and d all map to the same val. I am asking because I have to find the maximum occurrence of a number in a list but it no number is the clear max, I should just print "None". So if 2 number have the max, it means no number is the clear max in terms of occurrence. Also, how do I check if there's no clear max in the values.
Below is what I have so far but it always returns "None":
private static void getMaxOccrrance(String a) {
String[] sNew = a.split(",");
Hashtable<Integer,Integer> nums = new Hashtable<>();
for(String x : sNew) {
int num = Integer.parseInt(x);
if (!nums.containsKey(num)) {
nums.put(num, 1);
} else {
nums.put(num, nums.get(num) + 1);
}
}
int val = 0, max = 1;
for(int keys : nums.keySet()){
if(nums.get(keys) > max){
val = keys;
max = nums.get(keys);
}
}
boolean uniqueMax = true;
int count = 0;
for(int values : nums.values()){
if(val == values) {
count++;
if(count >= 2){
uniqueMax = false;
break;
}
}
}
if(uniqueMax){
System.out.println(val);
}else {
System.out.println("None");
}
}
If you work with Map in a single thread, much more efficient to keep&update this information directly on Map operations, if they are always non-decreasing by value put()s. E. g.
class MyMap<K> {
Map<K, Integer> impl;
K singleMaxKey;
int maxValue;
public void put(K key, int value) {
if (value > maxValue) {
maxValue = value;
singleMaxKey = key;
} else if (value == maxValue && !key.equals(singleMaxKey)) {
sibgleMaxKey = null;
}
impl.put(key, value);
}
}
If you have Java 8 you can do this fairly easily using streams. The following will return a map from each distinct value in a list to its frequency:
Map<Value, Integer> getFrequencyMap(Collection<Value> list) {
return list.stream().distinct()
.collect(Collectors.toMap(value -> value, value -> Collections.frequency(list, value));
}
You can call this with map.values() to get the frequency of all values in the your map.
If you wish to determine if there is more than one value that occurs the same, maximum number of times, you can check the new frequency map to see if the value occurs more than once:
Map<Key, Value> map;
Map<Value, Integer> valueFrequencies = getFrequencyMap(map.values());
int maxFrequency = valueFrequencies.values().stream().max().orElse(0);
if (Collections.frequency(valueFrequencies.values(), maxFrequency) > 1) {
// no clear max frequency
}
I would like to sort my HashMap (or TreeMap) by values. I kind of achieved this by creating a custom Comparator that sorts after value. However whenever I put in all my entries from the HashMap again I get duplicates.
How can I sort by values without creating duplicates?
CODE
public class Test {
public static void main(String[] args) {
HashMap<Integer, String> hMap = new HashMap<Integer, String>();
ValueComparator vc = new ValueComparator(hMap);
TreeMap<Integer, String> tMap = new TreeMap<Integer, String>(vc);
hMap.put(0, "b");
hMap.put(1, "c");
hMap.put(2, "a");
tMap.putAll(hMap);
tMap.putAll(hMap);
for (Map.Entry<Integer, String> entry : tMap.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}
class ValueComparator implements Comparator<Integer> {
Map<Integer, String> base;
public ValueComparator(Map<Integer, String> base) {
this.base = base;
}
public int compare(Integer a, Integer b) {
if (base.get(a).charAt(0) >= base.get(b).charAt(0))
return 1;
else return -1;
}
}
OUTPUT
2 a
2 a
0 b
0 b
1 c
1 c
You need to modify logic as below, handle all three cases of -1, 0 and 1
public int compare(Integer a, Integer b) {
if (base.get(a).charAt(0) == base.get(b).charAt(0))
return 0;
else if (base.get(a).charAt(0) > base.get(b).charAt(0))
return 1;
else
return -1;
}
output
2 a
0 b
1 c
The compare method should return 0 if both objects are equal. In your implementation, you're returning 1, and thus the map does not recognize duplicates properly.
One way to solve this is to reuse Character.compare to compare two chars:
public int compare(Integer a, Integer b) {
return Character.compare
(base.get(a).charAt(0), base.get(b).charAt(0));
}
Your comparator contract is wrong. the compare method contract says:
Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
Your code is only doing 1 and -1
what about the 0?
I have been given a large text as input. I have made a HashMap that stores each different word as a key, and number of times that occurs as value (Integer).
Now I have to make a method called mostOften(int k):List that return a List that gives the first k-words that from max number of occurrence to min number of occurrence ( descending order ) using the HashMap that I have made before.
The problem is that whenever 2 words have the same number of occurrence, then they should be sorted alphabetically.
The first idea that was on my mind was to swap keys and values of the given HashMap, and put it into TreeMap and TreeMap will sort the words by the key(Integer - number of occurrence of the word ) and then just pop the last/first K-entries from the TreeMap.
But I will have collision for sure, when the number of 2 or 3 words are the same. I will compare the words alphabetically but what Integer should I put as a key of the second word comming.
Any ideas how to implement this, or other options ?
Hints:
Look at the javadocs for the Collections.sort methods ... both of them!
Look at the javadocs for Map.entries().
Think about how to implement a Comparator that compares instances of a class with two fields, using the 2nd as a "tie breaker" when the other compares as equal.
Here's the solution with I come up.
First you create a class MyWord that can store the String value of the word and the number of occurences it appears.
You implement the Comparable interface for this class to sort by occurences first and then alphabetically if the number of occurences is the same
Then for the most often method, you create a new List of MyWord from your original map. You add the entries of this to your List
You sort this list
You take the k-first items of this list using subList
You add those Strings to the List<String> and you return it
public class Test {
public static void main(String [] args){
Map<String, Integer> m = new HashMap<>();
m.put("hello",5);
m.put("halo",5);
m.put("this",2);
m.put("that",2);
m.put("good",1);
System.out.println(mostOften(m, 3));
}
public static List<String> mostOften(Map<String, Integer> m, int k){
List<MyWord> l = new ArrayList<>();
for(Map.Entry<String, Integer> entry : m.entrySet())
l.add(new MyWord(entry.getKey(), entry.getValue()));
Collections.sort(l);
List<String> list = new ArrayList<>();
for(MyWord w : l.subList(0, k))
list.add(w.word);
return list;
}
}
class MyWord implements Comparable<MyWord>{
public String word;
public int occurence;
public MyWord(String word, int occurence) {
super();
this.word = word;
this.occurence = occurence;
}
#Override
public int compareTo(MyWord arg0) {
int cmp = Integer.compare(arg0.occurence,this.occurence);
return cmp != 0 ? cmp : word.compareTo(arg0.word);
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + occurence;
result = prime * result + ((word == null) ? 0 : word.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyWord other = (MyWord) obj;
if (occurence != other.occurence)
return false;
if (word == null) {
if (other.word != null)
return false;
} else if (!word.equals(other.word))
return false;
return true;
}
}
Output : [halo, hello, that]
In addition to your Map to store word counts I would use a PriorityQueue of fixed size K (with natural order). It will allow to reach O(N) complexity. Here is a code which use this approach:
In constructor we are reading input stream word by word filling the counters in the Map.
In the same time we are updating priority queue keeping it's max size = K (we need count top K words)
public class TopNWordsCounter
{
public static class WordCount
{
String word;
int count;
public WordCount(String word)
{
this.word = word;
this.count = 1;
}
}
private PriorityQueue<WordCount> pq;
private Map<String, WordCount> dict;
public TopNWordsCounter(Scanner scanner)
{
pq = new PriorityQueue<>(10, new Comparator<WordCount>()
{
#Override
public int compare(WordCount o1, WordCount o2)
{
return o2.count-o1.count;
}
});
dict = new HashMap<>();
while (scanner.hasNext())
{
String word = scanner.next();
WordCount wc = dict.get(word);
if (wc == null)
{
wc = new WordCount(word);
dict.put(word, wc);
}
if (pq.contains(wc))
{
pq.remove(wc);
wc.count++;
pq.add(wc);
}
else
{
wc.count++;
if (pq.size() < 10 || wc.count >= pq.peek().count)
{
pq.add(wc);
}
}
if (pq.size() > 10)
{
pq.poll();
}
}
}
public List<String> getTopTenWords()
{
Stack<String> topTen = new Stack<>();
while (!pq.isEmpty())
{
topTen.add(pq.poll().word);
}
return topTen;
}
}
import java.util.*;
public class Sort {
static class ValueComparator implements Comparator<String> {
Map<String, Integer> base;
ValueComparator(Map<String, Integer> base) {
this.base = base;
}
#Override
public int compare(String a, String b) {
if (base.get(a) >= base.get(b)) {
return 1;
} else {
return -1;
}
}
}
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
ValueComparator vc = new ValueComparator(map);
TreeMap<String, Integer> sorted = new TreeMap<String, Integer>(vc);
map.put("A", 1);
map.put("B", 2);
sorted.putAll(map);
for (String key : sorted.keySet()) {
System.out.println(key + " : " + sorted.get(key)); // why null values here?
}
System.out.println(sorted.values()); // But we do have non-null values here!
}
}
Output:
A : null
B : null
[1, 2]
BUILD SUCCESSFUL (total time: 0 seconds)
I wonder why we get null values at the first commented line while we do have non-null values as demonstrated by the second commented line.
Edit: #null's version seems not working. I've changed my code as follows:
public int compare(String a, String b) {
if (a.equals(b)) return 0;
if (base.get(a) >= base.get(b)) {
return 1;
} else return -1;
}
It seems to work but I'm not sure.
My guess is that your ValueComparator.compare() method never returns 0, indicating equality, causing the Map.get() method to not find matches.
Change your compare to in this way
public int compare(String a, String b) {
if (base.get(a) > base.get(b)) {
return 1;
}else if(base.get(a) == base.get(b)){
return 0;
}
return -1;
}
Even with your Comparator which is definitely broken the program will work if you change it as
for (Map.Entry e : sorted.entrySet()) {
System.out.println(e.getKey() + " : " + e.getValue());
}