基于Keys列表获取子HashMap的最佳方法是什么?

时间:2015-03-04 14:19:11

标签: java hashmap

我有一个HashMap,我希望得到一个新的HashMap,它只包含第一个HashMap中的元素,其中K属于一个特定的List。

我可以查看所有键并填写一个新的HashMap,但我想知道是否有更有效的方法来做它?

感谢

12 个答案:

答案 0 :(得分:16)

使用Java8流,有一个功能(优雅)的解决方案。如果keys是要保留的密钥列表,map是来源Map

keys.stream()
    .filter(map::containsKey)
    .collect(Collectors.toMap(Function.identity(), map::get));

完整示例:

    List<Integer> keys = new ArrayList<>();
    keys.add(2);
    keys.add(3);
    keys.add(42); // this key is not in the map

    Map<Integer, String> map = new HashMap<>();
    map.put(1, "foo");
    map.put(2, "bar");
    map.put(3, "fizz");
    map.put(4, "buz");

    Map<Integer, String> res = keys.stream()
        .filter(map::containsKey)
        .collect(Collectors.toMap(Function.identity(), map::get));

    System.out.println(res.toString());

打印:{2=bar, 3=fizz}

编辑为地图中缺少的密钥添加filter

答案 1 :(得分:4)

根据您的使用情况,这可能是一种更有效的实施方式

public class MapView {
  List ak;
  Map map;
  public MapView(Map map, List allowableKeys) {
     ak = allowableKeys;
     map = map;
  }
  public Object get(Object key) {
    if (!ak.contains(key)) return null;
    return map.get(key);
  }
}

答案 2 :(得分:4)

是的,有一个解决方案:

Map<K,V> myMap = ...;
List<K> keysToRetain = ...;
myMap.keySet().retainAll(keysToRetain);

retainAll上的Set操作会更新基础地图。请参阅java doc

修改 请注意,此解决方案会修改Map

答案 3 :(得分:3)

如果您有Map m1和List键,请尝试按照

Map m2 = new HashMap(m1);
m2.keySet().retainAll(keys);

答案 4 :(得分:3)

在番石榴的帮助下。

假设您有一张地图Map<String, String>,并希望使用List<String>列表中的值进行子地图。

Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "4");

final List<String> list = Arrays.asList("2", "4");

Map<String, String> subMap = Maps.filterValues(
                map, Predicates.in(list));

更新/注意:正如评论中提到的@assylias,使用contains()时将有O(n)。因此,如果您有大量列表,这可能会对性能产生巨大影响。

另一方面HashSet.contains()是常数时间O(1),所以如果有可能有Set而不是List,这可能是一个很好的方法(注意转换List to Set将花费O(n),所以最好不要转换:))

答案 5 :(得分:1)

如果您的密钥有排序,您可以使用TreeMap。

查看TreeMap.subMap()

但是,不允许使用列表执行此操作。

答案 6 :(得分:1)

您可以遍历列表并检查HashMap是否包含映射,而不是查看所有键。然后使用筛选的条目创建一个新的HashMap:

List<String> keys = Arrays.asList('a', 'c', 'e');

Map<String, String> old = new HashMap<>();
old.put('a', 'aa');
old.put('b', 'bb');
old.put('c', 'cc');
old.put('d', 'dd');
old.put('e', 'ee');

// only use an inital capacity of keys.size() if you won't add
// additional entries to the map; anyways it's more of a micro optimization
Map<String, String> newMap = new HashMap<>(keys.size(), 1f);

for (String key: keys) {
    String value = old.get(key);
    if (value != null) newMap.put(key, value);
}

答案 7 :(得分:0)

使用标准方法复制地图并删除列表中没有的所有键:

Map map2 = new Hashmap(map);
map2.keySet().retainAll(keysToKeep);

答案 8 :(得分:0)

你可以在返回的K HashMap上使用clone()方法。

类似的东西:

import java.util.HashMap;

public class MyClone {    
     public static void main(String a[]) {    
        Map<String, HashMap<String, String>> hashMap = new HashMap<String, HashMap<String, String>>();    
        Map hashMapCloned = new HashMap<String, String>();    

        Map<String, String> insert = new HashMap<String, String>();

        insert.put("foo", "bar");
        hashMap.put("first", insert);

        hashMapCloned.put((HashMap<String, String>) hashMap.get("first").clone());

    }    
}

它可能有一些语法错误,因为我没有测试,但尝试类似的东西。

答案 9 :(得分:0)

不,因为HashMap没有维护其条目的顺序。如果需要某个范围之间的子映射,可以使用TreeMap。另外,请看这question;它似乎与你的类似。

答案 10 :(得分:0)

您要求新的HashMap。由于HashMap不支持结构共享,因此没有比明显方法更好的方法。 (我假设null不能是值。)

Map<K, V> newMap = new HashMap<>();
for (K k : keys) {
    V v  = map.get(k);
    if (v != null)
        newMap.put(k, v);
}

如果您不是绝对要求创建的新对象是HashMap,则可以创建一个新类(理想情况下扩展为AbstractMap<K, V>),表示原始Map的受限视图。该课程将有两个私人最终字段

Map<? extends K, ? extends V> originalMap;
Set<?> restrictedSetOfKeys;

get的{​​{1}}方法将是这样的

Map

请注意,@Override public V get(Object k) { if (!restrictedSetOfKeys.contains(k)) return null; return originalMap.get(k); } restrictedSetOfKeys而不是Set会更好,因为如果它是List,您通常会有O(1)时间复杂度对于HashSet方法。

答案 11 :(得分:0)

你甚至可以自己成长:

public class FilteredMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {

    // The map I wrap.
    private final Map<K, V> map;
    // The filter.
    private final Set<K> filter;

    public FilteredMap(Map<K, V> map, Set<K> filter) {
        this.map = map;
        this.filter = filter;
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        // Make a new one to break the bond with the underlying map.
        Set<Entry<K, V>> entries = new HashSet<>(map.entrySet());
        Set<Entry<K, V>> remove = new HashSet<>();
        for (Entry<K, V> entry : entries) {
            if (!filter.contains(entry.getKey())) {
                remove.add(entry);
            }
        }
        entries.removeAll(remove);
        return entries;
    }

}

public void test() {
    Map<String, String> map = new HashMap<>();
    map.put("1", "One");
    map.put("2", "Two");
    map.put("3", "Three");
    Set<String> filter = new HashSet<>();
    filter.add("1");
    filter.add("2");
    Map<String, String> filtered = new FilteredMap<>(map, filter);
    System.out.println(filtered);

}

如果您对所有复制事项感到担忧,您也可以增加过滤后的Set和过滤后的Iterator

public interface Filter<T> {

    public boolean accept(T t);
}

public class FilteredIterator<T> implements Iterator<T> {

    // The Iterator
    private final Iterator<T> i;
    // The filter.
    private final Filter<T> filter;
    // The next.
    private T next = null;

    public FilteredIterator(Iterator<T> i, Filter<T> filter) {
        this.i = i;
        this.filter = filter;
    }

    @Override
    public boolean hasNext() {
        while (next == null && i.hasNext()) {
            T n = i.next();
            if (filter.accept(n)) {
                next = n;
            }
        }
        return next != null;
    }

    @Override
    public T next() {
        T n = next;
        next = null;
        return n;
    }
}

public class FilteredSet<K> extends AbstractSet<K> implements Set<K> {

    // The Set
    private final Set<K> set;
    // The filter.
    private final Filter<K> filter;

    public FilteredSet(Set<K> set, Filter<K> filter) {
        this.set = set;
        this.filter = filter;
    }

    @Override
    public Iterator<K> iterator() {
        return new FilteredIterator(set.iterator(), filter);
    }

    @Override
    public int size() {
        int n = 0;
        Iterator<K> i = iterator();
        while (i.hasNext()) {
            i.next();
            n += 1;
        }
        return n;
    }

}

public class FilteredMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {

    // The map I wrap.
    private final Map<K, V> map;
    // The filter.
    private final Filter<K> filter;

    public FilteredMap(Map<K, V> map, Filter<K> filter) {
        this.map = map;
        this.filter = filter;
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        return new FilteredSet<>(map.entrySet(), new Filter<Entry<K, V>>() {

            @Override
            public boolean accept(Entry<K, V> t) {
                return filter.accept(t.getKey());
            }

        });
    }

}

public void test() {
    Map<String, String> map = new HashMap<>();
    map.put("1", "One");
    map.put("2", "Two");
    map.put("3", "Three");
    Set<String> filter = new HashSet<>();
    filter.add("1");
    filter.add("2");
    Map<String, String> filtered = new FilteredMap<>(map, new Filter<String>() {

        @Override
        public boolean accept(String t) {
            return filter.contains(t);
        }
    });
    System.out.println(filtered);

}