Related
There is a hash map within the ArrayList. the output is like below
[{A=2},{A=3},{B=1},{B=4},{A=3}]
Below I have mentioned my code sample
ArrayList<Map<String, Short>> deviceInfo = new ArrayList<>();
Map<String, Integer> rssiMapper = new HashMap<>();
rssiMapper.put(device.getName(), rssi);
deviceInfo.add(rssiMapper);
I want to take mean value of A and B separately. How can I achieve that
Try it like this.
List<Map<String,Integer>> list = List.of(
Map.of("A", 2),
Map.of("A", 3),
Map.of("B", 1),
Map.of("B", 4),
Map.of("A", 3));
Map<String, Double> avgs = list.stream()
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.groupingBy(
Entry::getKey,
Collectors.averagingInt(Entry::getValue)));
System.out.println(avgs);
Prints
{A=2.6666666666666665, B=2.5}
As was suggested, if you are unfamiliar with streams, here is an iterative approach.
Map<String,Double> avgs = new HashMap<>();
Map<String,Integer> count = new HashMap<>();
for (Map<String,Integer> map : list) {
for (Entry<String,Integer> e : map.entrySet()) {
String key = e.getKey();
int value = e.getValue();
// These just either initialize or update the appropriate
// values.
avgs.compute(key, (k,v)-> v == null ? value : v + value);
count.compute(key, (k,v)->v == null ? 1 : v + 1);
}
}
// now find the averages.
for(Entry<String,Double> e : avgs.entrySet()) {
avgs.computeIfPresent(e.getKey(), (k,v)->v/count.get(e.getKey()));
}
System.out.println(avgs);
You can create maps to track the sum, count and the mean (i.e. sum / count) of the entries as shown below:
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class Main {
public static void main(String[] args) {
List<Map<String, Integer>> list = List.of(Map.of("A", 2), Map.of("A", 3), Map.of("B", 1), Map.of("B", 4),
Map.of("A", 3));
Map<String, Integer> sumMap = new HashMap<>();
Map<String, Integer> countMap = new HashMap<>();
Map<String, Double> meanMap = new HashMap<>();
for (Map<String, Integer> map : list) {
for (Entry<String, Integer> entry : map.entrySet()) {
sumMap.put(entry.getKey(), sumMap.getOrDefault(entry.getKey(), 0) + entry.getValue());
countMap.put(entry.getKey(), countMap.getOrDefault(entry.getKey(), 0) + 1);
meanMap.put(entry.getKey(),
(double) sumMap.getOrDefault(entry.getKey(), 0) / countMap.getOrDefault(entry.getKey(), 1));
}
}
// Display
System.out.println(meanMap);
}
}
Output:
{A=2.6666666666666665, B=2.5}
I want to add list elements to corresponding keys but the problem I am facing is that values which are added already to different key is also added.
for every iteration I am adding key and values
like 1st iteration:
Hashmap added {A=[1,2]}
2nd iteration
Hashmap added {A=[1,2,3,4]}---- here I don't want to add 1,2-only new elements should be added
expected here is {A=[3,4]}
Map<String, List<String>> country_hashmap = new HashMap<String, List<String>>();
Map<String, List<String>> country_hashmaperor = new HashMap<String, List<String>>();
List<String> warningJsinList = new ArrayList<>();
List<String> errorList = new ArrayList<>();
for(int i =0;i<totalMessages;i++){
int totalMessageperElement = 5;
for(int j =0;j<totalMessageperElement;j++){
if(severityValue.equalsIgnoreCase("Warning")){
warningJsinList.add(message[i]).toString().replace("[", "").replace("]", "").replace("\"", ""));
if(!(j + 1 < totalMessageperElement)) {
country_hashmap.put(elementkey, warningJsinList);
}
}
else{
errorList.add(message[i]).toString().replace("[", "").replace("]", "").replace("\"", ""));
if(!(j + 1 < totalMessageperElement)) {
country_hashmaperor.put(elementkey, errorList);
}
}
}
Remove the lines with System.out.{print,println}, I've only added just to illustrate the functionality:
public class App {
public static void main(String[] args) {
Map<String, List<String>> map = new HashMap<>();
add(map, "A", Stream.of("1", "2").collect(toList()));
add(map, "A", Stream.of("1", "2", "3", "4").collect(toList()));
add(map, "A", Stream.of("4", "5", "6").collect(toList()));
}
private static void add(Map<String, List<String>> map, String key, List<String> newValues) {
System.out.print("Map before: " + map + ", adding: " + newValues + " for key " + key);
List<String> oldValues = map.getOrDefault(key, new ArrayList<>());
newValues.removeAll(oldValues);
map.put(key, newValues);
System.out.println(", map after: " + map);
}
}
Output:
Map before: {}, adding: [1, 2] for key A, map after: {A=[1, 2]}
Map before: {A=[1, 2]}, adding: [1, 2, 3, 4] for key A, map after: {A=[3, 4]}
Map before: {A=[3, 4]}, adding: [4, 5, 6] for key A, map after: {A=[5, 6]}
Not recommended, but if you're certain that you need a data structure that's XORing (remove if existing, add if missing), you could do this:
static class XorList<T> extends ArrayList<T> {
#Override
public boolean addAll(Collection<? extends T> c) {
c.forEach(this::add);
return true;
}
#Override
public boolean add(T t) {
return this.contains(t) ? this.remove(t) : super.add(t);
}
}
Map<String, XorList<String>> map1 = new HashMap<>();
map1.computeIfAbsent("A", (key) -> new XorList<>()).addAll(Stream.of("1", "2").collect(toList()));
System.out.println(map1);
map1.computeIfAbsent("A", (key) -> new XorList<>()).addAll(Stream.of("1", "2", "3", "4").collect(toList()));
System.out.println(map1);
map1.computeIfAbsent("A", (key) -> new XorList<>()).addAll(Stream.of("4", "5", "6").collect(toList()));
System.out.println(map1);
Output for this one is:
{A=[1, 2]}
{A=[3, 4]}
{A=[3, 5, 6]}
By the way, you probably need to use a Set instead of List, isn't it?
How to find most frequent element, but when there are few most frequent element return null.
I would like to find code equivalent of:
public static void main(String[] args) {
System.out.println("Should return A -> " + mostFrequent(Arrays.asList("A", "A", "B")));
System.out.println("Should null as element in list have same frequency -> "
+ mostFrequent(Arrays.asList("A", "B")));
}
private static String mostFrequent(List<String> elements) {
Map<String, Long> ordered = new TreeMap<>();
for (String e : elements) {
if (!ordered.containsKey(e)) {
ordered.put(e, 0L);
}
Long tmp = ordered.get(e);
ordered.put(e, ++tmp);
}
String mostFrequent = null;
long i = 0;
Iterator<Map.Entry<String, Long>> it = ordered.entrySet().iterator();
while (it.hasNext() && i < 2) {
Map.Entry<String, Long> pair = it.next();
if (i == 0) {
mostFrequent = pair.getKey();
} else {
if (ordered.get(mostFrequent) == ordered.get(pair.getKey())) {
return null;
}
}
i++;
}
return mostFrequent;
}
However stream version does not handle most frequent elements with the same frequency.
private static String mostFrequentStream(List<String> elements) {
return elements.stream()
.reduce(BinaryOperator.maxBy(
Comparator.comparingInt(o -> Collections.frequency(elements, o))))
.orElse(null);
}
How to modify stream above to achieve it?
using groupingBy:
String mostFrequentStream(List<String> elements) {
Map<String, Long> temp = elements.stream()
.collect(Collectors.groupingBy(a -> a, Collectors.counting()));
return new HashSet<>(temp.values()).size() < temp.size() ?
null : temp.entrySet()
.stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey).get();
}
I managed to build a concatenated Stream but it got long:
private static String mostFrequentStream3(List<String> elements) {
return elements.stream() // part 1
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet().stream() // part 2
.collect(Collectors.groupingBy(Entry::getValue))
.entrySet().stream() // part 3
.max(Entry.comparingByKey())
.map(Entry::getValue)
.filter(v -> v.size() == 1)
.map(v -> v.get(0).getKey())
.orElse(null);
}
To "find most frequent element, but when there are few most frequent element return null"
Part 1 counts the frequency of every element.
Part 2 groups entries by frequency.
Part 3 looks up the entry with the highest frequency. If this entry does only have one element ("there are few most frequent"), then it's the one and only maximum. Otherwise null is returned.
I would never use stream for this to avoid hurting readability and performance at the same time. For the sake of fun -
private static String mostFrequentStream(List<String> elements) {
Map<String, Long> frequencyMap = elements.stream().collect(groupingBy(Function.identity(), counting()));
return frequencyMap.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.limit(2).reduce((i, e) -> i.getValue().equals(e.getValue()) ? new AbstractMap.SimpleEntry<>(null, 0L) : i).get().getKey();
}
I want to get the person having highest values from all the tables. Below is the example which i retrieve from db
Id play(count) listen(count) display(count) comment(count)
a 3 1 4 2
b 2 5 3 7
c 6 3 0 1
d 0 0 5 4
e 6 4 8 9
f 4 2 5 7
in this all counts related to same id but coming from different tables.Here I want e(6,4,8,9) as sorting output. How can i do this sorting?
Try something like this:
Map<String, List<Integer>> map = new LinkedHashMap<>();
// Assuming that you are working with query output resultset
try {
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
List<Integer> li = new ArrayList<>();
li.add(rs.getInt("PLAY"));
li.add(rs.getInt("LISTEN"));
li.add(rs.getInt("DISPLAY"));
li.add(rs.getInt("COMMENT"));
map.put(rs.getString("ID"), li);
}
} catch (SQLException e ) {
/* SQLException handler */
} finally {
if (stmt != null) { stmt.close(); }
}
map = sortByValues(map);
for (Map.Entry<String, List<Integer>> entry: map.entrySet()) {
System.out.println(entry.getKey() + "," + entry.getValue());
}
}
public static Map<String, List<Integer>> sortByValues(Map<String, List<Integer>> map) {
List<Map.Entry<String, List<Integer>>> list =
new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String, List<Integer>>>() {
public int compare(Map.Entry<String, List<Integer>> m1, Map.Entry<String, List<Integer>> m2) {
int sum1 = 0;
for(Integer d : m1.getValue())
sum1 += d;
int sum2 = 0;
for(Integer d : m2.getValue())
sum2 += d;
return (new Integer(sum2)).compareTo(new Integer(sum1));
}
}) ;
Map<String, List<Integer>> result = new LinkedHashMap<>();
for (Map.Entry<String, List<Integer>> entry: list) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
Note: I have a separate method sortByValues() to do the comparison. This makes code neat to read and reusable rather than implementing an anonymous block after try-catch.
What's happening here?
Our aim is to sort IDs by different values fetched from Database. I feel Map data structure to be the apt one here. Example Map which is represented in a generic way as Map<String, List<Integers>>.
Map<String, List<Integer>> map = new LinkedHashMap<>();
Creates the linked hash map to store the pair
List<Integer> li = new ArrayList<>();
Creates an Arraylist object inside while loop to store each value fetched from database. It's scope dies within this loop.
map.put(id, li);
Adds each users id and values in the format Map<String, List<Integer>>
map = sortByValues(map);
Accesses the static sortByValues() to fetch the sorted map based on values it has.
sortByValues(Map<String, List<Integer>> map)
Overrides the Comparator's compare() anonymously and performs sorting based upon values. It sums up each ID's value and does the comparison.
Mock Execution:
Map<String, List<Integer>> map = new LinkedHashMap<>();
// Map<String, List<Integer>> map = new HashMap<>();
List<Integer> li = new ArrayList<>();
li.add(1);
li.add(2);
li.add(3);
// MathUtils.sum()
map.put("a", li);
// map.put("a", 5);
List<Integer> li2 = new ArrayList<>();
li2.add(3);
li2.add(-1);
li2.add(1);
map.put("b", li2);
List<Integer> li3 = new ArrayList<>();
li3.add(10);
li3.add(-1);
li3.add(9);
map.put("c", li3);
map = sortByValues(map);
for (Map.Entry<String, List<Integer>> entry: map.entrySet()) {
System.out.println(entry.getKey() + "," + entry.getValue());
}
Result:
c,[10, -1, 9]
a,[1, 2, 3]
b,[3, -1, 1]
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);
}