逆序删除Map中的元素

场景

使用LinkedHashMap时逆序删除元素

代码

Map<String, String> map = new LinkedHashMap<>();
map.put("1_1", "1");
map.put("1_2", "1");
map.put("1_3", "1");
map.put("2_1", "2");
map.put("2_2", "2");
map.put("2_3", "2");

List<Map.Entry<String, String>> list = new ArrayList<>(map.entrySet());
ListIterator<Map.Entry<String, String>> iterator = list.listIterator(list.size());
while (iterator.hasPrevious()) {
    Map.Entry<String, String> entry = iterator.previous();
    String key = entry.getKey();
    if (key != null && key.startsWith("2")) {
        iterator.remove();
    }
}
System.out.println(list);
System.out.println(map);

输出

[1_1=1, 1_2=1, 1_3=1]
{1_1=1, 1_2=1, 1_3=1, 2_1=2, 2_2=2, 2_3=2}

分析

为了逆序地删除map中的元素,首先用了LinkedHashMap来存元素,通过底层的双向链表结构来保证有序性;其次,为了使用List的双向迭代器listIterator,把Map.Entry的Set集合添加到ArrayList中,最后通过listIterator由后向前删除key为2开头的元素,最后打印结果发现只有list中的元素被删了,map中的元素没有变

问题出在哪里?

猜想一:深拷贝

首先会想到会不会是深拷贝的问题,也就是说如果listIterator中的Map.Entry结构中的元素地址与原来的不一样,那么这个现象就可以解释了

public ArrayList(Collection<? extends E> c) {
    Object[] a = c.toArray();
    if ((size = a.length) != 0) {
        if (c.getClass() == ArrayList.class) {
            elementData = a;
        } else {
            elementData = Arrays.copyOf(a, size, Object[].class);
        }
    } else {
        // replace with empty array.
        elementData = EMPTY_ELEMENTDATA;
    }
}

阅读源码发现new ArrayList(Collection<? extends E> c)底层使用System.arraycopy来做数组拷贝,属于浅拷贝

猜想二:迭代器

那么继续阅读迭代器的源码,首先看LinkedHashMap自己的迭代器

final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
    public final int size()                 { return size; }
    public final void clear()               { LinkedHashMap.this.clear(); }
    public final Iterator<Map.Entry<K,V>> iterator() {
        return new LinkedEntryIterator();
    }
    ...
    }
    public final boolean remove(Object o) {
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            Object key = e.getKey();
            Object value = e.getValue();
            return removeNode(hash(key), key, value, true, true) != null;
        }
        return false;
    }
    ...
}

发现remove方法最终会回调继承自HashMap的removeNode方法,这个方法才是真正的去map的底层数据结构中删除元素

同样看下ArrayList的双向迭代器listIterator源码

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;
    Itr() {}
    public boolean hasNext() {
        return cursor != size;
    }
    ...
    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();
        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
    ...
}

它同样会回调ArrayList当前对象的remove方法

结论

至此,疑团解开,开头的代码中试图使用list中的迭代器删除map底层数据结构中的元素,当然是行不通的。究其原因是对entryset这个东西的理解不深,它更像是当前map结构的视图,并不直接持有底层数据结构的引用,最终使用迭代器来删除仍然是回调remove底层数据结构的方法

最后,代码的修改方法也很简单,使用Map中的V remove(Object key)接口来删就可以了

参考

How to Iterate LinkedHashMap in Reverse Order in Java? - GeeksforGeeks

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值