在Java中对HashMap进行排序

2023/06/07

1. 概述

在这个快速教程中,我们将学习如何在Java中对HashMap进行排序。

更具体地说,我们将使用以下方法按键或值对HashMap条目进行排序:

  • TreeMap
  • ArrayList和Collections.sort()
  • TreeSet
  • 使用Stream API
  • 使用Guava库

2. 使用TreeMap

正如我们所知,TreeMap中的键是使用它们的自然顺序排序的。当我们想按键对键值对进行排序时,这是一个很好的解决方案。因此我们的想法是将我们的HashMap中的所有数据推送到TreeMap中。

首先,让我们定义一个HashMap并使用一些数据初始化它:

Map<String, Employee> map = new HashMap<>();

Employee employee1 = new Employee(1L, "Mher");
map.put(employee1.getName(), employee1);
Employee employee2 = new Employee(22L, "Annie");
map.put(employee2.getName(), employee2);
Employee employee3 = new Employee(8L, "John");
map.put(employee3.getName(), employee3);
Employee employee4 = new Employee(2L, "George");
map.put(employee4.getName(), employee4);

对于Employee类,请注意我们实现了Comparable

public class Employee implements Comparable<Employee> {

    private Long id;
    private String name;

    // constructor, getters, setters

    // override equals and hashCode
    @Override
    public int compareTo(Employee employee) {
        return (int)(this.id - employee.getId());
    }
}

接下来,我们使用其构造函数将条目存储在TreeMap中:

TreeMap<String, Employee> sorted = new TreeMap<>(map);

我们也可以使用putAll方法来复制数据:

TreeMap<String, Employee> sorted = new TreeMap<>();
sorted.putAll(map);

就是这样!为了确保我们的Map条目按键排序,让我们打印出来:

Annie=Employee{id=22, name='Annie'}
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Mher=Employee{id=1, name='Mher'}

正如我们所见,键是按自然顺序排序的。

3. 使用ArrayList

当然,我们可以借助ArrayList对Map的条目进行排序。与前一种方法的主要区别在于我们在这里不维护Map接口

3.1 按键排序

让我们将键集加载到ArrayList中:

List<String> employeeByKey = new ArrayList<>(map.keySet());
Collections.sort(employeeByKey);

输出是:

[Annie, George, John, Mher]

3.2 按值排序

现在,如果我们想按Employee对象的id字段对Map值进行排序怎么办?我们也可以为此使用ArrayList。

首先,让我们将值复制到列表中:

List<Employee> employeeById = new ArrayList<>(map.values());

然后我们对其进行排序:

Collections.sort(employeeById);

请记住,这是有效的,因为Employee实现了Comparable接口。否则,我们需要为Collections.sort调用定义一个手动比较器。

为了检查结果,我们打印employeeById:

[Employee{id=1, name='Mher'}, 
Employee{id=2, name='George'}, 
Employee{id=8, name='John'}, 
Employee{id=22, name='Annie'}]

如我们所见,对象按其id字段排序。

4. 使用TreeSet

如果我们不想在排序集合中接受重复值,TreeSet有一个很好的解决方案

首先,让我们在初始Map中添加一些重复的条目:

Employee employee5 = new Employee(1L, "Mher");
map.put(employee5.getName(), employee5);
Employee employee6 = new Employee(22L, "Annie");
map.put(employee6.getName(), employee6);

4.1 按键排序

要按其键条目对Map进行排序:

SortedSet<String> keySet = new TreeSet<>(map.keySet());

让我们打印keySet并查看输出:

[Annie, George, John, Mher]

现在我们对Map键进行了排序,没有重复项。

4.2 按值排序

同样,对于Map值,转换代码如下所示:

SortedSet<Employee> values = new TreeSet<>(map.values());

结果是:

[Employee{id=1, name='Mher'}, 
Employee{id=2, name='George'}, 
Employee{id=8, name='John'}, 
Employee{id=22, name='Annie'}]

如我们所见,输出中没有重复项。当我们覆盖equals和hashCode时,这适用于自定义对象

5. 使用Lambda和Stream

从Java 8开始,我们可以使用Stream API和Lambda表达式对Map进行排序。我们所需要做的就是通过Map的流管道调用sorted方法。

5.1 按键排序

要按键排序,我们使用comparingByKey比较器:

map.entrySet()
    .stream()
    .sorted(Map.Entry.<String, Employee>comparingByKey())
    .forEach(System.out::println);

最后的forEach阶段打印出结果:

Annie=Employee{id=22, name='Annie'}
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Mher=Employee{id=1, name='Mher'}

默认情况下,排序方式是升序。

5.2 按值排序

当然,我们也可以按Employee对象排序:

map.entrySet()
    .stream()
    .sorted(Map.Entry.comparingByValue())
    .forEach(System.out::println);

如我们所见,上面的代码打印出一个按Employee对象的id字段排序的Map:

Mher=Employee{id=1, name='Mher'}
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}

此外,我们可以将结果收集到新Map中:

Map<String, Employee> result = map.entrySet()
    .stream()
    .sorted(Map.Entry.comparingByValue())
    .collect(Collectors.toMap(
        Map.Entry::getKey, 
        Map.Entry::getValue, 
        (oldValue, newValue) -> oldValue, LinkedHashMap::new));

请注意,我们将结果收集到LinkedHashMap中。默认情况下,Collectors.toMap返回一个新的HashMap,但正如我们所知,HashMap不保证迭代顺序,而LinkedHashMap可以。

6. 使用Guava

最后,允许我们对HashMap进行排序的库是Guava。在开始之前,检查一下我们关于Guava Map的文章会很有用。

首先,让我们声明一个Ordering,因为我们想按Employee的id字段对Map进行排序:

Ordering naturalOrdering = Ordering.natural()
    .onResultOf(Functions.forMap(map, null));

现在我们只需要使用ImmutableSortedMap来说明结果:

ImmutableSortedMap.copyOf(map, naturalOrdering);

再一次,输出是一个按id字段排序的Map:

Mher=Employee{id=1, name='Mher'}
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}

7. 总结

在本文中,我们回顾了多种按键或值对HashMap进行排序的方法。

当属性是自定义类时,我们还学习了如何通过实现Comparable来做到这一点。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章