Most concise way to get highest n numbers out of a map? - java

I have the following already:
public enum InvoiceCurrency {
EUR(
s -> (s.contains("€") || s.contains("EUR"))
),
USD(
s -> (s.contains("$") || s.contains("USD"))
);
private final Predicate<String> predicate;
InvoiceCurrency(final Predicate<String> predicate) {
this.predicate = predicate;
}
public boolean matchesString(final String value) {
return predicate.test(value);
}
public static EnumMap<InvoiceCurrency, Integer> createMapping(final Stream<String> valuesStream) {
EnumMap<InvoiceCurrency, Integer> mapping = new EnumMap<>(InvoiceCurrency.class);
mapping.replaceAll((k, v) -> 0);
Stream<InvoiceCurrency> enums = Arrays.stream(InvoiceCurrency.values());
valuesStream.forEach(
s -> enums.forEach(
e -> {
if (e.matchesString(s)) {
mapping.compute(e, (k, v) -> v++);
}
}
)
);
return mapping;
}
}
private InvoiceCurrency calculateCurrency() {
EnumMap<InvoiceCurrency, Integer> map = InvoiceCurrency.createMapping(data.words.stream().map(w -> w.content));
InvoiceCurrency maximum = map.entrySet().parallelStream(). //how to continue?
}
This results in a mapping from the enum to the 'number of occurences', so EUR can be mapped to 10 and USD to 1. Possibly, the count may be the same.
Now have do I, as concise as possibly and with the ability to use java-8, get the InvoiceCurrency that belongs to the highest number? And is there a concise way to see that the top 2 of the sorted integer count actually has the same value?
I know I can program it with loops, etc., but I wish to rely on the java-8 spirit for the most maintainable code.

Simple example with a Map<String, Integer> but the same will work with your example. Prints the top 2 entries (b and c or d).
import static java.util.Collections.reverseOrder;
import static java.util.Comparator.comparingInt;
//...
Map<String, Integer> map = new HashMap<>();
map.put("a", 2);
map.put("b", 10);
map.put("c", 5);
map.put("d", 5);
map.put("e", 1);
map.entrySet().parallelStream()
.sorted(reverseOrder(comparingInt(Map.Entry::getValue)))
.limit(2)
.forEach(System.out::println);
//or: .forEachOrdered(System.out::println);
//to print in descending order
NOTE: from b129 on, you can also use sorted(comparingInt(Map.Entry::getValue).reversed()) instead of sorted(reverseOrder(comparingInt(Map.Entry::getValue))).

Related

How to use Map.computeIfAbsent() in a stream?

I have a fun puzzler. Say I have a list of String values:
["A", "B", "C"]
Then I have to query another system for a Map<User, Long> of users with an attribute that corresponds to those values in the list with a count:
{name="Annie", key="A"} -> 23
{name="Paul", key="C"} -> 16
I need to return a new List<UserCount> with a count of each key. So I expect:
{key="A", count=23},
{key="B", count=0},
{key="C", count=16}
But I'm having a hard time computing when one of my User objects has no corresponding count in the map.
I know that map.computeIfAbsent() does what I need, but how can I apply it based on what's on the contents of the original list?
I think I need to stream the over the original list, then apply compute? So I have:
valuesList.stream()
.map(it -> valuesMap.computeIfAbsent(it.getKey(), k-> OL))
...
But here's where I get stuck. Can anyone provide any insight as to how I accomplish what I need?
You can create an auxiliary Map<String, Long> which will associate each string key with the count and then generate a list of UserCount based on it.
Example:
public record User(String name, String key) {}
public record UserCount(String key, long count) {}
public static void main(String[] args) {
List<String> keys = List.of("A", "B", "C");
Map<User, Long> countByUser =
Map.of(new User("Annie", "A"), 23L,
new User("Paul", "C"), 16L));
Map<String, Long> countByKey = countByUser.entrySet().stream()
.collect(Collectors.groupingBy(entry -> entry.getKey().key(),
Collectors.summingLong(Map.Entry::getValue)));
List<UserCount> userCounts = keys.stream()
.map(key -> new UserCount(key, countByKey.getOrDefault(key, 0L)))
.collect(Collectors.toList());
System.out.println(userCounts);
}
Output
[UserCount[key=A, count=23], UserCount[key=B, count=0], UserCount[key=C, count=16]]
Regarding the idea of utilizing computeIfAbsent() with stream - this approach is wrong and discouraged by the documentation of the Stream API.
Sure, you can use computeIfAbsent() to solve this problem, but not in conjunction with streams. It's not a good idea to create a stream that operates via side effects (at least without compelling reason).
And I guess you even don't need Java 8 computeIfAbsent(), plain and simple putIfAbsent() will be sufficient.
The following code will produce the same result:
Map<String, Long> countByKey = new HashMap<>();
countByUser.forEach((k, v) -> countByKey.merge(k.key(), v, Long::sum));
keys.forEach(k -> countByKey.putIfAbsent(k, 0L));
List<UserCount> userCounts = keys.stream()
.map(key -> new UserCount(key, countByKey.getOrDefault(key, 0L)))
.collect(Collectors.toList());
And instead of applying forEach() on a map and list, you can create two enhanced for loops if this options looks convoluted.
Another educational and parallel friendly version would be to gather the logic in one place and build your own custom accumulator and combiner for the Collector
public static void main(String[] args) {
Map<User, Long> countByUser =
Map.of(new User("Alice", "A"), 23L,
new User("Bob", "C"), 16L);
List<String> keys = List.of("A", "B", "C");
UserCountAggregator userCountAggregator =
countByUser.entrySet()
.parallelStream()
.collect(UserCountAggregator::new,
UserCountAggregator::accumulator,
UserCountAggregator::combiner);
List<UserCount> userCounts = userCountAggregator.getUserCounts(keys);
System.out.println(userCounts);
}
Output
[UserCount(key=A, count=23), UserCount(key=B, count=0), UserCount(key=C, count=16)]
User and UserCount classes with Lombok's #Value
#Value
class User {
private String name;
private String key;
}
#Value
class UserCount {
private String key;
private long count;
}
And the UserCountAggregator which contains your custom accumulator and combiner
class UserCountAggregator {
private Map<String, Long> keyCounts = new HashMap<>();
public void accumulator(Map.Entry<User, Long> userLongEntry) {
keyCounts.put(userLongEntry.getKey().getKey(),
keyCounts.getOrDefault(userLongEntry.getKey().getKey(), 0L)
+ userLongEntry.getValue());
}
public void combiner(UserCountAggregator other) {
other.keyCounts
.forEach((key, value) -> keyCounts.merge(key, value, Long::sum));
}
public List<UserCount> getUserCounts(List<String> keys) {
return keys.stream()
.map(key -> new UserCount(key, keyCounts.getOrDefault(key, 0L)))
.collect(Collectors.toList());
}
}
final Map<User,Long> valuesMap = ...
// First, map keys to counts (assuming keys are unique for each user)
final Map<String,Long> keyToCountMap = valuesMap.entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey().key, e -> e.getValue()));
final List<UserCount> list = valuesList.stream()
.map(key -> new UserCount (key, keyToCountMap.getOrDefault(key, 0L)))
.collect(Collectors.toList());

Sort list by multiple fields(not then compare) in java

Now I have an object:
public class Room{
private long roomId;
private long roomGroupId;
private String roomName;
... getter
... setter
}
I want sort list of rooms by 'roomId', but in the meantime while room objects has 'roomGroupId' greator than zero and has same value then make them close to each other.
Let me give you some example:
input:
[{"roomId":3,"roomGroupId":0},
{"roomId":6,"roomGroupId":0},
{"roomId":1,"roomGroupId":1},
{"roomId":2,"roomGroupId":0},
{"roomId":4,"roomGroupId":1}]
output:
[{"roomId":6,"roomGroupId":0},
{"roomId":4,"roomGroupId":1},
{"roomId":1,"roomGroupId":1},
{"roomId":3,"roomGroupId":0},
{"roomId":2,"roomGroupId":0}]
As shown above, the list sort by 'roomId', but 'roomId 4' and 'roomId 1' are close together, because they has the same roomGroupId.
This does not have easy nice solution (maybe I am wrong).
You can do this like this
TreeMap<Long, List<Room>> roomMap = new TreeMap<>();
rooms.stream()
.collect(Collectors.groupingBy(Room::getRoomGroupId))
.forEach((key, value) -> {
if (key.equals(0L)) {
value.forEach(room -> roomMap.put(room.getRoomId(), Arrays.asList(room)));
} else {
roomMap.put(
Collections.max(value, Comparator.comparing(Room::getRoomId))
.getRoomId(),
value
.stream()
.sorted(Comparator.comparing(Room::getRoomId)
.reversed())
.collect(Collectors.toList())
);
}
});
List<Room> result = roomMap.descendingMap()
.entrySet()
.stream()
.flatMap(entry -> entry.getValue()
.stream())
.collect(Collectors.toList());
If you're in Java 8, you can use code like this
Collections.sort(roomList, Comparator.comparing(Room::getRoomGroupId)
.thenComparing(Room::getRoomId));
If not, you should use a comparator
class SortRoom implements Comparator<Room>
{
public int compare(Room a, Room b)
{
if (a.getRoomGroupId().compareTo(b.getRoomGroupId()) == 0) {
return a.getRoomId().compareTo(b.getRoomId());
}
return a.getRoomGroupId().compareTo(b.getRoomGroupId();
}
}
and then use it like this
Collections.sort(roomList, new SortRoom());

Overwriting values in a HashMap that are in an ArrayList<String>

Let's say I have a HashMap with String keys and Integer values:
map = {cat=1, kid=3, girl=3, adult=2, human=5, dog=2, boy=2}
I want to switch the keys and values by putting this information into another HashMap. I know that a HashMap cannot have duplicate keys, so I tried to put the information into a HashMap with the Integer for the keys that would map to a String ArrayList so that I could potentially have one Integer mapping to multiple Strings:
swap = {1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}
I tried the following code:
HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();
for (String x : map.keySet()) {
for (int i = 0; i <= 5; i++) {
ArrayList<String> list = new ArrayList<String>();
if (i == map.get(x)) {
list.add(x);
swap.put(i, list);
}
}
}
The only difference in my code is that I didn't hard code the number 5 into my index; I have a method that finds the highest integer value in the original HashMap and used that. I know it works correctly because I get the same output even if I hard code the 5 in there, I just didn't include it to save space.
My goal here is to be able to do this 'reversal' with any set of data, otherwise I could just hard code the value. The output I get from the above code is this:
swap = {1=[cat], 2=[boy], 3=[girl], 5=[human]}
As you can see, my problem is that the value ArrayList is only keeping the last String that was put into it, instead of collecting all of them. How can I make the ArrayList store each String, rather than just the last String?
With Java 8, you can do the following:
Map<String, Integer> map = new HashMap<>();
map.put("cat", 1);
map.put("kid", 3);
map.put("girl", 3);
map.put("adult", 2);
map.put("human", 5);
map.put("dog", 2);
map.put("boy", 2);
Map<Integer, List<String>> newMap = map.keySet()
.stream()
.collect(Collectors.groupingBy(map::get));
System.out.println(newMap);
The output will be:
{1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}
you are recreating the arrayList for every iteration and i can't figure out a way to do it with that logic, here is a good way though and without the need to check for the max integer:
for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
List<String> get = swap.get(value);
if (get == null) {
get = new ArrayList<>();
swap.put(value, get);
}
get.add(key);
}
Best way is to iterate over the key set of the original map.
Also you have to asure that the List is present for any key in the target map:
for (Map.Entry<String,Integer> inputEntry : map.entrySet())
swap.computeIfAbsent(inputEntry.getValue(),()->new ArrayList<>()).add(inputEntry.getKey());
This is obviously not the best solution, but approaches the problem the same way you did by interchanging inner and outer loops as shown below.
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("cat", 1);
map.put("kid", 3);
map.put("girl", 3);
map.put("adult", 2);
map.put("human", 5);
map.put("dog", 2);
map.put("boy", 2);
HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();
for (Integer value = 0; value <= 5; value++) {
ArrayList<String> list = new ArrayList<String>();
for (String key : map.keySet()) {
if (map.get(key) == value) {
list.add(key);
}
}
if (map.containsValue(value)) {
swap.put(value, list);
}
}
Output
{1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}
Best way I can think of is using Map.forEach method on existing map and Map.computeIfAbsent method on new map:
Map<Integer, List<String>> swap = new HashMap<>();
map.forEach((k, v) -> swap.computeIfAbsent(v, k -> new ArrayList<>()).add(k));
As a side note, you can use the diamond operator <> to create your new map (there's no need to repeat the type of the key and value when invoking the map's constructor, as the compiler will infer them).
As a second side note, it's good practice to use interface types instead of concrete types, both for generic parameter types and for actual types. This is why I've used List and Map instead of ArrayList and HashMap, respectively.
Using groupingBy like in Jacob's answer but with Map.entrySet for better performance, as suggested by Boris:
// import static java.util.stream.Collectors.*
Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(groupingBy(Entry::getValue, mapping(Entry::getKey, toList())));
This uses two more methods of Collectors: mapping and toList.
If it wasn't for these two helper functions, the solution could look like this:
Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(
groupingBy(
Entry::getValue,
Collector.of(
ArrayList::new,
(list, e) -> {
list.add(e.getKey());
},
(left, right) -> { // only needed for parallel streams
left.addAll(right);
return left;
}
)
)
);
Or, using toMap instead of groupingBy:
Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(
toMap(
Entry::getValue,
(e) -> new ArrayList<>(Arrays.asList(e.getKey())),
(left, right) -> {
left.addAll(right);
return left;
}
)
);
It seams you override the values instrad of adding them to the already creared arraylist. Try this:
HashMap<Integer, ArrayList<String>> swapedMap = new HashMap<Integer, ArrayList<String>>();
for (String key : map.keySet()) {
Integer swappedKey = map.get(key);
ArrayList<String> a = swapedMap.get(swappedKey);
if (a == null) {
a = new ArrayList<String>();
swapedMap.put(swappedKey, a)
}
a.add(key);
}
I didn't have time to run it (sorry, don't have Java compiler now), but should be almost ok :)
You could use the new merge method in java-8 from Map:
Map<Integer, List<String>> newMap = new HashMap<>();
map.forEach((key, value) -> {
List<String> values = new ArrayList<>();
values.add(key);
newMap.merge(value, values, (left, right) -> {
left.addAll(right);
return left;
});
});

Aggregating values in hashmap by keys

Consider a simple HashMap<Integer,Integer>. How can I get all the values stored against keys which are multiple of, say 5? I have worked on Java and Collections for some time now, but suddenly I am clueless.
Any help would be highly appreciated.
Regards,
Salil
List<Integer> values = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if (entry.getKey() % 5 == 0) {
values.add(entry.getValue());
}
}
FWIW, a comparable Java 8 approach might look like
map.entrySet().stream()
.filter(entry -> entry.getKey() % 5 == 0)
.map(Entry<Integer, Integer>::getValue)
.collect(toList());
Guava has a few helper methods to help you achieve this. In particular, Maps.filterKeys(Map, Predicate)
// populate a Map
Map<Integer, Integer> map = new HashMap<>();
map.put(3, 0);
map.put(15, 42);
map.put(75, 1234);
// filter it
Map<Integer, Integer> filtered = Maps.filterKeys(map, new Predicate<Integer>() {
#Override
public boolean apply(Integer input) {
return input % 5 == 0; // filter logic
}
});
// get the values
System.out.println(filtered.values());
prints
[1234, 42]
As Louis notes, "this isn't actually more efficient -- or even shorter -- than the "traditional" for-each approach."

How to count the number of occurrences of an element in a List

I have an ArrayList, a Collection class of Java, as follows:
ArrayList<String> animals = new ArrayList<String>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");
As you can see, the animals ArrayList consists of 3 bat elements and one owl element. I was wondering if there is any API in the Collection framework that returns the number of bat occurrences or if there is another way to determine the number of occurrences.
I found that Google's Collection Multiset does have an API that returns the total number of occurrences of an element. But that is compatible only with JDK 1.5. Our product is currently in JDK 1.6, so I cannot use it.
I'm pretty sure the static frequency-method in Collections would come in handy here:
int occurrences = Collections.frequency(animals, "bat");
That's how I'd do it anyway. I'm pretty sure this is jdk 1.6 straight up.
In Java 8:
Map<String, Long> counts =
list.stream().collect(Collectors.groupingBy(e -> e, Collectors.counting()));
Alternative Java 8 solution using Streams:
long count = animals.stream().filter(animal -> "bat".equals(animal)).count();
This shows, why it is important to "Refer to objects by their interfaces" as described in Effective Java book.
If you code to the implementation and use ArrayList in let's say, 50 places in your code, when you find a good "List" implementation that count the items, you will have to change all those 50 places, and probably you'll have to break your code ( if it is only used by you there is not a big deal, but if it is used by someone else uses, you'll break their code too)
By programming to the interface you can let those 50 places unchanged and replace the implementation from ArrayList to "CountItemsList" (for instance ) or some other class.
Below is a very basic sample on how this could be written. This is only a sample, a production ready List would be much more complicated.
import java.util.*;
public class CountItemsList<E> extends ArrayList<E> {
// This is private. It is not visible from outside.
private Map<E,Integer> count = new HashMap<E,Integer>();
// There are several entry points to this class
// this is just to show one of them.
public boolean add( E element ) {
if( !count.containsKey( element ) ){
count.put( element, 1 );
} else {
count.put( element, count.get( element ) + 1 );
}
return super.add( element );
}
// This method belongs to CountItemList interface ( or class )
// to used you have to cast.
public int getCount( E element ) {
if( ! count.containsKey( element ) ) {
return 0;
}
return count.get( element );
}
public static void main( String [] args ) {
List<String> animals = new CountItemsList<String>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");
System.out.println( (( CountItemsList<String> )animals).getCount( "bat" ));
}
}
OO principles applied here: inheritance, polymorphism, abstraction, encapsulation.
Sorry there's no simple method call that can do it. All you'd need to do though is create a map and count frequency with it.
HashMap<String,int> frequencymap = new HashMap<String,int>();
foreach(String a in animals) {
if(frequencymap.containsKey(a)) {
frequencymap.put(a, frequencymap.get(a)+1);
}
else{ frequencymap.put(a, 1); }
}
There is no native method in Java to do that for you. However, you can use IterableUtils#countMatches() from Apache Commons-Collections to do it for you.
Simple Way to find the occurrence of string value in an array using Java 8 features.
public void checkDuplicateOccurance() {
List<String> duplicateList = new ArrayList<String>();
duplicateList.add("Cat");
duplicateList.add("Dog");
duplicateList.add("Cat");
duplicateList.add("cow");
duplicateList.add("Cow");
duplicateList.add("Goat");
Map<String, Long> couterMap = duplicateList.stream().collect(Collectors.groupingBy(e -> e.toString(),Collectors.counting()));
System.out.println(couterMap);
}
Output : {Cat=2, Goat=1, Cow=1, cow=1, Dog=1}
You can notice "Cow" and cow are not considered as same string, in case you required it under same count, use .toLowerCase(). Please find the snippet below for the same.
Map<String, Long> couterMap = duplicateList.stream().collect(Collectors.groupingBy(e -> e.toString().toLowerCase(),Collectors.counting()));
Output : {cat=2, cow=2, goat=1, dog=1}
To achieve that one can do it in several ways, namely:
Methods that return the number of occurrence of a single element:
Collection Frequency
Collections.frequency(animals, "bat");
Java Stream:
Filter
animals.stream().filter("bat"::equals).count();
Just iteration thought the list
public static long manually(Collection<?> c, Object o){
int count = 0;
for(Object e : c)
if(e.equals(o))
count++;
return count;
}
Methods that create a map of frequencies:
Collectors.groupingBy
Map<String, Long> counts =
animals.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
merge
Map<String, Long> map = new HashMap<>();
c.forEach(e -> map.merge(e, 1L, Long::sum));
Manually
Map<String, Integer> mp = new HashMap<>();
animals.forEach(animal -> mp.compute(animal, (k, v) -> (v == null) ? 1 : v + 1));
A running example with all the methods:
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Frequency {
public static int frequency(Collection<?> c, Object o){
return Collections.frequency(c, o);
}
public static long filter(Collection<?> c, Object o){
return c.stream().filter(o::equals).count();
}
public static long manually(Collection<?> c, Object o){
int count = 0;
for(Object e : c)
if(e.equals(o))
count++;
return count;
}
public static Map<?, Long> mapGroupBy(Collection<?> c){
return c.stream()
.collect(Collectors.groupingBy(Function.identity() , Collectors.counting()));
}
public static Map<Object, Long> mapMerge(Collection<?> c){
Map<Object, Long> map = new HashMap<>();
c.forEach(e -> map.merge(e, 1L, Long::sum));
return map;
}
public static Map<Object, Long> manualMap(Collection<?> c){
Map<Object, Long> map = new HashMap<>();
c.forEach(e -> map.compute(e, (k, v) -> (v == null) ? 1 : v + 1));
return map;
}
public static void main(String[] args){
List<String> animals = new ArrayList<>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");
System.out.println(frequency(animals, "bat"));
System.out.println(filter(animals,"bat"));
System.out.println(manually(animals,"bat"));
mapGroupBy(animals).forEach((k, v) -> System.out.println(k + " -> "+v));
mapMerge(animals).forEach((k, v) -> System.out.println(k + " -> "+v));
manualMap(animals).forEach((k, v) -> System.out.println(k + " -> "+v));
}
}
The methods name should have reflected what those methods are doing, however, I used the name to reflect the approach being used instead (given that in the current context it is okey).
I wonder, why you can't use that Google's Collection API with JDK 1.6. Does it say so? I think you can, there should not be any compatibility issues, as it is built for a lower version. The case would have been different if that were built for 1.6 and you are running 1.5.
Am I wrong somewhere?
Actually, Collections class has a static method called : frequency(Collection c, Object o) which returns the number of occurrences of the element you are searching for, by the way, this will work perfectly for you:
ArrayList<String> animals = new ArrayList<String>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");
System.out.println("Freq of bat: "+Collections.frequency(animals, "bat"));
A slightly more efficient approach might be
Map<String, AtomicInteger> instances = new HashMap<String, AtomicInteger>();
void add(String name) {
AtomicInteger value = instances.get(name);
if (value == null)
instances.put(name, new AtomicInteger(1));
else
value.incrementAndGet();
}
To get the occurrences of the object from the list directly:
int noOfOccurs = Collections.frequency(animals, "bat");
To get the occurrence of the Object collection inside list, override the equals method in the Object class as:
#Override
public boolean equals(Object o){
Animals e;
if(!(o instanceof Animals)){
return false;
}else{
e=(Animals)o;
if(this.type==e.type()){
return true;
}
}
return false;
}
Animals(int type){
this.type = type;
}
Call the Collections.frequency as:
int noOfOccurs = Collections.frequency(animals, new Animals(1));
What you want is a Bag - which is like a set but also counts the number of occurances. Unfortunately the java Collections framework - great as they are dont have a Bag impl. For that one must use the Apache Common Collection link text
List<String> list = Arrays.asList("as", "asda", "asd", "urff", "dfkjds", "hfad", "asd", "qadasd", "as", "asda",
"asd", "urff", "dfkjds", "hfad", "asd", "qadasd" + "as", "asda", "asd", "urff", "dfkjds", "hfad", "asd",
"qadasd", "as", "asda", "asd", "urff", "dfkjds", "hfad", "asd", "qadasd");
Method 1:
Set<String> set = new LinkedHashSet<>();
set.addAll(list);
for (String s : set) {
System.out.println(s + " : " + Collections.frequency(list, s));
}
Method 2:
int count = 1;
Map<String, Integer> map = new HashMap<>();
Set<String> set1 = new LinkedHashSet<>();
for (String s : list) {
if (!set1.add(s)) {
count = map.get(s) + 1;
}
map.put(s, count);
count = 1;
}
System.out.println(map);
​If you use Eclipse Collections, you can use a Bag. A MutableBag can be returned from any implementation of RichIterable by calling toBag().
MutableList<String> animals = Lists.mutable.with("bat", "owl", "bat", "bat");
MutableBag<String> bag = animals.toBag();
Assert.assertEquals(3, bag.occurrencesOf("bat"));
Assert.assertEquals(1, bag.occurrencesOf("owl"));
The HashBag implementation in Eclipse Collections is backed by a MutableObjectIntMap.
Note: I am a committer for Eclipse Collections.
Put the elements of the arraylist in the hashMap to count the frequency.
So do it the old fashioned way and roll your own:
Map<String, Integer> instances = new HashMap<String, Integer>();
void add(String name) {
Integer value = instances.get(name);
if (value == null) {
value = new Integer(0);
instances.put(name, value);
}
instances.put(name, value++);
}
Java 8 - another method
String searched = "bat";
long n = IntStream.range(0, animals.size())
.filter(i -> searched.equals(animals.get(i)))
.count();
package traversal;
import java.util.ArrayList;
import java.util.List;
public class Occurrance {
static int count;
public static void main(String[] args) {
List<String> ls = new ArrayList<String>();
ls.add("aa");
ls.add("aa");
ls.add("bb");
ls.add("cc");
ls.add("dd");
ls.add("ee");
ls.add("ee");
ls.add("aa");
ls.add("aa");
for (int i = 0; i < ls.size(); i++) {
if (ls.get(i) == "aa") {
count = count + 1;
}
}
System.out.println(count);
}
}
Output: 4
Integer[] spam = new Integer[] {1,2,2,3,4};
List<Integer> list=Arrays.asList(spam);
System.out.println(list.stream().collect(Collectors.groupingBy(Function.identity(),Collectors.counting())));
System.out.println(list.stream().collect(Collectors.groupingBy(Function.identity(),HashMap::new,Collectors.counting())));
output
{1=1, 2=2, 3=1, 4=1}
If you are a user of my ForEach DSL, it can be done with a Count query.
Count<String> query = Count.from(list);
for (Count<Foo> each: query) each.yield = "bat".equals(each.element);
int number = query.result();
I didn't want to make this case more difficult and made it with two iterators
I have a HashMap with LastName -> FirstName. And my method should delete items with dulicate FirstName.
public static void removeTheFirstNameDuplicates(HashMap<String, String> map)
{
Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator();
Iterator<Map.Entry<String, String>> iter2 = map.entrySet().iterator();
while(iter.hasNext())
{
Map.Entry<String, String> pair = iter.next();
String name = pair.getValue();
int i = 0;
while(iter2.hasNext())
{
Map.Entry<String, String> nextPair = iter2.next();
if (nextPair.getValue().equals(name))
i++;
}
if (i > 1)
iter.remove();
}
}
List<String> lst = new ArrayList<String>();
lst.add("Ram");
lst.add("Ram");
lst.add("Shiv");
lst.add("Boss");
Map<String, Integer> mp = new HashMap<String, Integer>();
for (String string : lst) {
if(mp.keySet().contains(string))
{
mp.put(string, mp.get(string)+1);
}else
{
mp.put(string, 1);
}
}
System.out.println("=mp="+mp);
Output:
=mp= {Ram=2, Boss=1, Shiv=1}
Map<String,Integer> hm = new HashMap<String, Integer>();
for(String i : animals) {
Integer j = hm.get(i);
hm.put(i,(j==null ? 1 : j+1));
}
for(Map.Entry<String, Integer> val : hm.entrySet()) {
System.out.println(val.getKey()+" occurs : "+val.getValue()+" times");
}
You can use groupingBy feature of Java 8 for your use case.
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<String> animals = new ArrayList<>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");
Map<String,Long> occurrenceMap =
animals.stream().collect(Collectors.groupingBy(Function.identity(),Collectors.counting()));
System.out.println("occurrenceMap:: " + occurrenceMap);
}
}
Output
occurrenceMap:: {bat=3, owl=1}

Categories