在Java中按键对Map进行排序

ne5o7dgx  于 2023-03-06  发布在  Java
关注(0)|答案(2)|浏览(143)

我检查了一些相关的问题,但没有回答我的问题。我试图排序Map升序字母,但我有问题。以下是目前的代码:

// provinceName is key
    public Map<String, String> regionMap() {
        return 
            em
            .createQuery(
                """
                SELECT DISTINCT p.provinceName AS prov_id ,p.provinceAbbreviation AS prov_value
                FROM CanadianPersonalIncomeTaxRate p         
                ORDER BY p.provinceName ASC
                """,
                Tuple.class
            )
            .getResultStream()
                //    .filter(prov_value -> prov_value.get())
                //    .sorted(Comparator.comparing(prov_value -> prov_value))
            .collect(
                Collectors.toMap(
                    tuple -> tuple.get(0, String.class),
                    tuple -> tuple.get(1, String.class)
                )
            );
    }

我正在考虑使用.sorted或TreeMap,但我愿意接受如何最干净/简单地做它的建议。任何帮助都非常感谢。

zujrkrfu

zujrkrfu1#

TreeMap和LinkedHashMap是满足上述需求的良好候选对象。
TreeMap是一个Map实现,它按排序顺序维护键。

return em.createQuery("""
            SELECT DISTINCT p.provinceName AS prov_id ,p.provinceAbbreviation AS prov_value
            FROM CanadianPersonalIncomeTaxRate p         
            ORDER BY p.provinceName ASC
            """, Tuple.class)
            .getResultStream()
            .collect(Collectors.toMap(
                    tuple -> tuple.get(0, String.class),
                    tuple -> tuple.get(1, String.class),
                    (a, b) -> a,TreeMap::new // use TreeMap to maintain sorted order
            ));

示例来维护插入顺序。

return em.createQuery("""
            SELECT DISTINCT p.provinceName AS prov_id ,p.provinceAbbreviation AS prov_value
            FROM CanadianPersonalIncomeTaxRate p         
            ORDER BY p.provinceAbbreviation ASC
            """, Tuple.class)
            .getResultStream()
            .sorted(Comparator.comparing(tuple -> tuple.get(1, String.class))) // sort by value
            .collect(Collectors.toMap(
                    tuple -> tuple.get(0, String.class),
                    tuple -> tuple.get(1, String.class),
                    (a, b) -> a,
                    LinkedHashMap::new // use LinkedHashMap to maintain insertion order
            ));
vbopmzt1

vbopmzt12#

通过TreeMap :: new

更改结果的声明以指示您希望Map按键排序。使用NavigableMap
这一点:

public Map<String, String> regionMap() {

...应该是:

public NavigableMap<String, String> regionMap() {

通过向Collectors.toMap调用传递另一个参数(Map工厂,如TreeMap :: new)来指定NavigableMap的具体实现。我们还必须传递一个merge函数,以决定在出现重复元素时如何处理。有关Collectors.toMap的重载,请参阅Javadoc。

Collectors.toMap(
                tuple -> tuple.get( 0 , String.class ) ,
                tuple -> tuple.get( 1 , String.class ) ,
                ( oldValue , newValue ) -> oldValue ) ,
                TreeMap :: new
            )

在这种方法中,数据库排序变得多余,因为您是在Java端排序,所以可以删除SQL行:

ORDER BY p.provinceName ASC

但是,按照预先排序的顺序向TreeMap添加元素可能有助于提高map的排序性能(这只是我的猜测),所以我会考虑保留ORDER BY
如果你想返回一个不可修改的Map,使用Collectors.toUnmodifiableMap和相同的参数。

示例代码

下面是一些示例代码。
首先,输入一些数据,来模拟你的数据库查询.

String[][] inputs =
        {
                { "Ontario" , "ON" } ,
                { "Québec" , "QA" } ,
                { "Nova Scotia" , "NS" } ,
                { "New Brunswick" , "NB" } ,
                { "Manatob" , "MB" } ,
                { "British Columbia" , "BC" } ,
                { "Prince Edward Island" , "PE" } ,
                { "Saskatchewan" , "SK" } ,
                { "Alberta" , "AB" } ,
                { "Newfoundland and Labrador" , "NL" }
        };

逻辑是,我们从数组的数组中检索每一行,通过从零开始的索引计数(0 & 1)从每一行/数组中提取第一个和第二个元素,然后将它们收集到一个NavigableMap中,具体来说是TreeMap

NavigableMap < String, String > map =
        Arrays
                .stream( inputs )
                .collect(
                        Collectors.toMap(
                                ( String[] input ) -> input[ 0 ] ,
                                ( String[] input ) -> input[ 1 ] ,
                                ( String oldValue , String newValue ) -> oldValue ,
                                TreeMap :: new
                        )
                );

或者,如果你不需要看到类型声明,使用下面的代码。你的情况很复杂,为了清晰起见,我个人会保留类型声明。这样做可以防止你在命名oldTuple , newTuple时犯的错误,而实际上合并的是String值对象oldValue, newValue

NavigableMap < String, String > map =
        Arrays
                .stream( inputs )
                .collect(
                        Collectors.toMap(
                                input -> input[ 0 ] ,
                                input -> input[ 1 ] ,
                                ( oldValue , newValue ) -> oldValue ,
                                TreeMap :: new
                        )
                );

结果:
map.toString()= {阿尔伯塔省= AB,不列颠哥伦比亚省= BC,马纳托布省= MB,新不伦瑞克省= NB,纽芬兰和拉布拉多省= NL,新斯科舍省= NS,安大略省= ON,爱德华王子岛省= PE,魁北克省= QA,萨斯喀彻温省= SK}
我们可以跳过不需要的行。

  • 添加对Stream :: filter的呼叫。
  • 传递一个Predicate。注意 predicate 中的!。我们正在过滤元素以 * 保留 *,而不是 * 丢失 *。
String[][] inputs =
        {
                { "Ontario" , "ON" } ,
                { "Federal" , "CAN" } ,  // <-- Unwanted row.
                { "Québec" , "QA" } ,
                { "Nova Scotia" , "NS" } ,
                { "New Brunswick" , "NB" } ,
                { "Manatob" , "MB" } ,
                { "British Columbia" , "BC" } ,
                { "Prince Edward Island" , "PE" } ,
                { "Saskatchewan" , "SK" } ,
                { "Alberta" , "AB" } ,
                { "Newfoundland and Labrador" , "NL" }
        };

NavigableMap < String, String > map =
        Arrays
                .stream( inputs )
                .filter( ( String[] input ) -> ! input[ 1 ].equalsIgnoreCase( "CAN" ) )
                .collect(
                        Collectors.toMap(
                                input -> input[ 0 ] ,
                                input -> input[ 1 ] ,
                                ( oldValue , newValue ) -> oldValue ,
                                TreeMap :: new
                        )
                );

相关问题