假设我有List
个Employee
个对象。 Employee
对象具有getDepartment
方法,该方法返回Department
对象。我想遍历该列表以找到Employee
s最多的部门(即Department
最常返回的getDepartment
对象)。最快的方法是什么?
public class Employee{
static allEmployees = new ArrayList<Employee>();
int id;
Department department;
public Employee(int id, Department department){
this.id = id;
this.department = department;
allEmployees.add(this);
}
public Department getDepartment(){
return department;
}
public static List<Employee> getAllEmployees(){
return allEmployees;
}
}
public class Department{
int id;
String name;
public Department(int id){
this.id = id;
}
public String getName(){
return name;
}
}
如果有两个部门的员工人数相同,则返回哪个部门无关紧要。
谢谢!
答案 0 :(得分:3)
创建部门ID地图 - &gt;计数。
通过id,您可以获得所有计数的集合。您还可以维护一个最大项目,该项目是对具有最高计数的地图条目的引用。
算法类似于:
1)初始化Map和currentMax
2)循环员工
3)为每位员工,获得其部门编号
4)做一些像map.get(currentId)的事情
a)如果当前计数为空,则将其初始化为
5)递增计数
6)如果增加的计数是> currentMax,更新currentMax
该算法将在O(n)中运行;我认为你不能比这更好。它的空间复杂度也是O(n),因为计数的数量与输入的大小成正比。
如果需要,可以创建一个使用合成的类(即包含一个Map和一个List),并管理保持指向具有最高计数的Entry的指针。这样就可以正确封装这部分功能。这种方法的更大好处是它允许您在将项目输入列表时维护计数(您将代理将员工添加到列表中的方法,以便更新地图计数器)。可能会有点矫枉过正。
答案 1 :(得分:2)
这是一个vanilla Java 8解决方案:
Employee.getAllEmployees()
.stream()
.collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting()))
.entrySet()
.stream()
.max(Comparator.comparing(Entry::getValue))
.ifPresent(System.out::println);
最多两次通过员工名单。如果您愿意添加第三方依赖项,则使用jOOλ的等效解决方案是:
Seq.seq(Employee.getAllEmployees())
.grouped(Employee::getDepartment, Agg.count())
.maxBy(Tuple2::v2)
.ifPresent(System.out::println);
(免责声明:我为jOOλ背后的公司工作)
答案 2 :(得分:1)
我会使用Guava执行类似的操作:
Multiset<Department> departments = HashMultiset.create();
for (Employee employee : employees) {
departments.add(employee.getDepartment());
}
Multiset.Entry<Department> max = null;
for (Multiset.Entry<Department> department : departments.entrySet()) {
if (max == null || department.getCount() > max.getCount()) {
max = department;
}
}
您需要在equals
上正确实施hashCode
和Department
才能实现此目的。
还有一个问题here提到了将来创建“排行榜”类型Multiset
的可能性,它会根据其中包含的每个条目的数量来维护订单。
答案 3 :(得分:0)
由于您只想计算员工,因此制作地图相对容易。
HashMap<Department, Integer> departmentCounter;
将部门映射到员工数量(您为每个员工增加计数)。或者,您可以使用列表将整个Employee存储在地图中:
HashMap<Department, List<Employee>> departmentCounter;
然后查看列表的大小。
如果您不知道如何使用该类,您可以查看HashMap文档: http://download.oracle.com/javase/1.4.2/docs/api/java/util/HashMap.html
提示:您需要使用HashMap.keySet()来查看已输入的部门。
答案 4 :(得分:0)
我会这样做,modulo == null和isEmpty检查:
public static <C> Multimap<Integer, C> getFrequencyMultimap(final Collection<C> collection,
final Ordering<Integer> ordering) {
@SuppressWarnings("unchecked")
Multimap<Integer, C> result = TreeMultimap.create(ordering, (Comparator<C>) Ordering.natural());
for (C element : collection) {
result.put(Collections.frequency(collection, element), element);
}
return result;
}
public static <C> Collection<C> getMostFrequentElements(final Collection<C> collection) {
Ordering<Integer> reverseIntegerOrdering = Ordering.natural().reverse();
Multimap<Integer, C> frequencyMap = getFrequencyMultimap(collection, reverseIntegerOrdering);
return frequencyMap.get(Iterables.getFirst(frequencyMap.keySet(), null));
}
还有CollectionUtils.getCardinalityMap()将完成第一种方法的工作,但这更灵活,也更加狡猾。
请记住,C类应该很好地实现,即具有equals(),hashCode()并实现Comparable。
您可以使用它:
Collection<Dummy> result = LambdaUtils.getMostFrequentElements(list);
作为奖励,你也可以用类似的方法得到频率较低的元素,只需用Ordering.natural()提供第一个方法,不要反转它。