【问题标题】:Get the Distinct and count values from a field of an object in a Collection从集合中对象的字段中获取 Distinct 和 count 值
【发布时间】:2015-06-22 12:57:24
【问题描述】:

我有一个 ListPin 对象 (List<Pin>),其中 Pin 类具有以下属性:

String pinNumber, String pinType, Date insertDate

我想得到一个 HashMap<String pinNumber, int count>,它们有不同的 pinNumber,告诉我 List<Pin> 中有多少不同的 pinNumber 以及每个的计数。

所以我知道这样做的方法是:

  • 遍历List<Pin>
  • 检查HashMap是否已经包含pinNumber的键值和:
  • 如果不存在则增加或添加。

我想对 Pin 对象中的每个字段执行相同的操作。

我确定应该有更简单的方法来做到这一点?

也许番石榴有更简单的东西?

【问题讨论】:

  • 您能否阐明“对每个字段执行相同操作”的含义?你的意思是你想为每个字段单独计数,或者你想使用所有字段进行比较(在这种情况下你应该覆盖Pin.equals()方法)。
  • 我想对任何字段执行相同的任务。所以是的,每个字段的单独计数。

标签: java design-patterns collections guava


【解决方案1】:

如果您有可能使用 Java 8(因为您想要做的基本上听起来像是“分组依据”操作),这可以使用新的 Stream API 以一种优雅的方式解决(正如用户 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;
    }
}

输出:

map = {PIN-1=3, PIN-3=1, PIN-2=2}

【讨论】:

    【解决方案2】:

    你不需要番石榴。您可以使用标准的 Java 8 功能。一种方法是使用流,但是如果您需要计算多个字段的计数,则它们不适合。相反,您可以使用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);
        });
    

    这样做的好处是它可以在listOfPins 上仅通过一次迭代来完成,而对于流,您需要为每个字段传递一次。

    【讨论】:

      【解决方案3】:

      如果您不想依赖另一个库并希望保持与旧版 JVM 的向后兼容性,这是一种可能的解决方案。它不是最好的,也不是最容易使用的,但它确实有效。

      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;
          }
      }
      

      输出

      pinTypeFrequency typeB: 1 typeA: 2

      pinNumberFrequency 80:1 50:2

      插入日期频率 2015 年 6 月 22 日星期一 12:09:19 EDT:3

      仅供娱乐和历史参考,Java 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;
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

        更简单的实现:

        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);
        
            }
        }
        

        【讨论】:

        • 我不明白这将如何可行。 (Collections.frequency)[grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… 仅使用单一方法评估相等性。每个字段都需要不同的相等性测试。
        • 您需要覆盖等于。我已经提到过。
        • 您能否为问题中提供的案例展示一个工作示例?
        • 这仅适用于单个字段:pinNumber,但不会为每个单独的字段(即 pinNumberpinTypeinsertDate)生成单独的计数,如问题。
        • 我添加了一个更简单的实现。
        猜你喜欢
        • 2014-02-23
        • 2014-07-20
        • 2020-07-21
        • 1970-01-01
        • 2020-03-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-06
        相关资源
        最近更新 更多