What is the best / most efficient way to find the total number of Values in a HashMap.
I do not mean the .size() method as it counts the number of keys. I want the total number of values in all the keys.
I want to do so as my key is a String, but my value is a List.
In Java 8, you can also utilize the Stream API:
int total = map.values()
.stream()
.mapToInt(List::size) // or (l -> l.size())
.sum()
This has the advantage that you don't have to repeat the List<Foo> type for a for variable, as in the pre-Java 8 solution:
int total = 0;
for (List<Foo> list : map.values())
{
total += list.size();
}
System.out.println(total);
In addition to that, although not advised, you could also use that value inline without needing a temp variable:
System.out.println(map.values().stream().mapToInt(List::size).sum());
Easiest would be, iterate and add over list sizes.
int total = 0;
for (List<Foo> l : map.values()) {
total += l.size();
}
// here is the total values size
Starting with Java 8, given a Map<K, List<V>> map you could use the Stream API and have:
int size = map.values().stream().mapToInt(List::size).sum();
This creates a Stream of the values with stream(), maps each of them to their size with mapToInt, where the mapper is the method reference List::size refering to List#size(), and sum the results with sum().
Say you have a map
Map<String, List<Object>> map = new HashMap<>();
You can do this by calling the values() method and calling size() method for all the lists:
int total = 0;
Collection<List<Object>> listOfValues = map.values();
for (List<Object> oneList : listOfValues) {
total += oneList.size();
}
If I understand the question correctly, you have a Map<String, List<Something>>, and you want to count the total number of items in all the Lists in the Map's values. Java 8 offers a pretty easy way of doing this by streaming the values, mapping them to their size() and then just summing them:
Map<String, List<Something>> map = ...;
int totalSize = map.values().stream().mapToInt(List::size).sum());
With Eclipse Collections, the following will work using MutableMap.
MutableMap<String, List<String>> map =
Maps.mutable.of("key1", Lists.mutable.of("a", "b", "c"),
"key2", Lists.mutable.of("d", "e", "f", "g"));
long total = map.sumOfInt(List::size);
Note: I am a committer for Eclipse Collections.
import java.util.HashMap;
public class Solution {
public static void main(String args[]) {
int total = 0;
HashMap<String,String> a = new HashMap<String,String>();
a.put("1.","1");
a.put("2.","11");
a.put("3.","21");
a.put("4.","1211");
a.put("5.","111221");
for (String l : a.values()) {
total ++;
}
System.out.println(total);
}
}
Related
How to set list of chars and list of integers into one list? I have two lists which I took from hash map and now I have to format it into pretty view. After formatting I have two lists one of integers and other one of characters. Is it possible to put this values into one array list
private List<Character> convertToList(){
Set<Character> mapKeyToList = output().keySet();
List<Character> keys = new ArrayList<>(mapKeyToList);
return keys;
}
private List<Integer> convertInts(){
Collection<Integer> values = output().values();
List<Integer> quantity = new ArrayList<>(values);
return quantity;
}
Example of output :
"char" - int
"char" - int
"char" - int
You can get the entrySet from your map and get in new ArrayList
List<Entry<Character, Integer>> res = new ArrayList<>(output().entrySet());
You can get key and value using .getKey() and .getValue()
for (Entry<Character, Integer> entry : res) {
Character c = entry.getKey();
Integer i = entry.getValue();
// formatting
}
Here you can directly use resultMap.entrySet() instead of res in loop also.
Assuming that both lists are the same size (which they should be, if they are created from the same map), you can just use a for loop to pull elements from each list and populate the new list.
List<String> newList = new ArrayList<>();
for (int i = 0; i < ints.size(); i++) {
newList.add(String.format("%s - %d", chars.get(i), ints.get(i)));
}
Of course, you could also loop over the map and do this with the Streams API if you are using Java 8 or later.
List<String> result = values.entrySet().stream()
.map(x -> String.format("%s - %d", x.getKey(), x.getValue()))
.collect(Collectors.toList());
I have an array of integers and I want to know which number has been repeated more than the others.If we have two numbers which repeated same my program should return the number with min index.
For example : I have an array list which save below nums.
ArrayList<Integer> list = new ArrayList<>();
list.add(9);
list.add(0);
list.add(1);
list.add(1);
list.add(9);
so I created Hashmap to save the number as key and its frequency as Value
HashMap<Integer, Integer> list1 = new HashMap<>();
for (int i = 0; i < list.size(); i++) {
list1.put(list.get(i), Collections.frequency(list, list.get(i)));
}
Since I have two numbers "1,9" which repeated same as each other "2 times"
my program should return 1 because one has minumum index.
so how can I fin max value in hashmap and return the minumum index
I'd suggest to use the groupingBy collector instead of frequency and then leverage the stream API to apply further operations to refine your query.
Optional<Integer> key = list.stream() // Stream<Integer>
.collect(groupingBy(Function.identity(), counting())) // Map<Integer, Long>
.entrySet().stream() //Stream<Entry<Integer, Long>>
.max(Comparator.comparingLong((ToLongFunction<Map.Entry<Integer, Long>>) Map.Entry::getValue)
.thenComparingInt(s -> -s.getKey())) // Optional<Entry<Integer, Long>> // Optional<Entry<Integer, Long>>
.map(Map.Entry::getKey); //Optional<Integer>
supplying the groupingBy collector to the collect method allows us to group each element in the source list and as a result have a Map<Integer, Long> where each entry represents the element and the number of times it occurs in the source list.
we then find the max value in Hashmap and return the minimum key.
Finally, we have a Optional<Integer> containing the key otherwise an empty optional.
Running this code against the sample data you've provided this will retrieve the number 1 which meets your requirements.
Since you mentioned that the numbers in the list represent indices this should suffice.
If you're not familiar with the Optional<T> API then I'd suggest browsing the documentation here.
You need to use LinkedHashMap to maintain the insertion order in the Map and then you can use lambda to index with max value which comes first in the index like below:
List<Integer> list = new ArrayList<>();
list.add(9);
list.add(0);
list.add(1);
list.add(1);
list.add(9);
Map<Integer, Integer> map = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
map.put(list.get(i), Collections.frequency(list, list.get(i)));
}
System.out.println(map);
System.out.println(Collections.max(map.entrySet(),
Comparator.comparingInt(entry -> entry.getValue())).getKey());
O/P:
{9=2, 0=1, 1=2}
9
If you want index with min key then replace above LinkedHashMap with below:
SortedMap<Integer, Integer> map = new TreeMap<>();
It will give 1 as output.
I've the following code
final Map<String, Location> map = new HashMap<>();
map.put("1", new Location("a", null));
map.put("2", new Location("b", null));
map.put("3", new Location("c", null));
final List<String> list = new ArrayList<>();
for (final Location s : map.values()) {
list.add(s.getId());
}
The result is a,b,c (as expected) when I print the list.
for (final String string : list) {
System.out.println(string);
}
Is there a better way of getting the Id's without using the for loop in Java6.
As per Java8, referencing the code form #rohit-jain answer
final List<String> list = map.values().stream().map(loc -> loc.getId()).collect(Collectors.toList());
Is there anything this consise in java6?
Not sure about efficiency (because it wouldn't affect much), but if you want to do that using lambdas, it can be like this:
final Map<String, Location> locationMap = new HashMap<>();
locationMap.put("1", new Location("a", null));
locationMap.put("2", new Location("b", null));
locationMap.put("3", new Location("c", null));
final List<String> list = locationMap.values().stream()
.map(loc -> loc.getId())
.collect(Collectors.toList());
System.out.println(list); // can be `[a, b, c]` or `[b, c, a]`, etc
But, just because you don't see for loop here doesn't mean that it isn't iterating over the values of the map. It does, but just hides the iteration logic.
Or, if you just want to print the value (one time usage), you can even avoid creating a list there:
locationMap.values().stream().map(loc -> loc.getId())
.forEach(System.out::println);
EDIT: The question was modified, and now does no longer seem to refer to efficiency. Now, this answer does not really fit any more, but for now, I'll leave it here, maybe someone finds it helpful
First a general hint: You said
The result is a,b,c (as expected) when I print the list.
However, you should not expect that. The HashMap is not sorted in any way. The order of the elments could be different. And also important: If you added more elements to the map, then the order of the elements that have previously be contained in the map may change!
If you want the order of the elements during the iteration to be the same as the insertion order, you should use a LinkedHashMap instead of a HashMap. It preserves the iteration order, and there, your expectation about the output would be met.
Interestingly, this leads to the question about the performance:
Iterating over a LinkedHashMap in fact can be (noticably) faster than iterating over a Map. Here's a small microbenchmark, which, as always, should be taken with a grain of salt:
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
public class MapIteration
{
public static void main(String[] args)
{
long sum = 0;
for (int size=100000; size<=1000000; size += 100000)
{
Map<String, Integer> mapA =
new HashMap<String, Integer>();
fillMap(mapA, size);
Map<String, Integer> mapB =
new LinkedHashMap<String, Integer>();
fillMap(mapB, size);
int runs = 100;
long beforeA = System.nanoTime();
for (int i=0; i<runs; i++)
{
sum += computeValueSum(mapA);
}
long afterA = System.nanoTime();
double durationA = (afterA - beforeA) / 1e6;
long beforeB = System.nanoTime();
for (int i=0; i<runs; i++)
{
sum += computeValueSum(mapB);
}
long afterB = System.nanoTime();
double durationB = (afterB - beforeB) / 1e6;
System.out.printf(
"Normal size %10d duration %10.3f\n", size, durationA);
System.out.printf(
"Linked size %10d duration %10.3f\n", size, durationB);
}
System.out.println(sum);
}
private static void fillMap(Map<String, Integer> map, int n)
{
Random random = new Random(0);
for (int i=0; i<n; i++)
{
map.put(String.valueOf(i), random.nextInt(n));
}
}
private static long computeValueSum(Map<?, Integer> map)
{
long sum = 0;
for (Integer i : map.values())
{
sum += i;
}
return sum;
}
}
For me, it prints...
...
Normal size 1000000 duration 2611,288
Linked size 1000000 duration 1796,030
Again, this should not be taken for granted, unless it is verified with a proper Micobenchmarking framework, but frankly: The LinkedHashMap is 30% faster, give or take a few, and I doubt that a Micobenchmarking framework will tell me the opposite here.
In general, I basically always use LinkedHashMap instead of a plain HashMap. But not because of the performance, but mainly because of the consistent iteration order. Regarding the performance: The insertions and deletions in a LinkedHashMap may be a tad more expensive than for a HashMap, but these performance differences are negligible in most cases.
If you use Eclipse Collections, you can write the following:
MutableMap<String, Location> map = Maps.mutable.empty();
map.put("1", new Location("a", null));
map.put("2", new Location("b", null));
map.put("3", new Location("c", null));
List<String> list = map.collect(Location::getId).toSortedList();
Bag<String> bag = map.collect(Location::getId);
Assert.assertEquals(Lists.mutable.with("a", "b", "c"), list);
Assert.assertEquals(Bags.mutable.with("a", "b", "c"), bag);
The following code will also work with Java 5 - 7:
Function<Location, String> function = new Function<Location, String>()
{
public String valueOf(Location location)
{
return location.getId();
}
};
List<String> list = map.collect(function).toSortedList();
Bag<String> bag = map.collect(function);
Assert.assertEquals(Lists.mutable.with("a", "b", "c"), list);
Assert.assertEquals(Bags.mutable.with("a", "b", "c"), bag);
Note: I am a committer for Eclipse Collections
If you like to use a Java 6 compatible version, then you could use Guava and its Function interface:
public class ExtractLocationId implements Function<Location, String> {
#Override
public String apply(final Location location) {
return location.getId();
}
}
And use it like this:
final List<String> list =
FluentIterable.from(map.values()).transform(new ExtractLocationId()).toList();
It needs more code than the Java 8 Lambda version (due to the own Function implementation) but it supports older Java versions.
You cannot run away from iteration over map at some point.
For maps most efficient way is to iterate over "entrySet()".
I get instance of Map from another method
Map map = service.getMap();
Now I want to get arbitrary 10 elements from this map.
I know that I can iterate over the Map.Entry but I don't like this decision.
Is there another way?
Apparently what you want is 10 arbitrary elements from the map. This would be available with a simple Java 8 idiom:
set.entrySet().stream().limit(10).collect(toList());
If you need only values, I would suggest approach like this:
Map map = service.getMap();
Object[] array= map.entrySet().toArray();
Arrays.copyOfRange(array, 0, 10);
or with generics
Map<Key, Value> map = service.getMap();
Map.Entry<Key, Value>[] array = (Map.Entry<Key, Value>[]) map.entrySet().toArray(new Map.Entry[0]);
Arrays.copyOfRange(array, 0, 10);
The Map.Entry has getKey() and getValue()
You could do this with an intermediate List to hold the keys from which you then select random elements. For example, this code creates a Map of 15 elements and then selects 10 at random from it:
public static void main(String[] args) throws Exception {
Map<Integer, String> map = new HashMap<>();
for(int x = 0; x < 15; x++) {
map.put(x, "val: " + String.valueOf(x));
}
List<Integer> keyList = new ArrayList<>(map.keySet());
Map<Integer, String> randomMap = new HashMap();
for(int x = 0; x < 10 && !keyList.isEmpty(); x++) {
Integer key = keyList.remove(new Random().nextInt(keyList.size()));
randomMap.put(key, map.get(key));
}
System.out.println(randomMap);
}
This also has the advantage that it's actually selecting random elements rather than the other solutions which just return the first 10 elements and would give you the same 10 each time.
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