如何将特定的HashMap条目移动到映射的末尾?

68

如何将特定的HashMap条目移动到最后一个位置?

例如,我有以下HashMap值:

HashMap<String,Integer> map = new HashMap<String,Integer>();

map= {Not-Specified 1, test 2, testtest 3};

"Not-Specified"可能出现在任何位置,可以出现在首位或地图中间。但我想将"Not-Specified"移动到最后一个位置。

我该如何做到这一点?


2
你能解释一下为什么要这样做吗?也许使用HashMap并不是解决你的问题的最佳方案。 - Soundlink
请参见https://dev59.com/cHI-5IYBdhLWcg3wR2Jr。 - rogerdpack
8个回答

168

简单回答您的问题:

默认情况下,地图没有最后一项,这不是它们的约定部分。


顺便提一下:最好根据接口编写代码,而不是实现类(参见Joshua Bloch的《Effective Java》第8章,第52条:通过接口引用对象)。

因此,您的声明应该如下:

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

所有的地图都共享一个通用契约,因此客户端不需要知道它属于什么类型的地图,除非他指定具有扩展契约的子接口。


可能的解决方案

有序映射:

有一个子接口SortedMap,它通过基于顺序的查找方法扩展了映射接口,并且它还有一个子接口NavigableMap进一步扩展了它。该接口的标准实现TreeMap允许您按自然排序(如果它们实现了Comparable接口)或按提供的Comparator对条目进行排序。

您可以通过lastEntry方法访问最后一个条目:

NavigableMap<String,Integer> map = new TreeMap<String, Integer>();
// add some entries
Entry<String, Integer> lastEntry = map.lastEntry();

链接映射:

还有一种特殊情况是LinkedHashMap,它是一个HashMap实现,可以存储插入键的顺序。但是,没有接口来支持此功能,也没有直接访问最后一个键的方法。您只能通过使用列表等技巧来实现:

Map<String,String> map = new LinkedHashMap<String, Integer>();
// add some entries
List<Entry<String,Integer>> entryList =
    new ArrayList<Map.Entry<String, Integer>>(map.entrySet());
Entry<String, Integer> lastEntry =
    entryList.get(entryList.size()-1);

正确解决方案:

由于您无法控制插入顺序,因此应使用NavigableMap接口,即编写一个比较器将Not-Specified条目放在最后。

以下是示例:

final NavigableMap<String,Integer> map = 
        new TreeMap<String, Integer>(new Comparator<String>() {
    public int compare(final String o1, final String o2) {
        int result;
        if("Not-Specified".equals(o1)) {
            result=1;
        } else if("Not-Specified".equals(o2)) {
            result=-1;
        } else {
            result =o1.compareTo(o2);
        }
        return result;
    }

});
map.put("test", Integer.valueOf(2));
map.put("Not-Specified", Integer.valueOf(1));
map.put("testtest", Integer.valueOf(3));
final Entry<String, Integer> lastEntry = map.lastEntry();
System.out.println("Last key: "+lastEntry.getKey()
         + ", last value: "+lastEntry.getValue());

输出:

最后一个键:未指定,最后一个值:1

使用HashMap的解决方案:

如果您必须依赖于HashMap,则仍然有一种解决方案,使用a)上述比较器的修改版本,b)使用Map的entrySet初始化的List和c)Collections.sort()助手方法:

    final Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("test", Integer.valueOf(2));
    map.put("Not-Specified", Integer.valueOf(1));
    map.put("testtest", Integer.valueOf(3));

    final List<Entry<String, Integer>> entries =
        new ArrayList<Entry<String, Integer>>(map.entrySet());
    Collections.sort(entries, new Comparator<Entry<String, Integer>>(){

        public int compareKeys(final String o1, final String o2){
            int result;
            if("Not-Specified".equals(o1)){
                result = 1;
            } else if("Not-Specified".equals(o2)){
                result = -1;
            } else{
                result = o1.compareTo(o2);
            }
            return result;
        }

        @Override
        public int compare(final Entry<String, Integer> o1,
            final Entry<String, Integer> o2){
            return this.compareKeys(o1.getKey(), o2.getKey());
        }

    });

    final Entry<String, Integer> lastEntry =
        entries.get(entries.size() - 1);
    System.out.println("Last key: " + lastEntry.getKey() + ", last value: "
        + lastEntry.getValue());

}

输出:

最后一个键:未指定,最后一个值:1


为什么实现Comparable接口然后使用TreeMap不是你的首选?你似乎比我更了解Maps,所以我想知道。 - nckbrz
@nixxbb 这 确实是 我的第一选择,这就是为什么它直接出现在字面答案之后。 - Sean Patrick Floyd
哦,我明白了,你在谈论Comparable和Comparator。在OP的情况下,键是字符串,但他需要自定义顺序。这就是为什么。否则我会同意你的观点。 - Sean Patrick Floyd
哦,我明白了。做得非常好。谢谢你的回复,我从你的答案中学到了很多... - nckbrz

19

HashMap没有"最后一个位置",因为它没有排序。

你可以使用其他实现了java.util.SortedMap接口的Map,其中最流行的是TreeMap


1
请参阅https://dev59.com/cHI-5IYBdhLWcg3wR2Jr - rogerdpack

5

SortedMap 是逻辑/最佳选择,不过另一个选择是使用 LinkedHashMap,它维护两种排序模式,最近添加的在最后面,最近访问的在最后面。详见 Javadocs。


2
当使用数字作为键时,我认为您也可以尝试这样做:
        Map<Long, String> map = new HashMap<>();
        map.put(4L, "The First");
        map.put(6L, "The Second");
        map.put(11L, "The Last");

        long lastKey = 0;
        //you entered Map<Long, String> entry
        for (Map.Entry<Long, String> entry : map.entrySet()) {
            lastKey = entry.getKey();
        }
        System.out.println(lastKey); // 11

1

对于哈希表来说,移动操作没有意义,因为它是一个字典,具有基于键的哈希码进行分桶的特性,然后是通过equals解决冲突哈希码的链表。 对于排序映射,请使用TreeMap,然后传入自定义比较器。


在创建TreeMap之后,您无法“传入自定义比较器”。您只能将其用作构造函数参数。 - Sean Patrick Floyd

0
在这种情况下,通常已知最后使用的键,因此可以使用它来访问最后一个值(与该键插入的值)。
class PostIndexData {
    String _office_name;
    Boolean _isGov;
    public PostIndexData(String name, Boolean gov) {
        _office_name = name;
        _isGov = gov;
    }
}
//-----------------------
class KgpData {
    String _postIndex;
    PostIndexData _postIndexData;
    public KgpData(String postIndex, PostIndexData postIndexData) {
        _postIndex = postIndex;
        _postIndexData = postIndexData;;
    }
}

public class Office2ASMPro {
    private HashMap<String,PostIndexData> _postIndexMap = new HashMap<>();
    private HashMap<String,KgpData> _kgpMap = new HashMap<>();
...
private void addOffice(String kgp, String postIndex, String officeName, Boolean gov) {
            if (_postIndexMap.get(postIndex) == null) {
                _postIndexMap.put(postIndex, new PostIndexData(officeName, gov));
            }
            _kgpMap.put( kgp, new KgpData(postIndex, _postIndexMap.get(postIndex)) );
        }

嗨,Mad Calm,记得要尽量保持你的示例简洁明了,这样任何阅读问题/答案的人都能轻松理解。只是粘贴代码而没有太多解释通常不是一个好主意 :-) 谢谢你的贡献。 - Panthro

0
HashMap明确不定义键的顺序。根据类Javadocs
“该类不保证映射的顺序;特别是,它不保证顺序会随时间保持恒定。”
其他一些映射类型定义了键的顺序,包括LinkedHashMap和实现了SortedMap接口的映射。Java 21引入了一个SequencedMap接口,用于具有明确定义的遇到顺序的映射。
根据其Javadocs,一个SequencedMap是:

一个具有明确定义的遍历顺序、支持两端操作且可逆转的Map

SequencedMap的操作之一是putLast。这个可选操作用于支持它的SequencedMap类型,在映射的末尾插入一个条目,如果键已经存在,则将其移动到末尾。LinkedHashMap支持此方法,但SortedMap不支持。根据方法的Javadocs
如果给定的映射在地图中不存在,则将其插入地图中;如果已经存在,则替换映射的值(可选操作)。完成此操作后,给定的映射将存在于此地图中,并且它将是此地图遇到顺序中的最后一个映射。

相关场景

将上述内容应用于您所提出的场景,我们得到:

LinkedHashMap<String,Integer> map = new LinkedHashMap<String,Integer>();

populateMap(map); // Populate the map as: {Not-Specified 1, test 2, testtest 3};

map.putLast("Not-Specified", map.get("Not-Specified"));

-2
Find missing all elements from array
        int[] array = {3,5,7,8,2,1,32,5,7,9,30,5};
        TreeMap<Integer, Integer> map = new TreeMap<>();
        for(int i=0;i<array.length;i++) {
            map.put(array[i], 1);
        }
        int maxSize = map.lastKey();
        for(int j=0;j<maxSize;j++) {
            if(null == map.get(j))
                System.out.println("Missing `enter code here`No:"+j);
        }

查找数组中缺失的所有元素 - Muruganandam C
这似乎与所提出的问题没有太多关系。 - DavidW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接