【问题标题】:Sort a map with "MMMyyyy" as key以“MMMyyyy”为键对地图进行排序
【发布时间】:2015-11-07 00:11:25
【问题描述】:

我有一张地图,其键为 MMMyyyy 格式,我需要按月份排序。 输入:

unsorted: {
 "Dec2010": 1,
 "Apr2010": 1,
 "Feb2010": 0,
 "Nov2010": 2,
 "Mar2010": 0,
 "Jun2010": 2,
 "Sep2010": 1,
 "May2010": 0,
 "Oct2010": 1,
 "Jul2010": 0,
 "Aug2010": 0,
 "Jan2010": 1
}

排序后应该如下所示:

sorted: {
 "Jan2010": 1
 "Feb2010": 0,
 "Mar2010": 0,
 "Apr2010": 1,
 "May2010": 0,
 "Jun2010": 2,
 "Jul2010": 0,
 "Aug2010": 0,
 "Sep2010": 1,
 "Oct2010": 1,
 "Nov2010": 2,
 "Dec2010": 1,     
}

目前我正在使用按字母顺序对其进行排序的树形图,但我如何根据月份层次对其进行排序。

代码:

    Map<String, Integer> unsorted = new HashMap<>();
    unsorted.put("Dec2010", 1);
    unsorted.put("Apr2010", 1);
    unsorted.put("Feb2010", 0);
    unsorted.put("Nov2010", 2);
    unsorted.put("Mar2010", 0);
    unsorted.put("Jun2010", 2);
    unsorted.put("Sep2010", 1);
    unsorted.put("May2010", 0);
    unsorted.put("Oct2010", 1);
    unsorted.put("Jul2010", 0);
    unsorted.put("Aug2010", 0);
    unsorted.put("Jan2010", 1);

    System.out.println("\nSorted......");
    Map<String, Integer> sorted = new TreeMap<>(unsorted);
    for (Map.Entry<String, Integer> entry : sorted.entrySet()) {
        System.out.println("Key : " + entry.getKey()
                + " Value : " + entry.getValue());
    }

【问题讨论】:

  • 你能分享你到目前为止所做的一切吗?
  • 我已经更新了我正在使用的当前示例代码。
  • 不要将您的月份和年份作为字符串存储在地图中。使用适当的日期对象。在这种情况下,这意味着 YearMonth 对象。只有当需要给出字符串输出时,才将YearMonth格式化成所需格式的字符串。

标签: java sorting hashmap simpledateformat treemap


【解决方案1】:

让我们尝试使用自定义比较器:

public class Main {

    public static void main(String[] args) {

        final SimpleDateFormat df = new SimpleDateFormat("MMMyyyy");

        Map<String, Integer> map = new TreeMap<>(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                String s1 = (String) o1;
                String s2 = (String) o2;
                try {
                    return df.parse(s1).compareTo(df.parse(s2));
                } catch (ParseException e) {
                    throw new RuntimeException("Bad date format");
                }
            };
        });

        map.put("Dec2011",1);
        map.put("Jan2011",0);
        map.put("Feb2011",1);
        map.put("Mar2011",0);
        map.put("Oct2011",1);
        map.put("Sep2011",0);

        for(Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }

    }
}

输出:

Jan2011 -> 0
Feb2011 -> 1
Mar2011 -> 0
Sep2011 -> 0
Oct2011 -> 1
Dec2011 -> 1

【讨论】:

  • 谢谢 mzc,这就是我要找的。​​span>
  • 我认为你可以简化为 Map map = new TreeMap((o1, o2) -> { try { return df.parse(o1).compareTo(df.parse (o2)); } catch (ParseException e) { throw new RuntimeException("Bad date format"); } });
【解决方案2】:

旧的日期时间 API(java.util 日期时间类型及其格式化 API,SimpleDateFormat)已过时且容易出错。建议完全停止使用,改用java.timemodern date-time API*

Java SE 8

import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> unsorted = new HashMap<>();
        unsorted.put("Dec2010", 1);
        unsorted.put("Apr2010", 1);
        unsorted.put("Feb2010", 0);
        unsorted.put("Nov2010", 2);
        unsorted.put("Mar2010", 0);
        unsorted.put("Jun2010", 2);
        unsorted.put("Sep2010", 1);
        unsorted.put("May2010", 0);
        unsorted.put("Oct2010", 1);
        unsorted.put("Jul2010", 0);
        unsorted.put("Aug2010", 0);
        unsorted.put("Jan2010", 1);

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MMMuuuu", Locale.ENGLISH);
        Comparator<String> comparator = (s1, s2) -> YearMonth.parse(s1, dtf).compareTo(YearMonth.parse(s2, dtf));
        Map<String, Integer> sorted = new TreeMap<>(comparator);
        sorted.putAll(unsorted);
        System.out.println(sorted);
    }
}

输出:

{Jan2010=1, Feb2010=0, Mar2010=0, Apr2010=1, May2010=0, Jun2010=2, Jul2010=0, Aug2010=0, Sep2010=1, Oct2010=1, Nov2010=2, Dec2010=1}

Trail: Date Time 了解有关 modern date-time API* 的更多信息。

Java SE 9

Java SE 9 引入了Map.ofEntries,您可以使用它来初始化您的Map&lt;String, Integer&gt; unsorted

import static java.util.Map.entry;

import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> unsorted = Map.ofEntries(
                                            entry("Dec2010", 1),
                                            entry("Apr2010", 1),
                                            entry("Feb2010", 0),
                                            entry("Nov2010", 2),
                                            entry("Mar2010", 0),
                                            entry("Jun2010", 2),
                                            entry("Sep2010", 1),
                                            entry("May2010", 0),
                                            entry("Oct2010", 1),
                                            entry("Jul2010", 0),
                                            entry("Aug2010", 0),
                                            entry("Jan2010", 1)
                                        );

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MMMuuuu", Locale.ENGLISH);
        Comparator<String> comparator = (s1, s2) -> YearMonth.parse(s1, dtf).compareTo(YearMonth.parse(s2, dtf));         
        Map<String, Integer> sorted = new TreeMap<>(comparator); 
        sorted.putAll(unsorted);
        System.out.println(sorted);
    }
}

使用单个语句进行排序和映射:

您可以利用强大的Stream API(教程:12)来完成您的工作。

import static java.util.Map.entry;

import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> unsorted = Map.ofEntries(
                                            entry("Dec2010", 1),
                                            entry("Apr2010", 1),
                                            entry("Feb2010", 0),
                                            entry("Nov2010", 2),
                                            entry("Mar2010", 0),
                                            entry("Jun2010", 2),
                                            entry("Sep2010", 1),
                                            entry("May2010", 0),
                                            entry("Oct2010", 1),
                                            entry("Jul2010", 0),
                                            entry("Aug2010", 0),
                                            entry("Jan2010", 1)
                                        );

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MMMuuuu", Locale.ENGLISH);
        Map<String, Integer> sorted = unsorted
                                        .entrySet()
                                        .stream()
                                        .collect(Collectors.toMap(
                                            e -> YearMonth.parse(e.getKey(), dtf),
                                            e -> e.getValue(),
                                            (v1, v2) -> v1,
                                            TreeMap::new
                                         )) // Returns TreeMap<YearMonth, Integer>
                                        .entrySet()
                                        .stream()
                                        .collect(Collectors.toMap(
                                            e -> dtf.format(e.getKey()),
                                            e -> e.getValue(),
                                            (v1, v2) -> v1,
                                            LinkedHashMap::new
                                         ));
        
        System.out.println(sorted);
    }
}

通过documentation了解更多关于Collectors#toMap的信息。


* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7 . 如果您正在为一个 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

【讨论】:

    【解决方案3】:

    实际上,使用TreeMap 和自定义Comparator,答案很简单直接

    Map<String, Integer> map = new HashMap<>();
    map.put("Dec2010", 1);
    map.put("Apr2010", 1);
    map.put("Feb2010", 0);
    map.put("Nov2010", 2);
    map.put("Mar2010", 0);
    map.put("Jun2010", 2);
    map.put("Sep2010", 1);
    map.put("May2010", 0);
    map.put("Oct2010", 1);
    map.put("Jul2010", 0);
    map.put("Aug2010", 0);
    map.put("Jan2010", 1);
    
    Map<String, Integer> sortedMap = new TreeMap<>(Comparator.comparing(text -> YearMonth.parse(text, DateTimeFormatter.ofPattern("MMMyyyy", Locale.ENGLISH))));
    sortedMap.putAll(map);
    
    sortedMap.entrySet().forEach(System.out::println);
    

    根据Arvind Kumar Avinash 的建议,这里是使用Eclipse Collections 的示例

    这个例子非常相似,只是它有一个重载的构造函数,接受源映射作为第二个参数

    Map<String, Integer> sortedMap = new TreeSortedMap<>(
        Comparator.comparing(text -> YearMonth.parse(text, DateTimeFormatter.ofPattern("MMMyyyy", Locale.ENGLISH))), 
        map
    );
    
    sortedMap.entrySet().forEach(System.out::println);
    

    两个结果都是

    Jan2010=1
    Feb2010=0
    Mar2010=0
    Apr2010=1
    May2010=0
    Jun2010=2
    Jul2010=0
    Aug2010=0
    Sep2010=1
    Oct2010=1
    Nov2010=2
    Dec2010=1
    

    【讨论】:

    • 使用 Eclipse Collections 的不错的单语句解决方案!在不使用 Eclipse Collections 的情况下,是否有更短的方法来完成我使用单语句所做的事情?我可能错过了什么。
    • Here 是 Yassin 使用 Eclipse Collections 的另一个不错的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-26
    • 1970-01-01
    • 2020-05-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多