Get Distinct and Count values ββfrom object field in collection
I have List
objects Pin
( List<Pin>
) where the class Pin
has the following attributes:
String pinNumber, String pinType, Date insertDate
I would like to get a HashMap
s <String pinNumber, int count>
that has a separate pinNumber telling me how many different pinNumbers are in List<Pin>
and the count of each one.
This way I know to do this:
- Iterating through
List<Pin>
- Make sure it
HashMap
already contains the key pinNumber and: - Increase it or add it if it doesn't exist.
I would like to do the same for each of the fields of the object Pin
.
I'm sure there must be an easier way to do this?
Maybe Guava has something simpler?
source to share
If you have the ability to use Java 8 (and since what you want to do basically sounds like a "group by" operation), this can be solved in an elegant way using the new Stream API (as hinted by user vallismortis):
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
List<Pin> pins = Arrays.asList(
new Pin("PIN-1", "T1", new Date()),
new Pin("PIN-1", "T2", new Date()),
new Pin("PIN-1", "T3", new Date()),
new Pin("PIN-2", "T2", new Date()),
new Pin("PIN-2", "T2", new Date()),
new Pin("PIN-3", "T2", new Date())
);
Map<String, Long> map = pins.stream().collect(groupingBy(Pin::getPinNumber, counting()));
System.out.println("map = " + map);
}
}
class Pin {
String pinNumber;
String pinType;
Date insertDate;
public Pin(String pinNumber, String pinType, Date insertDate) {
this.pinNumber = pinNumber;
this.pinType = pinType;
this.insertDate = insertDate;
}
public String getPinNumber() {
return pinNumber;
}
public String getPinType() {
return pinType;
}
public Date getInsertDate() {
return insertDate;
}
}
Output:
map = {PIN-1=3, PIN-3=1, PIN-2=2}
source to share
You don't need Guava for this. You can use the standard Java 8 functions. One way is with streams, but these are not suitable if you need to compute counts for multiple fields. You can use the method instead Map.merge
:
Map<String, Integer> byNumber = new HashMap<>();
Map<String, Integer> byType = new HashMap<>();
Map<Date, Integer> byInsertDate = new HashMap<>();
listOfPins.forEach(pin -> {
byNumber.merge(pin.getPinNumber(), 1, Integer::sum);
byType.merge(pin.getPinType(), 1, Integer::sum);
byInsertDate.merge(pin.getInsertDate(), 1, Integer::sum);
});
This has the advantage that it can only be done in one iteration over listOfPins
, whereas with streams, you need one pass for each field.
source to share
An even simpler implementation:
public static void main(String[] args) {
List<Pin> pinList = new ArrayList<Pin>();
// Add employee to list
pinList.add(new Pin("1234", "local", null));
pinList.add(new Pin("2345", "extra", null));
pinList.add(new Pin("3456", "extra", null));
pinList.add(new Pin("1234", "local", null));
Map<String, Integer> mapPinNumber = new HashMap<String, Integer>();
for (Pin pin : pinList) {
Integer cnt = mapPinNumber.get(pin.getPinNumber());
mapPinNumber.put(pin.getPinNumber(), (cnt == null) ? 1 : ++cnt);
}
printMap(mapPinNumber);
Map<String, Integer> mapPinType = new HashMap<String, Integer>();
for (Pin pin : pinList) {
Integer cnt = mapPinType.get(pin.getPinType());
mapPinType.put(pin.getPinType(), (cnt == null) ? 1 : ++cnt);
}
printMap(mapPinType);
}
private static void printMap(Map<String, Integer> map) {
String key;
int value;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
key = entry.getKey();
value = entry.getValue();
System.out.println(key + ": " + value);
}
}
source to share
Here's one possible solution if you don't want to rely on another library and want to maintain backward compatibility with older JVMs. It's not the best or the easiest to use, but it works.
FrequencyUtil.java
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
public class FrequencyUtil
{
private static FrequencyUtil SINGLETON;
private static FrequencyUtil getInstance()
{
if (FrequencyUtil.SINGLETON == null)
{
FrequencyUtil.SINGLETON = new FrequencyUtil();
}
return FrequencyUtil.SINGLETON;
}
public static <X> Map<X, Integer> frequency(final Collection<X> objects, final Comparator<X> comparator)
{
Map<ComparatorWrapper<X>, Integer> frequencies = new HashMap<ComparatorWrapper<X>, Integer>();
for (X object : objects)
{
ComparatorWrapper<X> wrapper = FrequencyUtil.getInstance().new ComparatorWrapper<X>(object, comparator);
Integer count = frequencies.get(wrapper);
frequencies.put(wrapper, (count == null) ? 1 : count + 1);
}
// unwrap the frequencies
Map<X, Integer> frequenciesRaw = new HashMap<X, Integer>();
for (ComparatorWrapper<X> wrapper : frequencies.keySet())
{
frequenciesRaw.put(wrapper.getObject(), frequencies.get(wrapper));
}
return frequenciesRaw;
}
private class ComparatorWrapper<Z>
{
private Z object;
private Comparator<Z> comparator;
ComparatorWrapper(final Z object, final Comparator<Z> comparator)
{
this.object = object;
this.comparator = comparator;
return;
}
public Z getObject()
{
return this.object;
}
@Override
public int hashCode()
{
return 0;
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj)
{
if ((obj == null) || !(obj instanceof ComparatorWrapper))
{
return false;
}
return this.comparator.compare(this.object, ((ComparatorWrapper<Z>) obj).getObject()) == 0;
}
}
}
FrequencyTest.java
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
public class FrequencyTest
{
public void test()
{
List<Pin> pins = new ArrayList<Pin>();
Pin pin1 = new Pin();
Pin pin2 = new Pin();
Pin pin3 = new Pin();
pin1.setPinType("typeA");
pin2.setPinType("typeB");
pin3.setPinType("typeA");
pin1.setPinNumber("50");
pin2.setPinNumber("50");
pin3.setPinNumber("80");
pin1.setInsertDate(Calendar.getInstance().getTime());
pin2.setInsertDate(Calendar.getInstance().getTime());
pin3.setInsertDate(Calendar.getInstance().getTime());
pins.add(pin1);
pins.add(pin2);
pins.add(pin3);
Comparator<Pin> pinTypeComparator = new Comparator<Pin>()
{
@Override
public int compare(final Pin o1, final Pin o2)
{
return o1.getPinType().compareTo(o2.getPinType());
}
};
Comparator<Pin> pinNumberComparator = new Comparator<Pin>()
{
@Override
public int compare(final Pin o1, final Pin o2)
{
return o1.getPinNumber().compareTo(o2.getPinNumber());
}
};
Comparator<Pin> insertDateComparator = new Comparator<Pin>()
{
private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
@Override
public int compare(final Pin o1, final Pin o2)
{
return this.sdf.format(o1.getInsertDate()).compareTo(this.sdf.format(o2.getInsertDate()));
}
};
Map<Pin, Integer> pinTypeFrequency = FrequencyUtil.frequency(pins, pinTypeComparator);
Map<Pin, Integer> pinNumberFrequency = FrequencyUtil.frequency(pins, pinNumberComparator);
Map<Pin, Integer> insertDateFrequency = FrequencyUtil.frequency(pins, insertDateComparator);
System.out.println("pinTypeFrequency");
for (Pin pin : pinTypeFrequency.keySet())
{
System.out.println(pin.getPinType() + ": " + pinTypeFrequency.get(pin));
}
System.out.println();
System.out.println("pinNumberFrequency");
for (Pin pin : pinNumberFrequency.keySet())
{
System.out.println(pin.getPinNumber() + ": " + pinNumberFrequency.get(pin));
}
System.out.println();
System.out.println("insertDateFrequency");
for (Pin pin : insertDateFrequency.keySet())
{
System.out.println(pin.getInsertDate().toString() + ": " + insertDateFrequency.get(pin));
}
}
public static void main(String[] args)
{
try
{
new FrequencyTest().test();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
}
Pin.java
import java.util.Date;
public class Pin
{
private String pinNumber;
private String pinType;
private Date insertDate;
public String getPinNumber()
{
return pinNumber;
}
public void setPinNumber(String pinNumber)
{
this.pinNumber = pinNumber;
}
public String getPinType()
{
return pinType;
}
public void setPinType(String pinType)
{
this.pinType = pinType;
}
public Date getInsertDate()
{
return insertDate;
}
public void setInsertDate(Date insertDate)
{
this.insertDate = insertDate;
}
}
Output
pinTypeFrequency typeB: 1 typeA: 2
pinNumberFrequency 80: 1 50: 2
insertDateFrequency Mon Jun 22 12:09:19 EDT 2015: 3
Just for an interesting and historical reference, Java version 1.2:
FrequencyUtil12.java
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class FrequencyUtil
{
private static FrequencyUtil SINGLETON;
private static FrequencyUtil getInstance()
{
if (FrequencyUtil.SINGLETON == null)
{
FrequencyUtil.SINGLETON = new FrequencyUtil();
}
return FrequencyUtil.SINGLETON;
}
public static Map frequency(final Collection objects, final Comparator comparator)
{
Map frequencies = new HashMap();
Iterator iter = objects.iterator();
while (iter.hasNext())
{
Object object = iter.next();
ComparatorWrapper wrapper = FrequencyUtil.getInstance().new ComparatorWrapper(object, comparator);
Integer count = (Integer) frequencies.get(wrapper);
frequencies.put(wrapper, (count == null) ? 1 : count + 1);
}
// unwrap the frequencies
Map frequenciesRaw = new HashMap();
Iterator keys = frequencies.keySet().iterator();
while (keys.hasNext())
{
ComparatorWrapper wrapper = (ComparatorWrapper) keys.next();
frequenciesRaw.put(wrapper.getObject(), frequencies.get(wrapper));
}
return frequenciesRaw;
}
private class ComparatorWrapper
{
private Object object;
private Comparator comparator;
ComparatorWrapper(final Object object, final Comparator comparator)
{
this.object = object;
this.comparator = comparator;
return;
}
public Object getObject()
{
return this.object;
}
public int hashCode()
{
return 0;
}
public boolean equals(Object obj)
{
if ((obj == null) || !(obj instanceof ComparatorWrapper))
{
return false;
}
return this.comparator.compare(this.object, ((ComparatorWrapper) obj).getObject()) == 0;
}
}
}
source to share