Ive created a map with keys of type integer and values are Sets of Strings. I have populated the map with some test data, and now need to write a method that prints out the contents of the map like "key: value, value, value"
Im assuming iterating through the map, and assigning the keys to a int variable and printing these out is how to start, but how would I then go about printing the values in the set of strings?
public class HandicapRecords {
private Map<Integer, Set<String>> handicapMap;
public HandicapRecords() {
handicapMap = new HashMap<>();
}
public void handicapMap() {
Set<String> players = new HashSet<>();
players.add("Michael");
players.add("Roger");
players.add("Toby");
handicapMap.put(10, players);
players = new HashSet<>();
players.add("Bethany");
players.add("Martin");
handicapMap.put(16, players);
players = new HashSet<>();
players.add("Megan");
players.add("Declan");
handicapMap.put(4, players);
}
public void printMap() {
//code for method to go here
}
}
You can iterate on a Set data structure just as you could in a list (well, actually list preserves the order, whereas the set does not but I presume that that would go beyond the scope of this question).
To print the data, you could do the following:
for (Integer num : handicapMap.keySet()) {
System.out.print("Key : " + String.valueOf(num) + " Values:");
for (String player : handicapMap.get(num)) {
System.out.print(" " + player + " ");
}
System.out.println();
}
You gave use nested for-each loop. We cant directly iterate through HashMao, take the keySet and print.
Example:
public void printMap()
{
Set<Integer> keys=handicapMap.keySet();
for(Integer k:keys)
{
Set<String> players=handicapMap.get(k);
System.out.print(" "+k+":");
int i=0;
for(String p:players)
{
i++;
System.out.print(p);
if(i!=players.size())
System.out.print(",");
}
System.out.println();
}
}
I guess you won't know keys, so you have to iterate over all entries in hash map:
for (Map.Entry<Integer, Set<String>> entry : handicapMap.entrySet())
{
Integer key = entry.getKey();
HashSet<String> values = entry.getValue();
for (String s : values) {
// and now do what you need with your collection values
}
}
Related
My program calculates the digit sums of all values entered into a text file. The entered values and their according digit sums are stored in two seperate ArrayLists.
Both ArrayLists are combined into a LinkedHashMap in the end which should be ordered by the digit sum in descending order.
If you enter multiple values with the same digit sum, it's supposed to order those (and only those) in descending order by their original value, not the digit sum this time (the rest stays the same as before).
How do I achieve this with Comparators?
My lists and the map:
String filePath = args[0];
LineNumberReader br = new LineNumberReader(new FileReader(filePath));
LinkedHashMap<BigInteger, BigInteger> unsortedMap = new LinkedHashMap<BigInteger, BigInteger>();
List<BigInteger> inputList = new ArrayList<>();
List<BigInteger> DSList = new ArrayList<>();
if(br.ready()){
while (true) {
String line = br.readLine();
if (line == null) {
break;
}
BigInteger input = new BigInteger(line);
inputList.add(input);
DSList.add(methods.digitSum(input));
}
}
for(int i = 0; i < inputList.size(); i++){
unsortedMap.put(inputList.get(i), DSList.get(i));
}
for(BigInteger key : unsortedMap.keySet()){
System.out.println(new BigDecimal(key).toPlainString() + " (Digit Sum: " + unsortedMap.get(key) + (")"));
}
methods.digitSum:
public static BigInteger digitSum(BigInteger number) {
String digits = number.toString();
int sum = 0;
for(int i = 0; i < digits.length(); i++) {
int digit = (int) (digits.charAt(i) - '0');
sum = sum + digit;
}
return BigInteger.valueOf(sum);
}
Output has to look like this:
x (digit sum: y)
x (digit sum: y)
...
x = entered value
y = digit sum of x
If you need any further information, feel free to ask.
Here is a solution with a simple class and a Comparator
class Values {
BigInteger number;
BigInteger digitSum;
Values(BigInteger number, BigInteger sum) {
this.number = number;
this.digitSum = sum;
}
#Override
public String toString() {
return number + " (digit sun:" + digitSum + ")";
}
}
And then create a list with this class
List<Values> inputList = new ArrayList<>();
and add objects of Values to the list using the constructor when reading the file
For sorting you can create a Comparator object like this
Comparator<Values> compareSum = (Values v1, Values v2) -> {
int result = v1.digitSum.compareTo(v2.digitSum);
return result != 0 ? result : v1.number.compareTo(v2.number);
};
and sort your list in descending order
inputList.sort(compareSum.reversed());
As there are duplicate keys with different values, you cannot use LinkedHashMap or any other Java Map implementations to store the same key with different value.
You can either create your custom Map or use Apache's Common Map Implementations. The following code shows a solution using MultiValueMap and a custom comparator that sorts by key and then by value in descending order:
MultiValuedMap<BigInteger, BigInteger> unsortedMap = new ArrayListValuedHashMap<>();
if(br.ready()){
while (true) {
String line = br.readLine();
if (line == null) {
break;
}
//Use a single map to put both values
unsortedMap.put(new BigInteger(line), new BigInteger(methods.digitSum(line)));
}
}
//Do the sorting using custom comparator
List<Map.Entry<BigInteger, BigInteger>> list = new LinkedList<>(unsortedMap.entries());
// Sort the list
Collections.sort(list, new Comparator<Map.Entry<BigInteger, BigInteger> >() {
public int compare(Map.Entry<BigInteger, BigInteger> o1,
Map.Entry<BigInteger, BigInteger> o2) {
int c = o1.getKey().compareTo(o2.getKey());
if (c==0){
c = o1.getValue().compareTo(o2.getValue());
}
return c * -1; //descending
}
});
//Place the sorted List into another Map that retains the insert order
Multimap<BigInteger, BigInteger> sortedMap = LinkedHashMultimap.create();
for (Map.Entry<BigInteger, BigInteger> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
//Print
for(BigInteger key : sortedMap.keySet()){
for (BigInteger bi: sortedMap.get(key)) {
System.out.println("x: " + new BigDecimal(key).toPlainString() + " (Digit Sum: " + bi + (")"));
}
}
To use the above Maps you should include the apache commons collections dependency
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
You can refer to below code to sort your map by value in descending order (in case of same values, sort in descending order of keys). In your case, the keys of the map (passed in parameter) are the inputs from text file and respective values in map are the digit sum.
public static LinkedHashMap<BigInteger, BigInteger> sortByValue(LinkedHashMap<BigInteger, BigInteger> hm)
{
List<Map.Entry<BigInteger, BigInteger> > list =
new LinkedList<Map.Entry<BigInteger, BigInteger> >(hm.entrySet());
Collections.sort(list, new Comparator<Map.Entry<BigInteger, BigInteger> >() {
public int compare(Map.Entry<BigInteger, BigInteger> o1,
Map.Entry<BigInteger, BigInteger> o2)
{
// To handle same value case
if(o1.getValue().compareTo(o2.getValue()) == 0) {
return (o2.getKey()).compareTo(o1.getKey());
}
return (o2.getValue()).compareTo(o1.getValue());
}
});
LinkedHashMap<BigInteger, BigInteger> temp = new LinkedHashMap<BigInteger, BigInteger>();
for (Map.Entry<BigInteger, BigInteger> aa : list) {
temp.put(aa.getKey(), aa.getValue());
}
return temp;
}
The compareTo method is called on object o2 rather than object o1 to get the result in descending order. As mentioned in comment of above code snippet, the if condition will handle your requirement of comparing the keys and sort in descending order of keys when the values are same.
The list of Entry objects is sorted as as per your requirement and these sorted Entry objects are added to LinkedHashMap to maintain the insertion order into the map.
Does exist any collection which stores only unique strings, but count how many times this string was added?
So every time, when I try add the same string/item again, number of items remain the same but numbers of occurrence of given item will increase?
You can use a HashMap and wrap some code around it:
public class CounterMap<K> {
private final Map<K, Integer> internalMap = new HashMap<K, Integer>();
public void increment(K key) {
initKeyIfNew(key);
Integer oldValue = internalMap.get(key);
Integer newValue = oldValue + 1;
internalMap.put(key, newValue);
}
public int getCount(K key) {
initKeyIfNew(key);
return internalMap.get(key);
}
private void initKeyIfNew(K key) {
if (internalMap.get(key) == null) {
internalMap.put(key, 0);
}
}
}
Then you can use it like this:
CounterMap<String> myCounterMap = new CounterMap<String>();
myCounterMap.increment("hello");
...
As far as I know, there is not such build in collections, but you can simply achieve that by using Map collection:
Map<String, Integer> map = new HashMap<>();
String sample = "foo";
if (map.containsKey(sample))
map.put(sample, map.get(sample) + 1);
You can also use solution from external library, for example Multiset from Google Guava:
Multiset<String> multiset = HashMultiset.create();
String test = "foo";
multiset.add(test);
multiset.add(test);
multiset.add(test);
System.out.println(multiset.count(test));
with output:
3
Hope it helps.
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class UniqueStringCount {
public static void main(String[] args) {
boolean takeUserInput = true;
HashMap<String, Integer> uniqueStringMap = new HashMap<>();
int counter = 0;
System.out.println("Welcome. To close the program type exit.");
System.out.println();
do {
Scanner scan = new Scanner(System.in);
System.out.println("Enter the unique string");
String userInput = scan.next();
if(userInput.equalsIgnoreCase("exit")) {
takeUserInput = false;
scan.close();
}
System.out.println();
if(!userInput.equalsIgnoreCase("exit")) {
if(uniqueStringMap.containsKey(userInput)) {
counter = uniqueStringMap.get(userInput);
uniqueStringMap.put(userInput, ++counter);
continue;
}
counter = 0;
uniqueStringMap.put(userInput, ++counter);
}
} while(takeUserInput);
if(!uniqueStringMap.isEmpty()) {
for(Map.Entry<String, Integer> entry : uniqueStringMap.entrySet()) {
System.out.println("String " + entry.getKey() + " was added " + entry.getValue() + " times.");
System.out.println();
}
System.out.println("Bye bye.");
}
}
}
With Java 8:
Map<String, Integer> map = new HashMap<>();
When adding a string, do:
map.merge(s, 1, Integer::sum);
What this does is add the string s and set the value to 1 if it wasn't there yet. If it was there already, then it takes the current value and the new value you're adding (1 again) and sums them, and puts that back into the map.
Problem
Data is in the format:
Map<String, Map<String, Integer>>
Which looks something like this:
{"MilkyWay": {"FirstStar" : 3, "SecondStar" : 9 .... }, "Andromeda": {"FirstStar" : 10, "SecondStar" : 9 .... } }
I want to compare the Star values in a quick loop, so I'd like to compare the integer value of FirstStar in MilkyWay and Andromeda and have it return true or falseif the values are the same or not. Since this Map of Maps is huge.
My Attempt
I'd like to do it something like:
//GalaxyMap<String, <Map<String, Integer>>
for (Map<String, Integer> _starMap : GalaxyMap.values())
{
for (String _starKey : _starMap.keySet()){
//Can't quite think of the correct logic... and I'm tired...
}
}
I'd like to keep it as short as possible... I've been staring at this for a while and I'm going in circles with it.
EDIT
Outer keys differ, Inner keys are the same
Also since this is a response from a server, I don't know the size it's going to be
Why does this need to be a map. If you're always using "FirstStar", "SecondStar" etc, as your keys, then why not make it a list instead..
Map<String, List<Integer>>
Then you can do something like:
public boolean compareGalaxyStar(String galaxyName, String otherGalaxyName, int star) {
List<Integer> galaxyStars = galaxyMap.get(galaxyName);
List<Integer> otherGalaxyStars = galaxyMap.get(otherGalaxyName);
return galaxyStars.get(star) == otherGalaxyStars.get(star);
}
NOTE: You need to do some validation to make sure the input is correct.
To implement this for all stars, it is not much different.
if(galaxyStars.size() == otherGalaxyStars.size()) {
for(int x = 0; x < galaxyStars.size(); x++) {
// Perform your comparisons.
if(galaxyStars.get(x) != otherGalaxyStars.get(x)) {
// Uh oh, unequal.
return false;
}
}
}
If the structure of the inner maps also could differ, you should do something like that:
static boolean allStarValuesEqual(Map<String, Map<String, Integer>> galaxies) {
Map<String, Integer> refStars = null;
for (Map<String, Integer> galaxy : galaxies.values()) {
if (refStars == null) {
refStars = galaxy;
} else {
for (Entry<String, Integer> currentStar : galaxy.entrySet()) {
if (!currentStar.getValue().equals(refStars.get(currentStar.getKey()))) {
return false;
}
}
}
}
return true;
}
Please check below program along with output:
package com.test;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class CompareMapValues {
private final static String FS = "FirstStar";
private final static String SS = "SecondStar";
private final static String MW = "MilkyWay";
private final static String A = "Andromeda";
public static void main(String[] args) {
Map> map = new HashMap>();
Map innerMap1 = new HashMap();
innerMap1.put(FS, 3);
innerMap1.put(SS, 9);
Map innerMap2 = new HashMap();
innerMap2.put(FS, 10);
innerMap2.put(SS, 9);
map.put(MW, innerMap1);
map.put(A, innerMap2);
Set set = map.keySet();
for(String s: set) {
Map outerMap = map.get(s);
Set set2 = map.keySet();
for(String s2: set2) {
Map innerMap = map.get(s2);
if(!s2.equals(s)) {
Set set3 = outerMap.keySet();
for(String s3: set3) {
int i1 = outerMap.get(s3);
Set set4 = innerMap.keySet();
for(String s4: set4) {
int i2 = innerMap.get(s3);
if(s3.equals(s4) && i1==i2) {
System.out.println("For parent " + s + " for " + s3 + " value is " + i1);
}
}
}
}
}
}
}
}
//Output:
//For parent Andromeda for SecondStar value is 9
//For parent MilkyWay for SecondStar value is 9
Hope this helps.
I'm creating an educational game for young students who needs to learn the most common words. On random I pick three words of the list, show them on the screen, play an audio recording of one of the three words and then the student has to pick the word that has been pronounced. I keep track of how many times they have guessed each word. In that way I can set up a criteria for when new words should be introduced to the student. When three of the words are picked I'll like to pronounce the word that the student has had least exposure to.
I have a HashMap called words, which contains the words, and a integer value of how many times the student guessed the word.
HashMap<String,Integer> words
It contains between 10 - 120 keys/words. I'll like to create a method, which takes three of the hash map keys as parameters, that can return the String/key having the lowest value of the keys asked for.
I have had trouple getting this to work as intended and I'd appreciate any help.
Possibly the shortest solution, with Java 8:
private String getMinKey(Map<String, Integer> map, String... keys) {
return map.entrySet().stream()
.filter(p -> Arrays.asList(keys).contains(p.getKey()))
.min(Comparator.comparingInt(Map.Entry::getValue)).get().getKey();
}
What about this?
private String getMinKey(Map<String, Integer> map, String... keys) {
String minKey = null;
int minValue = Integer.MAX_VALUE;
for(String key : keys) {
int value = map.get(key);
if(value < minValue) {
minValue = value;
minKey = key;
}
}
return minKey;
}
First get the entry set from the map:
Set<Entry<String,Integer>> entries = map.entrySet();
Now dump that into an ArrayList so you can sort it:
List<Entry<String,Integer>> sortedEntries = new ArrayList<>(entries);
Now sort the list:
Collections.sort(sortedEntries, /*Define your comparitor here to compare by values */);
Your list now has the contents of the map sorted by value, you can access them in whatever order you like.
This is a variation of the answer of user3309578
static HashMap words = new HashMap();
private static String getMax () {
String minKey = null;
int minValue = Integer.MAX_VALUE;
for (String key : words.keySet()) {
int value = words.get(key);
if (value < minValue) {
minValue = value;
minKey = key;
}
}
return minKey;
}
public static void main (String[] args) {
words.put("a", 2);
words.put("b", 4);
words.put("c", 6);
words.put("d", 8);
words.put("e", 1);
words.put("f", 3);
words.put("g", 5);
words.put("h", 7);
System.out.println(getMax());
}
i made this, it can hold multiple keys=
HashMap<String,Integer>hashmap_original=new HashMap<>();
hashmap_original.put("key_1",1);
hashmap_original.put("key_2",4);
hashmap_original.put("key_3",1);
hashmap_original.put("key_4",3);
HashMap<String,Integer>hashmap_result=new HashMap<>();
int int_minium_value = 9999; //put a maxium value that u know your code it wont pass it
for (String String_key:hashmap_original.keySet()) {
int int_compare_value=hashmap_original.get(String_key); //get the value
if (int_compare_value<int_minium_value) {
int_minium_value=int_compare_value;
hashmap_result.clear(); //delete non min values
hashmap_result.put(String_key,int_compare_value);
} else if (int_compare_value==int_minium_value) {
hashmap_result.put(String_key,int_compare_value);
}
}
String result=null;//u can use a ArrayList
for (String key_with_the_lowest_value : hashmap_result.keySet()) {
if (result == null) {
result = key_with_the_lowest_value;
} else {
result=result+","+key_with_the_lowest_value;
}
}
I have a hashmap of the following type
HashMap<String,ArrayList<Integer>> map=new HashMap<String,ArrayList<Integer>>();
The values stored are like this :
mango | 0,4,8,9,12
apple | 2,3
grapes| 1,7
peach | 5,6,11
I want to store as well as fetch those Integers using Iterator or any other way with minimum lines of code.How can I do it?
EDIT 1
The numbers are added at random (not together) as key is matched to the appropriate line.
EDIT 2
How can I point to the arraylist while adding ?
I am getting error while adding a new number 18 in the line map.put(string,number);
Our variable:
Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();
To store:
map.put("mango", new ArrayList<Integer>(Arrays.asList(0, 4, 8, 9, 12)));
To add numbers one and one, you can do something like this:
String key = "mango";
int number = 42;
if (map.get(key) == null) {
map.put(key, new ArrayList<Integer>());
}
map.get(key).add(number);
In Java 8 you can use putIfAbsent to add the list if it did not exist already:
map.putIfAbsent(key, new ArrayList<Integer>());
map.get(key).add(number);
Use the map.entrySet() method to iterate on:
for (Entry<String, List<Integer>> ee : map.entrySet()) {
String key = ee.getKey();
List<Integer> values = ee.getValue();
// TODO: Do something.
}
The modern way (as of 2020) to add entries to a multimap (a map of lists) in Java is:
map.computeIfAbsent("apple", k -> new ArrayList<>()).add(2);
map.computeIfAbsent("apple", k -> new ArrayList<>()).add(3);
According to Map.computeIfAbsent docs:
If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null.
Returns:
the current (existing or computed) value associated with the specified key, or null if the computed value is null
The most idiomatic way to iterate a map of lists is using Map.forEach and Iterable.forEach:
map.forEach((k, l) -> l.forEach(v -> /* use k and v here */));
Or, as shown in other answers, a traditional for loop:
for (Map.Entry<String, List<Integer>> e : map.entrySet()) {
String k = e.getKey();
for (Integer v : e.getValue()) {
/* use k and v here */
}
}
Iterator it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pairs = (Map.Entry)it.next();
if(pairs.getKey().equals("mango"))
{
map.put(pairs.getKey(), pairs.getValue().add(18));
}
else if(!map.containsKey("mango"))
{
List<Integer> ints = new ArrayList<Integer>();
ints.add(18);
map.put("mango",ints);
}
it.remove(); // avoids a ConcurrentModificationException
}
EDIT:
So inside the while try this:
map.put(pairs.getKey(), pairs.getValue().add(number))
You are getting the error because you are trying to put an integer to the values, whereas it is expected an ArrayList.
EDIT 2:
Then put the following inside your while loop:
if(pairs.getKey().equals("mango"))
{
map.put(pairs.getKey(), pairs.getValue().add(18));
}
else if(!map.containsKey("mango"))
{
List<Integer> ints = new ArrayList<Integer>();
ints.add(18);
map.put("mango",ints);
}
EDIT 3:
By reading your requirements, I come to think you may not need a loop. You may want to only check if the map contains the key mango, and if it does add 18, else create a new entry in the map with key mango and value 18.
So all you may need is the following, without the loop:
if(map.containsKey("mango"))
{
map.put("mango", map.get("mango).add(18));
}
else
{
List<Integer> ints = new ArrayList<Integer>();
ints.add(18);
map.put("mango", ints);
}
You can use like this(Though the random number generator logic is not upto the mark)
public class WorkSheet {
HashMap<String,ArrayList<Integer>> map = new HashMap<String,ArrayList<Integer>>();
public static void main(String args[]) {
WorkSheet test = new WorkSheet();
test.inputData("mango", 5);
test.inputData("apple", 2);
test.inputData("grapes", 2);
test.inputData("peach", 3);
test.displayData();
}
public void displayData(){
for (Entry<String, ArrayList<Integer>> entry : map.entrySet()) {
System.out.print(entry.getKey()+" | ");
for(int fruitNo : entry.getValue()){
System.out.print(fruitNo+" ");
}
System.out.println();
}
}
public void inputData(String name ,int number) {
Random rndData = new Random();
ArrayList<Integer> fruit = new ArrayList<Integer>();
for(int i=0 ; i<number ; i++){
fruit.add(rndData.nextInt(10));
}
map.put(name, fruit);
}
}
OUTPUT
grapes | 7 5
apple | 9 5
peach | 5 5 8
mango | 4 7 1 5 5
You could try using MultiMap instead of HashMap
Initialising it will require fewer lines of codes. Adding and retrieving the values will also make it shorter.
Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();
would become:
Multimap<String, Integer> multiMap = ArrayListMultimap.create();
You can check this link: http://java.dzone.com/articles/hashmap-%E2%80%93-single-key-and
Method1 : Use putIfAbsent
Map<String, List<Integer>> map = new HashMap();
map.putIfAbsent("mango",new ArrayList<>());
map.get("mango").add(5);
Method 2: Check key present in Map
Map<String, List<Integer>> map = new HashMap();
if(! map.containsKey("mango"){
map.put("mango",new ArrayList<>());
}
List<Integer> list = map.get("mango");
list.add(3);
Method 3: Use getOrDefault
Map<String, List<Integer>> map = new HashMap();
List<Integer> list = map.getOrDefault("mango",new ArrayList<>());
list.add(4)
for (Map.Entry<String, ArrayList<Integer>> entry : map.entrySet()) {
System.out.println( entry.getKey());
System.out.println( entry.getValue());//Returns the list of values
}
static HashMap<Integer, ArrayList<Integer>> has(int arr[], int target) {
HashMap<Integer, ArrayList<Integer>> hm = new HashMap<Integer, ArrayList<Integer>>();
for (int i = 0; i < arr.length; i++) {
if (!hm.containsKey(arr[i])) {
ArrayList<Integer> res = new ArrayList<Integer>();
res.add(i + 1);
hm.put(arr[i], res);
} else {
hm.get(arr[i]).add(i);
}
}
return hm;
}
Fetch all at once =
List<Integer> list = null;
if(map!= null)
{
list = new ArrayList<Integer>(map.values());
}
For Storing =
if(map!= null)
{
list = map.get(keyString);
if(list == null)
{
list = new ArrayList<Integer>();
}
list.add(value);
map.put(keyString,list);
}