How to count occurrences for each value in MultiMap ? (java) - java

I have Multimap, which contains two strings. Example:
1 = [key,car],
2 = [key,blue],
3 = [key,car]
Multimap definition (I am using Guava library):
ListMultimap<Integer, String> map_multi = ArrayListMultimap.create();
And this is how I put values in MultiMap:
for (int i = 0; i < list.size(); i++) {
if (i + 1 < list.size()) {
multimap.put(i,(String) list.get(i));
multimap.put(i,(String) list.get(i+1));
} else if (i + 1 == list.size()) {
}
}
I want to count the occurrences of the same value inside the multimap.
So the result should be 2 if i count how many values [key,car] are (per example I have given above) in my multimap:
occurrences of [key,car] = 2
occurrences of [key,blue] = 1
I have also tried to implement this with multi value HashMap and I was counting it with this, way (Storage is class where I store two string values inside object):
B = Collections.frequency(new ArrayList<Storage>(map.values()), map.get(number));
But I don't get the right results.

You can achieve what you want by creating a map that has your multimap values as the keys and the count as the value:
Map<Collection<String>, Long> result = map_multi.asMap().values().stream()
.collect(Collectors.groupingBy(v -> v, Collectors.counting()));
Here I've used Guava's Multimap.asMap method to get a view over the original multimap, then collected the values into a new map.
Another way, without streams:
Map<Collection<String>, Integer> result = new HashMap<>();
map_multi.asMap().values().forEach(v -> result.merge(v, 1, Integer::sum));
This uses the Map.merge method to accumulate equal values by counting its occurrences.

Please try this code. map_multi.get(key).size() is your answer.
ListMultimap<Integer, String> map_multi = ArrayListMultimap.create();
map_multi.put(1, "car");
map_multi.put(2, "blue");
map_multi.put(3, "apple");
map_multi.put(1, "car");
for (Integer key : map_multi.keySet()) {
System.out.println(map_multi.get(key).get(0) + " occurances: " + map_multi.get(key).size());
}
Output:
car occurances: 2
blue occurances: 1
apple occurances: 1

So the first step is to create a Map<Integer, List<String>> from your ListMultimap. You can do this in the following way:
Map<Integer, List<String>> collect = map_multi.entries()
.stream()
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
Then let's say you have a List<String> with car and key in it.
Example:
List<String> myList = List.of("key", "car"); // java 9
You just iterate through the values() of the map and check if myList contains all elements from map's lists.
long count = collect.values()
.stream()
.filter(list -> list.containsAll(myList))
.count();

I think you're using wrong collection to store your data. Based on what you wrote, you want a map with integer keys and two-element tuples as values and then use Multiset to count frequencies:
Multiset is a collection that supports order-independent equality, like Set, but may have duplicate elements. A multiset is also sometimes called a bag.
Elements of a multiset that are equal to one another are referred to as occurrences of the same single element. The total number of occurrences of an element in a multiset is called the count of that element (the terms "frequency" and "multiplicity" are equivalent, but not used in this API).
The code below assumes you have proper implementation of two-element tuple (aka pair, or your Storage class, but with proper equals and hashCode implementations), like this one from jOOL:
HashMap<Integer, Tuple2<String, String>> m = new HashMap<>();
Tuple2<String, String> carTuple = new Tuple2<>("key", "car");
Tuple2<String, String> blueTuple = new Tuple2<>("key", "blue");
m.put(1, carTuple);
m.put(2, blueTuple);
m.put(3, carTuple);
ImmutableMultiset<Tuple2<String, String>> occurrences =
ImmutableMultiset.copyOf(m.values());
System.out.println(occurrences); // [(key, car) x 2, (key, blue)]
If you need to have few values (tuples) mapped under one key (integer), then you should change the first line to multimap:
ListMultimap<Integer, Tuple2<String, String>> m = ArrayListMultimap.create();
so that m.put(1, anotherTuple) is possible and putting doesn't override first value (carTuple) but rather adds it under 1 values list.
EDIT:
You can implement Tuple2 yourself if you don't need/want additional dependency, it could look like this class:
public class Tuple2<T1, T2> {
public final T1 v1;
public final T2 v2;
public Tuple2(T1 v1, T2 v2) {
this.v1 = v1;
this.v2 = v2;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Tuple2)) {
return false;
}
#SuppressWarnings({"unchecked", "rawtypes"}) final Tuple2<T1, T2> that = (Tuple2) o;
return Objects.equals(v1, that.v1) && Objects.equals(v2, that.v2);
}
#Override
public int hashCode() {
return Objects.hash(v1, v2);
}
#Override
public String toString() {
return "(" + v1 + ", " + v2 + ")";
}
}

Related

Possible permutations of string based on LinkedHashMap?

So this problem has been taunting me for days. Any help is greatly appreciated!
I have made a LinkedHashMap which stores possible combinations for each part of a string and I'm trying to get all the permutations in an ArrayList of Strings, while maintaing the string order.
For example if the map is:
a=ab, b=c
The combinations would be:
ab
ac
abb
abc
I have tried simply looping each keys and values list, heap's algorithm which didn't work out for keeping the order of elements and also tried using recursion but i wasn't sure how. If anyone could point me in the right direction or hint me, that would be great. Thanks
Another map example of what im trying to do.
If the map is:
A=a, B=b
Output is:
AB (key1, key2)
Ab (key1, value2)
aB (value1, key2)
ab (value1, value2)
I basically want every combination of the whole map in order while alternating between keys and values of the map.
Try this.
static List<String> possiblePermutations(Map<String, String> map) {
int size = map.size();
List<Entry<String, String>> list = new ArrayList<>(map.entrySet());
List<String> result = new ArrayList<>();
new Object() {
void perm(int i, String s) {
if (i >= size) {
result.add(s);
return;
}
Entry<String, String> entry = list.get(i);
perm(i + 1, s + entry.getKey());
perm(i + 1, s + entry.getValue());
}
}.perm(0, "");
return result;
}
public static void main(String[] args) {
Map<String, String> map = new LinkedHashMap<>();
map.put("a", "ab");
map.put("b", "c");
map.put("x", "y");
List<String> result = possiblePermutations(map);
System.out.println(result);
}
output:
[abx, aby, acx, acy, abbx, abby, abcx, abcy]
It's very simple ...
Let's say the number of entries in your map is N
There is a 1-to-1 mapping between each possible permutation of such strings and an array boolean of length N. If the array has a true in position K, we pick the key from map entry K, otherwise we pick the value.
Therefore, to generate all possible permutations you need to generate all possible boolean arrays (same as binary numbers) of length N and then use each one to create a corresponding string.

Java HashMap - How to simultaneously get and then remove a random entry from a HashMap?

I was wondering if it is possible to get a random value from a HashMap and then straight after remove that key/value from the HashMap? I can't seem to find any method that works, would a different data structure be more appropriate for this?
Edit:
I should've been more clear, I generate a random number and then retrieve the value that corresponds with that random number. I need to return the value and then remove the entry from the map.
Maybe Map#computeIfPresent would work in your case. From its documentation:
If the value for the specified key is present and non-null, attempts to compute a new mapping given the key and its current mapped value.
If the remapping function returns null, the mapping is removed.
var map = new HashMap<Integer, String>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");
map.computeIfPresent(2, (k, v) -> {
// `v` is equal to "Two"
return null; // Returning `null` removes the entry from the map.
});
System.out.println(map);
The above code outputs the following:
{1=One, 3=Three}
If you were to use a ConcurrentHashMap, then this would be an atomic operation.
The best way to both return and remove the key-value pair from a HashMap is by using the remove(key) method. This method removes the entry associated with the key and returns its corresponding value.
Integer randomNumber = new Random().nextInt(10);
Map<Integer, String> map = new HashMap<>();
String valueOfRandomNumberKey = map.remove(randomNumber);
The problem, as I understand it, is this: given a HashMap you want to
Choose a key at random from among the the keys currently associated in the Map;
Remove that association of that randomly chosen key from the map; and
Return the value that had, until recently, been associated with that key
Here's an example of how to do this, along with some a little test/demonstration routine:
public class Main
{
private static <K, V> V removeRandomEntry(Map<K, V> map){
Set<K> keySet = map.keySet();
List<K> keyList = new ArrayList<>(keySet);
K keyToRemove = keyList.get((int)(Math.random()*keyList.size()));
return map.remove(keyToRemove);
}
public static void main(String[] args){
Map<String, String> map = new HashMap<>();
for(int i = 0; i < 100; ++i)
map.put("Key" + i, "Value"+i);
int pass = 0;
while (!map.isEmpty())
System.out.println("Pass " + (++pass) + ": Removed: " + removeRandomEntry(map));
}
}
I would do it like this:
Hashmap<Integer, Object> example;
int randomNum = ThreadLocalRandom.current().nextInt(0, example.size());
example.getValue() //do something
example.remove(new Integer(randomNum));

How select first N items in Java TreeMap?

Given this map
SortedMap<Integer, String> myMap = new TreeMap<Integer, String>();
Instead of a for loop is there a utility function to copy first N items to a destination map?
Using the power of Java 8+:
TreeMap<Integer, String> myNewMap = myMap.entrySet().stream()
.limit(3)
.collect(TreeMap::new, (m, e) -> m.put(e.getKey(), e.getValue()), Map::putAll);
Maybe, but not as part of the standard Java API. And: the utility would use a loop inside.
So you'll need a loop, but you can create your own "utility" by doing it all in a static method in a utility class:
public static SortedMap<K,V> putFirstEntries(int max, SortedMap<K,V> source) {
int count = 0;
TreeMap<K,V> target = new TreeMap<K,V>();
for (Map.Entry<K,V> entry:source.entrySet()) {
if (count >= max) break;
target.put(entry.getKey(), entry.getValue());
count++;
}
return target;
}
The complexity is still O(n) (I doubt, that one can achieve O(1)) but you use it like a tool without "seeing" the loop:
SortedMap<Integer, String> firstFive = Util.putFirstEntries(5, sourceMap);
There's SortedMap.headMap() however you'd have to pass a key for the element to go up to. You could iterate N elements over Map.keySet() to find it, e.g.:
Integer toKey = null;
int i = 0;
for (Integer key : myMap.keySet()) {
if (i++ == N) {
toKey = key;
break;
}
}
// be careful that toKey isn't null because N is < 0 or >= myMap.size()
SortedMap<Integer, String> copyMap = myMap.headMap(toKey);
You can also use an ordored iterator to get the first x records, orderer by descending id for instance :
Iterator<Integer> iterator = myMap.descendingKeySet().iterator();
You can use the putAll(Map t) function to copy the items from the map to specified map.But it copies all the items. You cannot copy fixed number of items.
http://download.oracle.com/javase/1.4.2/docs/api/java/util/Map.html#putAll%28java.util.Map%29

How to compare two maps by their values

How to compare two maps by their values? I have two maps containing equal values and want to compare them by their values. Here is an example:
Map a = new HashMap();
a.put("foo", "bar"+"bar");
a.put("zoo", "bar"+"bar");
Map b = new HashMap();
b.put(new String("foo"), "bar"+"bar");
b.put(new String("zoo"), "bar"+"bar");
System.out.println("equals: " + a.equals(b)); // obviously false
How should I change the code to obtain a true?
The correct way to compare maps for value-equality is to:
Check that the maps are the same size(!)
Get the set of keys from one map
For each key from that set you retrieved, check that the value retrieved from each map for that key is the same (if the key is absent from one map, that's a total failure of equality)
In other words (minus error handling):
boolean equalMaps(Map<K,V>m1, Map<K,V>m2) {
if (m1.size() != m2.size())
return false;
for (K key: m1.keySet())
if (!m1.get(key).equals(m2.get(key)))
return false;
return true;
}
Your attempts to construct different strings using concatenation will fail as it's being performed at compile-time. Both of those maps have a single pair; each pair will have "foo" and "barbar" as the key/value, both using the same string reference.
Assuming you really want to compare the sets of values without any reference to keys, it's just a case of:
Set<String> values1 = new HashSet<>(map1.values());
Set<String> values2 = new HashSet<>(map2.values());
boolean equal = values1.equals(values2);
It's possible that comparing map1.values() with map2.values() would work - but it's also possible that the order in which they're returned would be used in the equality comparison, which isn't what you want.
Note that using a set has its own problems - because the above code would deem a map of {"a":"0", "b":"0"} and {"c":"0"} to be equal... the value sets are equal, after all.
If you could provide a stricter definition of what you want, it'll be easier to make sure we give you the right answer.
To see if two maps have the same values, you can do the following:
Get their Collection<V> values() views
Wrap into List<V>
Collections.sort those lists
Test if the two lists are equals
Something like this works (though its type bounds can be improved on):
static <V extends Comparable<V>>
boolean valuesEquals(Map<?,V> map1, Map<?,V> map2) {
List<V> values1 = new ArrayList<V>(map1.values());
List<V> values2 = new ArrayList<V>(map2.values());
Collections.sort(values1);
Collections.sort(values2);
return values1.equals(values2);
}
Test harness:
Map<String, String> map1 = new HashMap<String,String>();
map1.put("A", "B");
map1.put("C", "D");
Map<String, String> map2 = new HashMap<String,String>();
map2.put("A", "D");
map2.put("C", "B");
System.out.println(valuesEquals(map1, map2)); // prints "true"
This is O(N log N) due to Collections.sort.
See also:
Collection<V> values()
To test if the keys are equals is easier, because they're Set<K>:
map1.keySet().equals(map2.keySet())
See also:
Set<K> keySet()
All of these are returning equals. They arent actually doing a comparison, which is useful for sort. This will behave more like a comparator:
private static final Comparator stringFallbackComparator = new Comparator() {
public int compare(Object o1, Object o2) {
if (!(o1 instanceof Comparable))
o1 = o1.toString();
if (!(o2 instanceof Comparable))
o2 = o2.toString();
return ((Comparable)o1).compareTo(o2);
}
};
public int compare(Map m1, Map m2) {
TreeSet s1 = new TreeSet(stringFallbackComparator); s1.addAll(m1.keySet());
TreeSet s2 = new TreeSet(stringFallbackComparator); s2.addAll(m2.keySet());
Iterator i1 = s1.iterator();
Iterator i2 = s2.iterator();
int i;
while (i1.hasNext() && i2.hasNext())
{
Object k1 = i1.next();
Object k2 = i2.next();
if (0!=(i=stringFallbackComparator.compare(k1, k2)))
return i;
if (0!=(i=stringFallbackComparator.compare(m1.get(k1), m2.get(k2))))
return i;
}
if (i1.hasNext())
return 1;
if (i2.hasNext())
return -1;
return 0;
}
This question is old, but still relevant.
If you want to compare two maps by their values matching their keys, you can do as follows:
public static <K, V> boolean mapEquals(Map<K, V> leftMap, Map<K, V> rightMap) {
if (leftMap == rightMap) return true;
if (leftMap == null || rightMap == null || leftMap.size() != rightMap.size()) return false;
for (K key : leftMap.keySet()) {
V value1 = leftMap.get(key);
V value2 = rightMap.get(key);
if (value1 == null && value2 == null)
continue;
else if (value1 == null || value2 == null)
return false;
if (!value1.equals(value2))
return false;
}
return true;
}
Since you asked about ready-made Api's ... well Apache's commons. collections library has a CollectionUtils class that provides easy-to-use methods for Collection manipulation/checking, such as intersection, difference, and union.
I don't think there is a "apache-common-like" tool to compare maps since the equality of 2 maps is very ambiguous and depends on the developer needs and the map implementation...
For exemple if you compare two hashmaps in java:
- You may want to just compare key/values are the same
- You may also want to compare if the keys are ordered the same way
- You may also want to compare if the remaining capacity is the same
... You can compare a lot of things!
What such a tool would do when comparing 2 different map implementations such that:
- One map allow null keys
- The other throw runtime exception on map2.get(null)
You'd better to implement your own solution according to what you really need to do, and i think you already got some answers above :)
If you assume that there can be duplicate values the only way to do this is to put the values in lists, sort them and compare the lists viz:
List<String> values1 = new ArrayList<String>(map1.values());
List<String> values2 = new ArrayList<String>(map2.values());
Collections.sort(values1);
Collections.sort(values2);
boolean mapsHaveEqualValues = values1.equals(values2);
If values cannot contain duplicate values then you can either do the above without the sort using sets.
The result of equals in your example is obviously false because you are comparing the map a with some values in it with an empty map b (probably a copy and paste error). I recommend to use proper variable names (so you can avoid these kinds of errors) and make use of generics, too.
Map<String, String> first = new HashMap<String, String>();
first.put("f"+"oo", "bar"+"bar");
first.put("fo"+"o", "bar"+"bar");
Map second = new HashMap();
second.put("f"+"oo", "bar"+"bar");
second.put("fo"+"o", "bar"+"bar");
System.out.println("equals: " + first.equals(second));
The concatenation of your strings doesn't have any effect because it will be done at compile time.
#paweloque For Comparing two Map Objects in java, you can add the keys of a map to list and with those 2 lists you can use the methods retainAll() and removeAll() and add them to another common keys list and different keys list. Using the keys of the common list and different list you can iterate through map, using equals you can compare the maps.
The below code will give output like this:
Before {zoo=barbar, foo=barbar}
After {zoo=barbar, foo=barbar}
Equal: Before- barbar After- barbar
Equal: Before- barbar After- barbar
package com.demo.compareExample
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
public class Demo
{
public static void main(String[] args)
{
Map<String, String> beforeMap = new HashMap<String, String>();
beforeMap.put("foo", "bar"+"bar");
beforeMap.put("zoo", "bar"+"bar");
Map<String, String> afterMap = new HashMap<String, String>();
afterMap.put(new String("foo"), "bar"+"bar");
afterMap.put(new String("zoo"), "bar"+"bar");
System.out.println("Before "+beforeMap);
System.out.println("After "+afterMap);
List<String> beforeList = getAllKeys(beforeMap);
List<String> afterList = getAllKeys(afterMap);
List<String> commonList1 = beforeList;
List<String> commonList2 = afterList;
List<String> diffList1 = getAllKeys(beforeMap);
List<String> diffList2 = getAllKeys(afterMap);
commonList1.retainAll(afterList);
commonList2.retainAll(beforeList);
diffList1.removeAll(commonList1);
diffList2.removeAll(commonList2);
if(commonList1!=null & commonList2!=null) // athough both the size are same
{
for (int i = 0; i < commonList1.size(); i++)
{
if ((beforeMap.get(commonList1.get(i))).equals(afterMap.get(commonList1.get(i))))
{
System.out.println("Equal: Before- "+ beforeMap.get(commonList1.get(i))+" After- "+afterMap.get(commonList1.get(i)));
}
else
{
System.out.println("Unequal: Before- "+ beforeMap.get(commonList1.get(i))+" After- "+afterMap.get(commonList1.get(i)));
}
}
}
if (CollectionUtils.isNotEmpty(diffList1))
{
for (int i = 0; i < diffList1.size(); i++)
{
System.out.println("Values present only in before map: "+beforeMap.get(diffList1.get(i)));
}
}
if (CollectionUtils.isNotEmpty(diffList2))
{
for (int i = 0; i < diffList2.size(); i++)
{
System.out.println("Values present only in after map: "+afterMap.get(diffList2.get(i)));
}
}
}
/**getAllKeys API adds the keys of the map to a list */
private static List<String> getAllKeys(Map<String, String> map1)
{
List<String> key = new ArrayList<String>();
if (map1 != null)
{
Iterator<String> mapIterator = map1.keySet().iterator();
while (mapIterator.hasNext())
{
key.add(mapIterator.next());
}
}
return key;
}
}
public boolean equalMaps(Map<?, ?> map1, Map<?, ?>map2) {
if (map1==null || map2==null || map1.size() != map2.size()) {
return false;
}
for (Object key: map1.keySet()) {
if (!map1.get(key).equals(map2.get(key))) {
return false;
}
}
return true;
}
If anyone is looking to do it in Java 8 streams below is the example.
import java.util.HashMap;
import java.util.Map;
public class CompareTwoMaps {
public static void main(String[] args) {
Map<String, String> a = new HashMap<>();
a.put("foo", "bar" + "bar");
a.put("zoo", "bar" + "bar");
Map<String, String> b = new HashMap<>();
b.put(new String("foo"), "bar" + "bar");
b.put(new String("zoo"), "bar" + "bar");
System.out.println("result = " + areEqual(a, b));
}
private static boolean areEqual(Map<String, String> first, Map<String, String> second) {
return first.entrySet().stream()
.allMatch(e -> e.getValue().equals(second.get(e.getKey())));
}
}
If you want to compare two Maps then, below code may help you
(new TreeMap<String, Object>(map1).toString().hashCode()) == new TreeMap<String, Object>(map2).toString().hashCode()

Efficient way to get the most used keys in a HashMap - Java

I have a HashMap where the key is a word and the value is a number of occurrences of that string in a text. Now I'd like to reduce this HashMap to only 15 most used words (with greatest numbers of occurrences). Do you have any idea to do this efficiently?
Using an array instead of ArrayList as suggested by Pindatjuh could be better,
public class HashTest {
public static void main(String[] args) {
class hmComp implements Comparator<Map.Entry<String,Integer>> {
public int compare(Entry<String, Integer> o1,
Entry<String, Integer> o2) {
return o2.getValue() - o1.getValue();
}
}
HashMap<String, Integer> hm = new HashMap<String, Integer>();
Random rand = new Random();
for (int i = 0; i < 26; i++) {
hm.put("Word" +i, rand.nextInt(100));
}
ArrayList list = new ArrayList( hm.entrySet() );
Collections.sort(list, new hmComp() );
for ( int i = 0 ; i < 15 ; i++ ) {
System.out.println( list.get(i) );
}
}
}
EDIT reversed sorting order
One way I think of to tackle this, but it's probably not the most efficient, is:
Create an array of hashMap.entrySet().toArray(new Entry[]{}).
Sort this using Arrays.sort, create your own Comparator which will compare only on Entry.getValue() (which casts it to an Integer). Make it order descending, i.e. most/highest first, less/lowest latest.
Iterate over the sorted array and break when you've reached the 15th value.
Map<String, Integer> map = new HashMap<String, Integer>();
// --- Put entries into map here ---
// Get a list of the entries in the map
List<Map.Entry<String, Integer>> list = new Vector<Map.Entry<String, Integer>>(map.entrySet());
// Sort the list using an annonymous inner class implementing Comparator for the compare method
java.util.Collections.sort(list, new Comparator<Map.Entry<String, Integer>>(){
public int compare(Map.Entry<String, Integer> entry, Map.Entry<String, Integer> entry1)
{
// Return 0 for a match, -1 for less than and +1 for more then
return (entry.getValue().equals(entry1.getValue()) ? 0 : (entry.getValue() > entry1.getValue() ? 1 : -1));
}
});
// Clear the map
map.clear();
// Copy back the entries now in order
for (Map.Entry<String, Integer> entry: list)
{
map.put(entry.getKey(), entry.getValue());
}
Use first 15 entries of map. Or modify last 4 lines to put only 15 entries into map
You can use a LinkedHashMap and remove the least recently used items.

Categories

Resources