I am working on a simple project that obtains data from an input file, gets what it needs and prints it to a file. I am basically getting word frequency so each key is a string and the value is its frequency in the document. The problem however, is that I need to print out these values to a file in descending order of frequency. After making my hashmap, this is the part of my program that sorts it and writes it to a file.
//Hashmap I create
Map<String, Integer> map = new ConcurrentHashMap<String, Integer>();
int valueMax = -1;
//function to sort hashmap
while (map.isEmpty() == false){
for (Entry<String, Integer> entry: map.entrySet()){
if (entry.getValue() > valueMax){
max = entry.getKey();
System.out.println("max: " + max);
valueMax = entry.getValue();
System.out.println("value: " + valueMax);
}
}
map.remove(max);
out.write(max + "\t" + valueMax + "\n");
System.out.println(max + "\t" + valueMax);
}
When I run this i get:
t 9
t 9
t 9
t 9
t 9
....
so it appears the remove function is not working as it keeps getting the same value. I'm thinking i have an issue with a scope rule or I just don't understand hashmaps very well.
If anyone knows of a better way to sort a hashmap and print it, I would welcome a suggestion.
thanks
Your code doesn't work because on every subsequent iteration, entry.getValue() > valueMax is never true because you don't reset valueMax on re-entry into the while loop.
You don't need to muck around with double-looping over a concurrently accessible map though.
ConcurrentSkipListMap has a lastKey method that returns the greatest key and doesn't require iteration over the entire map.
From your code it looks like you aren't resetting valueMax at the end of your loop. This means the first time round the loop you'll find the maximum but you'll never find any subsequent values because you'll still be comparing to the overall maximum.
Hashmap: no order. You can use ArrayList, which implements List to have an order.
Take a look : http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html
I guess it's because there no entries in map with key > initial value of valueMax, so condition if (entry.getValue() > valueMax) never true.
Additionally, there's TreeMap which holds it's contents sorted, so you can just iterate over it's entrySet() w/o any additional logic.
What about something like (not tested)
final Map<String, Integer> map = new ConcurrentHashMap<String, Integer>();
final Comparator <String, String> comparator = new Comparator ()
{
compare(String o1, String o2)
{
return map.get(o1) - map.get(o2);
}
};
final TreeMap <String, Integer> sortedMap = new TreeMap (comparator);
sortedMap.addAll(map);
System.out.println(sortedMap);
Related
This question already has answers here:
Sort a Map<Key, Value> by values
(64 answers)
Closed 5 months ago.
I made this quick code for a class and I got everything to work fine as far as reading the text file and printing it out, but I can't figure out how to get it to print out in ascending order. The goal is to read a file and print out the number of times that word appears and sort it by the number of times it appears.
public class Analyser {
public static void main(String[] args) throws IOException {
File file = new File("src/txt.txt");
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
Map<String, Long> counts = new HashMap<>();
while ((line = br.readLine()) != null) {
String[] words = line.split("[\\s.;,?:!()\"]+");
for (String word : words) {
word = word.trim();
if (word.length() > 0) {
if (counts.containsKey(word)) {
counts.put(word, counts.get(word) + 1);
} else {
counts.put(word, 1L);
}
}
}
}
for (Map.Entry<String, Long> entry : counts.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
br.close();
}
}
A HashMap has no defined order. Maps which implement the SortedMap interface, such as TreeMap, do have an order defined by a Comparator, but they're ordered by keys, not values. And then there's LinkedHashMap, which maintains insertion order (or access order, if you want that instead). You could make use of LinkedHashMap if you make sure to insert the elements in the desired order. That, of course, means you still need to iterate your original map in the desired order somehow.
But I think, in your case, it does not matter so much that the map has the order you want. You only want order when you print the results. And you only do this once, so there's no need for some kind of caching of the order. So, I would probably just use an intermediate list or a stream.
Example using list:
List<Map.Entry<String, Long>> list = new ArrayList<>(counts.entrySet());
list.sort(Map.Entry.comparingByValue());
for (Map.Entry<String, Long> entry : list) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
Example using stream:
counts.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.forEachOrdered(entry -> System.out.println(entry.getKey() + " : " + entry.getValue());
The Map.Entry.comparingByValue() call returns a Comparator which is used to sort the elements. Specifically, it will sort by the natural order of the values of the entries. A Long's natural order is from smallest to largest. If you want a different order, then pass whatever Comparator you need to get that order (which may involve writing your own implementation). Though if you simply want to sort from largest to smallest (i.e., reversed natural order), just use Map.Entry.comparingByValue(Comparator.reverseOrder()) instead.
I also recommend, at least in "real" code, that you use try-with-resources instead of manually closing the reader. For example:
File file = new File("src/txt.txt");
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
// use 'br' here (the remainder of you code, minus the 'br.close()' call
}
When that try block exits, the resource referenced by br will be automatically closed for you, and in a safer way than simply calling br.close().
Since a TreeMap only sorts on the key and not the values, it won't work as you want to sort based on the count which is a value. Since you have already created your map, the easiest ways is to sort it and print as follows:
stream the entrySet() and then sort using the Entry's comparingByValue comparator.
counts.entrySet().stream()
.sorted(Entry.comparingByValue())
.forEach(System.out::println);
If you want to retain the sorted order in a different map, you can do it like this and then print the new map. Using a LinkedHashMap preserves the order.
sort as before
but collect in a map
first argument is the entry's key
second is the entry's value.
third is a BinaryOperator (Not used here but syntactically required)
fourth is a supplier which makes the map a LinkedHashMap
Map<String, Long> sortedMap =
counts.entrySet().stream().sorted(Entry.comparingByValue())
.collect(Collectors.toMap(Entry::getKey,
Entry::getValue, (a, b) -> a,
LinkedHashMap::new));
// now print the map
sortedMap.entrySet().forEach(System.out::println);
This question already has answers here:
How do I efficiently iterate over each entry in a Java Map?
(46 answers)
Closed 3 years ago.
What's the best way to iterate over the items in a HashMap?
If you're only interested in the keys, you can iterate through the keySet() of the map:
Map<String, Object> map = ...;
for (String key : map.keySet()) {
// ...
}
If you only need the values, use values():
for (Object value : map.values()) {
// ...
}
Finally, if you want both the key and value, use entrySet():
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
// ...
}
One caveat: if you want to remove items mid-iteration, you'll need to do so via an Iterator (see karim79's answer). However, changing item values is OK (see Map.Entry).
Iterate through the entrySet() like so:
public static void printMap(Map mp) {
Iterator it = mp.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
System.out.println(pair.getKey() + " = " + pair.getValue());
it.remove(); // avoids a ConcurrentModificationException
}
}
Read more about Map.
Extracted from the reference How to Iterate Over a Map in Java:
There are several ways of iterating over a Map in Java. Let's go over the most common methods and review their advantages and disadvantages. Since all maps in Java implement the Map interface, the following techniques will work for any map implementation (HashMap, TreeMap, LinkedHashMap, Hashtable, etc.)
Method #1: Iterating over entries using a For-Each loop.
This is the most common method and is preferable in most cases. It should be used if you need both map keys and values in the loop.
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
Note that the For-Each loop was introduced in Java 5, so this method is working only in newer versions of the language. Also a For-Each loop will throw NullPointerException if you try to iterate over a map that is null, so before iterating you should always check for null references.
Method #2: Iterating over keys or values using a For-Each loop.
If you need only keys or values from the map, you can iterate over keySet or values instead of entrySet.
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
// Iterating over keys only
for (Integer key : map.keySet()) {
System.out.println("Key = " + key);
}
// Iterating over values only
for (Integer value : map.values()) {
System.out.println("Value = " + value);
}
This method gives a slight performance advantage over entrySet iteration (about 10% faster) and is more clean.
Method #3: Iterating using Iterator.
Using Generics:
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<Integer, Integer> entry = entries.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
Without Generics:
Map map = new HashMap();
Iterator entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
Integer key = (Integer)entry.getKey();
Integer value = (Integer)entry.getValue();
System.out.println("Key = " + key + ", Value = " + value);
}
You can also use same technique to iterate over keySet or values.
This method might look redundant, but it has its own advantages. First of all, it is the only way to iterate over a map in older versions of Java. The other important feature is that it is the only method that allows you to remove entries from the map during iteration by calling iterator.remove(). If you try to do this during For-Each iteration you will get "unpredictable results" according to Javadoc.
From a performance point of view this method is equal to a For-Each iteration.
Method #4: Iterating over keys and searching for values (inefficient).
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Integer key : map.keySet()) {
Integer value = map.get(key);
System.out.println("Key = " + key + ", Value = " + value);
}
This might look like a cleaner alternative for method #1, but in practice it is pretty slow and inefficient as getting values by a key might be time-consuming (this method in different Map implementations is 20%-200% slower than method #1). If you have FindBugs installed, it will detect this and warn you about inefficient iteration. This method should be avoided.
Conclusion:
If you need only keys or values from the map, use method #2. If you are stuck with older version of Java (less than 5) or planning to remove entries during iteration, you have to use method #3. Otherwise use method #1.
for (Map.Entry<String, String> item : hashMap.entrySet()) {
String key = item.getKey();
String value = item.getValue();
}
You can iterate through the entries in a Map in several ways. Get each key and value like this:
Map<?,?> map = new HashMap<Object, Object>();
for(Entry<?, ?> e: map.entrySet()){
System.out.println("Key " + e.getKey());
System.out.println("Value " + e.getValue());
}
Or you can get the list of keys with
Collection<?> keys = map.keySet();
for(Object key: keys){
System.out.println("Key " + key);
System.out.println("Value " + map.get(key));
}
If you just want to get all of the values and aren't concerned with the keys, you can use:
Collection<?> values = map.values();
Smarter:
for (String key : hashMap.keySet()) {
System.out.println("Key: " + key + ", Value: " + map.get(key));
}
It depends. If you know you're going to need both the key and the value of every entry, then go through the entrySet. If you just need the values, then there's the values() method. And if you just need the keys, then use keyset().
A bad practice would be to iterate through all of the keys, and then within the loop, always do map.get(key) to get the value. If you're doing that, then the first option I wrote is for you.
i have this kind of data structure
Map<Integer, Integer> groupMap= new LinkedHashMap<>();
groupMap.put(10, 1);
groupMap.put(11, 0);
groupMap.put(14, 1);
groupMap.put(13, 0);
groupMap.put(12, 0);
groupMap.put(15, 1);
what can be the best way to find the key which has value 1 if i have a present key with one value.
Ex:i have key 14, now need to find the key 15 which has value 1
least looping will be helpfull.
my approch:
List<Integer> keys = new ArrayList<>();
keys.putAll(groupMap.keySet());
//getting the index of current key i have
int index = keys.indexOf(14);
if(keys.size() == index) return -1;
for(int i = index+1;i<keys.size();i++){
if(groupMap.get(i) == 1) return i;
}
i know it isn't a very good approach, but can you please suggest a good one.
This completely defeats the purpose of a key-value map. But if it's really what you want, I suppose you could do the following:
public static int getNextKeyByValue(int value, int previousKey) {
final Map<Integer, Integer> groupMap = new HashMap<>();
Iterator iterator = groupMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, Integer> entry = (Map.Entry<Integer, Integer>) iterator.next();
if (entry.getValue() == value && entry.getKey() != previousKey) {
return entry.getKey();
}
}
return -1;
}
From the topic which #Titus mentioned in the comment, the most elegant and shortest solution is to use stream:
int getFirstCorrectValueBiggerThan (int lastValue) {
return groupMap.entrySet().stream()
.filter(entry -> Objects.equals(entry.getValue(), 1))
.map(Map.Entry::getKey)
.filter(value -> value > lastValue)
.findFirst();
}
edit:
sorry for the mistake, the code provided does not solve your problem since it is comparing keys not indexes. Here you have proper version, however it is not so cool anymore.
ArrayList<Integer> filteredList = groupMap.entrySet().stream()
.filter(entry -> entry.getValue().equals(1))
.map(Map.Entry::getKey)
.collect(Collectors.toCollection(ArrayList::new));
int nextCorrectElement = filteredList.get(filteredList.indexOf(14) + 1);
update
as far as i undestand what is written in this tutorial about map:
When a user calls put(K key, V value) or get(Object key), the function computes the index of the bucket in which the Entry should be. Then, the function iterates through the list to look for the Entry that has the same key (using the equals() function of the key).
and check out this topic about hash map complexity.
O(1) certainly isn't guaranteed - but it's usually what you should assume when considering which algorithms and data structures to use.
On top of that, the key part of your solution- the ArrayList::indexOf- is O(N) complex- you have to iterate through each element till the one which meets the condition. More info is in this topic.
So efectively you are iterating through every element of your hashmap anyway. And what is more, the hashmap searching (get method) is not quaranteed to be O(1) complex so there is a chance that you will double your work.
I have made a simple test of performance for stream based solution and simple loop proposed in this topic. In fact loop will be faster than sequential stream for each case I think, but still if you want that kind of performance gain then try to write it in in C++. Otherwise if you have more complex example then using the parallel stream may get some advantage due to higher abstraction level of the problem stating.
I have not really clear your question. If you are looking for all the tuples with value equals to 1, you could follow the approach below:
for (Entry<Integer, Integer> entry : groupMap.entrySet()) {
if (entry.getValue() == 1) {
System.out.println("The key is: " + entry.getKey().toString());
}
}
This question already has answers here:
How do I efficiently iterate over each entry in a Java Map?
(46 answers)
Closed 3 years ago.
What's the best way to iterate over the items in a HashMap?
If you're only interested in the keys, you can iterate through the keySet() of the map:
Map<String, Object> map = ...;
for (String key : map.keySet()) {
// ...
}
If you only need the values, use values():
for (Object value : map.values()) {
// ...
}
Finally, if you want both the key and value, use entrySet():
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
// ...
}
One caveat: if you want to remove items mid-iteration, you'll need to do so via an Iterator (see karim79's answer). However, changing item values is OK (see Map.Entry).
Iterate through the entrySet() like so:
public static void printMap(Map mp) {
Iterator it = mp.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
System.out.println(pair.getKey() + " = " + pair.getValue());
it.remove(); // avoids a ConcurrentModificationException
}
}
Read more about Map.
Extracted from the reference How to Iterate Over a Map in Java:
There are several ways of iterating over a Map in Java. Let's go over the most common methods and review their advantages and disadvantages. Since all maps in Java implement the Map interface, the following techniques will work for any map implementation (HashMap, TreeMap, LinkedHashMap, Hashtable, etc.)
Method #1: Iterating over entries using a For-Each loop.
This is the most common method and is preferable in most cases. It should be used if you need both map keys and values in the loop.
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
Note that the For-Each loop was introduced in Java 5, so this method is working only in newer versions of the language. Also a For-Each loop will throw NullPointerException if you try to iterate over a map that is null, so before iterating you should always check for null references.
Method #2: Iterating over keys or values using a For-Each loop.
If you need only keys or values from the map, you can iterate over keySet or values instead of entrySet.
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
// Iterating over keys only
for (Integer key : map.keySet()) {
System.out.println("Key = " + key);
}
// Iterating over values only
for (Integer value : map.values()) {
System.out.println("Value = " + value);
}
This method gives a slight performance advantage over entrySet iteration (about 10% faster) and is more clean.
Method #3: Iterating using Iterator.
Using Generics:
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<Integer, Integer> entry = entries.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
Without Generics:
Map map = new HashMap();
Iterator entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
Integer key = (Integer)entry.getKey();
Integer value = (Integer)entry.getValue();
System.out.println("Key = " + key + ", Value = " + value);
}
You can also use same technique to iterate over keySet or values.
This method might look redundant, but it has its own advantages. First of all, it is the only way to iterate over a map in older versions of Java. The other important feature is that it is the only method that allows you to remove entries from the map during iteration by calling iterator.remove(). If you try to do this during For-Each iteration you will get "unpredictable results" according to Javadoc.
From a performance point of view this method is equal to a For-Each iteration.
Method #4: Iterating over keys and searching for values (inefficient).
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Integer key : map.keySet()) {
Integer value = map.get(key);
System.out.println("Key = " + key + ", Value = " + value);
}
This might look like a cleaner alternative for method #1, but in practice it is pretty slow and inefficient as getting values by a key might be time-consuming (this method in different Map implementations is 20%-200% slower than method #1). If you have FindBugs installed, it will detect this and warn you about inefficient iteration. This method should be avoided.
Conclusion:
If you need only keys or values from the map, use method #2. If you are stuck with older version of Java (less than 5) or planning to remove entries during iteration, you have to use method #3. Otherwise use method #1.
for (Map.Entry<String, String> item : hashMap.entrySet()) {
String key = item.getKey();
String value = item.getValue();
}
You can iterate through the entries in a Map in several ways. Get each key and value like this:
Map<?,?> map = new HashMap<Object, Object>();
for(Entry<?, ?> e: map.entrySet()){
System.out.println("Key " + e.getKey());
System.out.println("Value " + e.getValue());
}
Or you can get the list of keys with
Collection<?> keys = map.keySet();
for(Object key: keys){
System.out.println("Key " + key);
System.out.println("Value " + map.get(key));
}
If you just want to get all of the values and aren't concerned with the keys, you can use:
Collection<?> values = map.values();
Smarter:
for (String key : hashMap.keySet()) {
System.out.println("Key: " + key + ", Value: " + map.get(key));
}
It depends. If you know you're going to need both the key and the value of every entry, then go through the entrySet. If you just need the values, then there's the values() method. And if you just need the keys, then use keyset().
A bad practice would be to iterate through all of the keys, and then within the loop, always do map.get(key) to get the value. If you're doing that, then the first option I wrote is for you.
When I iterate over the values or keys are they going to correlate? Will the second key map to the second value?
No, not necessarily. You should really use the entrySet().iterator() for this purpose. With this iterator, you will be walking through all Map.Entry objects in the Map and can access each key and associated value.
to use the entrySet that #Cuchullain mentioned:
Map<String, String> map = new HashMap<String, String>();
// populate hashmap
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// your code here
}
You want to use this, LinkedHashMap, for predicable iteration order
public class Test {
public static void main(String[] args) {
HashMap <String,String> hashmap = new HashMap<String,String>();
hashmap.put("one", "1");
hashmap.put("two", "2");
hashmap.put("three", "3");
hashmap.put("four", "4");
hashmap.put("five", "5");
hashmap.put("six", "6");
Iterator <String> keyIterator = hashmap.keySet().iterator();
Iterator <String> valueIterator = hashmap.values().iterator();
while(keyIterator.hasNext()) {
System.out.println("key: "+keyIterator.next());
}
while(valueIterator.hasNext()) {
System.out.println("value: "+valueIterator.next());
}
}
}
key: two
key: five
key: one
key: three
key: four
key: six
value: 2
value: 5
value: 1
value: 3
value: 4
value: 6
Both values() and keySet() delegate to the entrySet() iterator so they will be returned in the same order. But like Alex says it is much better to use the entrySet() iterator directly.
I agree with pmac72. Don't assume that you'll get ordered values or keys from an unordered collection. If it works time to time it is just pure hazard. If you want order to be preserved, use a LinkedHashMap or a TreeMap or commons collections OrderedMap.
The question confused me at first but #Matt cleared it up for me.
Consider using the entrySet() method that returns a set with the key-value pairs on the Map.
Map<Integer, Integer> a = new HashMap<Integer, Integer>(2);
a.put(1, 2);
a.put(2, 3);
for (Map.Entry<Integer, Integer> entry : a.entrySet()) {
System.out.println(entry.getKey() + " => " + entry.getValue());
}
This outputs:
1 => 2
2 => 3
3 => 3
I second #basszero. While
for (Map.Entry<Integer, Integer> entry : a.entrySet())
will work, I find using a data structure that does this automatically is nicer. Now, you can just iterate "normally"
HashMap's keySet method returns a Set, which does not guarantee order.
HashMap's values() method returns a Collection, which does not guarantee order.
That said, the question was "are they going to correlate" so technically the answer is maybe, but don't rely on it.