Map映射

Map映射

将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。

Collection只管存储元素
Map既要存储元素value 也要管理该元素对应的索引key(不可变类)

Map这种键值(key-value)映射表的数据结构,作用就是能高效通过key快速查找value(元素)

Map<K, V>是一种键-值映射表,当我们调用put(K key, V value)方法时,就把keyvalue做了映射并放入Map。当我们调用V get(K key)时,就可以通过key获取到对应的value。如果key不存在,则返回null。和List类似,Map也是一个接口,最常用的实现类是HashMap

如果只是想查询某个key是否存在,可以调用boolean containsKey(K key)方法。

复放入key-value并不会有任何问题,但是一个key只能关联一个value

put()方法的签名是V put(K key, V value),如果放入的key已经存在,put()方法会返回被删除的旧的value,否则,返回null

Map中不存在重复的key,因为放入相同的key,只会把原有的key-value对应的value给替换掉。

功能方法

voidclear 从此映射中移除所有映射关系(可选操作)。
booleancontainsKey (Object key) 如果此映射包含指定键的映射关系,则返回 true
booleancontainsValue (Object value) 如果此映射将一个或多个键映射到指定值,则返回 true
Set<Map.Entry<K,V>>entrySet 返回此映射中包含的映射关系的 Set视图。
booleanequals(Object o) 比较指定的对象与此映射是否相等。
Vget(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null
inthashCode() 返回此映射的哈希码值。
booleanisEmpty() 如果此映射未包含键-值映射关系,则返回 true
Set<K>keySet() 返回此映射中包含的键的 Set 视图。
Vput(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)。
voidputAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。
Vremove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
intsize() 返回此映射中的键-值映射关系数。
Collection<V>values() 返回此映射中包含的值的 Collection 视图。

遍历Map

Map来说,要遍历key可以使用for each循环遍历Map实例的keySet()方法返回的Set集合,它包含不重复的key的集合:

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 123);
        map.put("pear", 456);
        map.put("banana", 789);
        for (String key : map.keySet()) {
            Integer value = map.get(key);
            System.out.println(key + " = " + value);
        }
    }
}

同时遍历keyvalue可以使用for each循环遍历Map对象的entrySet()集合,它包含每一个key-value映射:

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 123);
        map.put("pear", 456);
        map.put("banana", 789);
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + " = " + value);
        }
    }
}

MapList不同的是,Map存储的是key-value的映射关系,并且,它不保证顺序。在遍历的时候,遍历的顺序既不一定是put()时放入的key的顺序,也不一定是key的排序顺序。

TreeMap

基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。

红黑树 禁止null key禁止null 自然排序有序映射 不安全

常用方法

增添元素
  • V put(K key, V value):将指定映射放入该TreeMap中
  • V putAll(Map map):将指定map放入该TreeMap中
删除元素
  • void clear():清空TreeMap中的所有元素
  • V remove(Object key):从TreeMap中移除指定key对应的映射
修改元素
  • V replace(K key, V value):替换指定key对应的value值
  • boolean replace(K key, V oldValue, V newValue):当指定key的对应的value为指定值时,替换该值为新值
查找元素
  • boolean containsKey(Object key):判断该TreeMap中是否包含指定key的映射
  • boolean containsValue(Object value):判断该TreeMap中是否包含有关指定value的映射
  • Map.Entry<K, V> firstEntry():返回该TreeMap的第一个(最小的)映射
  • K firstKey():返回该TreeMap的第一个(最小的)映射的key
  • Map.Entry<K, V> lastEntry():返回该TreeMap的最后一个(最大的)映射
  • K lastKey():返回该TreeMap的最后一个(最大的)映射的key
  • v get(K key):返回指定key对应的value
  • SortedMap<K, V> headMap(K toKey):返回该TreeMap中严格小于指定key的映射集合
  • SortedMap<K, V> subMap(K fromKey, K toKey):返回该TreeMap中指定范围的映射集合(大于等于fromKey,小于toKey)
遍历接口
  • Set<Map<K, V>> entrySet():返回由该TreeMap中的所有映射组成的Set对象
  • void forEach(BiConsumer<? super K,? super V> action):对该TreeMap中的每一个映射执行指定操作
  • Collection<V> values():返回由该TreeMap中所有的values构成的集合
其他方法
  • Object clone():返回TreeMap实例的浅拷贝
  • Comparator<? super K> comparator():返回给该TreeMap的keys排序的comparator,若为自然排序则返回null
  • int size():返回该TreepMap中包含的映射的数量

使用TreeMap时,放入的Key必须实现Comparable接口。StringInteger这些类已经实现了Comparable接口,因此可以直接作为Key使用。作为Value的对象则没有任何要求。

如果作为Key的class没有实现Comparable接口,那么,必须在创建TreeMap时同时指定一个自定义排序算法:

public class Main {
    public static void main(String[] args) {
        Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {
            public int compare(Person o1, Person o2) {
                return o1.name.compareTo(o2.name);
            }
        });
        map.put(new Person("Tom"), 1);
        map.put(new Person("Bob"), 2);
        map.put(new Person("Lily"), 3);
        for (Person key : map.keySet()) {
            System.out.println(key);
        }
        // {Person: Bob}, {Person: Lily}, {Person: Tom}
        System.out.println(map.get(new Person("Bob"))); // 2
    }
}

class Person {
    public String name;
    Person(String name) {
        this.name = name;
    }
    public String toString() {
        return "{Person: " + name + "}";
    }
}

Comparator接口要求实现一个比较方法,它负责比较传入的两个元素ab,如果a<b,则返回负数,通常是-1,如果a==b,则返回0,如果a>b,则返回正数,通常是1TreeMap内部根据比较结果对Key进行排序。

在遍历时严格按照Key的顺序遍历,最常用的实现类是TreeMap

作为SortedMap的Key必须实现Comparable接口,或者传入Comparator

要严格按照compare()规范实现比较逻辑,否则,TreeMap将不能正常工作。

HashMap

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

底层 数组+链表/红黑树
允许null 键禁止null 无序 不安全
如果两个元素计算出来的哈希值和要去的哈希角标一致 称之为哈希冲突
HashMap中 解决哈希冲突的办法 链地址法 把哈希冲突的元素放到同一个桶里面
这个桶要么是个链表 要么是个红黑树
函数功能来自Map
LinkedHashMap 多了一个链表来管理元素的进入顺序 其他和HashMap一样的

查询效率高,插入,删除效率低。

方法摘要

voidclear]() 从此映射中移除所有映射关系。
Objectclone() 返回此 HashMap 实例的浅表副本:并不复制键和值本身。
booleancontainsKey(Object key) 如果此映射包含对于指定键的映射关系,则返回 true
booleancontainsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true
Set<Map.Entry<K,V>>entrySet() 返回此映射所包含的映射关系的 Set视图。
Vget(Object key) 返回指定键所映射的值;如果对于该键来说,此映射不包含任何映射关系,则返回 null
booleanisEmpty() 如果此映射不包含键-值映射关系,则返回 true
Set<K>keySet() 返回此映射中所包含的键的 Set视图。
Vput(K key, V value) 在此映射中关联指定值与指定键。
voidputAll(Map<? extends K,? extends V> m) 将指定映射的所有映射关系复制到此映射中,这些映射关系将替换此映射目前针对指定映射中所有键的所有映射关系。
Vremove(Object key) 从此映射中移除指定键的映射关系(如果存在)。
intsize() 返回此映射中的键-值映射关系数。
Collection<V>values() 返回此映射所包含的值的 Collection视图。

给一组数字,里面会有一些重复的元素,求取前k个出现频次最多的元素?

public class Main {
    public static void main(String[] args) {
        //给一组数字,里面会有一些重复的元素,求取前k个出现频次最多的元素?
        int[] arr = {1, 4, 1, 4, 2, 4, 1, 4, 2, 4, 3, 4, 4, 1, 4, 2, 4, 3, 4, 4, 2, 4, 1, 4, 2, 4, 3, 4, 3, 4, 4, 1, 2, 4, 1, 2, 4, 3, 1, 1, 1, 2, 3, 4};

        //key-Integer-元素
        //value-Integer-次数
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int num : arr) {
            if (map.containsKey(num)) {
                map.put(num, map.get(num) + 1);
            } else {
                map.put(num, 1);
            }
        }
        System.out.println(map);
        PriorityQueue<Freq> queue = new PriorityQueue<Freq>(new Comparator<Freq>() {
            @Override
            public int compare(Freq o1, Freq o2) {
                return o2.freq - o1.freq;
            }
        });
        for (Integer num : map.keySet()) {
            queue.offer(new Freq(num, map.get(num)));
        }
        System.out.println(queue.poll());
        System.out.println(queue.poll());
    }
}

class Freq {
    int num;    //数字
    int freq;   //次数

    public Freq(int num, int freq) {
        this.num = num;
        this.freq = freq;
    }

    @Override
    public String toString() {
        return "Freq{" +
                "num=" + num +
                ", freq=" + freq +
                '}';
    }
}

Hashtable

此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。

为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法

哈希表(HashTable)又叫做散列表,是根据关键码值(即键值对)而直接访问的数据结构。也就是说,它通过把关键码映射到表中一个位置来访问记录,以加快查找速度。看到这里你可能比较疑惑,它是怎么加快查找速度的?下一节就有说明!这个映射函数就叫做散(哈希)函数,存放记录的数组叫做散列表。

底层由数组实现
禁止null 键禁止null 无序 不安全
如果两个元素计算出来的哈希值和要去的哈希角标一致 称之为哈希冲突
Hashtable中 解决哈希冲突的办法 开放地址法+二次哈希 将冲突的元素后置存放
将哈希冲突的元素进行后置存放,为了关联哈希值一样的元素,Entry结点中有一个next来进行关联元素
函数功能来自Map

HashTable与HashMap对比

(1)线程安全:HashMap是线程不安全的类,多线程下会造成并发冲突,但单线程下运行效率较高;HashTable是线程安全的类,很多方法都是用synchronized修饰,但同时因为加锁导致并发效率低下,单线程环境效率也十分低;

(2)插入null:HashMap允许有一个键为null,允许多个值为null;但HashTable不允许键或值为null;

(3)容量:HashMap底层数组长度必须为2的幂,这样做是为了hash准备,默认为16;而HashTable底层数组长度可以为任意值,这就造成了hash算法散射不均匀,容易造成hash冲突,默认为11;

(4)Hash映射:HashMap的hash算法通过非常规设计,将底层table长度设计为2的幂,使用位与运算代替取模运算,减少运算消耗;而HashTable的hash算法首先使得hash值小于整型数最大值,再通过取模进行散射运算;

Properties

Properties内部本质上是一个Hashtable,但我们只需要用到Properties自身关于读写配置的接口。

Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。

ConcurrentHashMap

concurrentHashMap是一个支持高并发更新与查询的哈希表(基于HashMap)。
在保证安全的前提下,进行检索不需要锁定。与hashtable不同,该类不依赖于synchronization去保证线程操作的安全。

和HashMap一致
只有ConcurrentHashMap它是同步的 线程安全的

总结

集合映射null值重复值有序线程安全底层实现
ArrayList允许允许有序不安全动态数组
LinkedList允许允许有序不安全双向链表
Vector允许允许有序安全动态数组
Stack允许允许有序安全动态数组
ArrayDeque禁止允许有序不安全双端队列(数组)
PriorityQueue禁止允许有序(自然排序)安全最小堆(数组)
HashSet允许禁止无序不安全数组+ 哈希冲突:链地址法 链表/红黑树
LinkedHashSet允许禁止有序不安全数组+ 哈希冲突:链地址法 链表/红黑树,额外的链表
TreeSet禁止禁止有序(自然排序)不安全红黑树(二分搜索树)
Hashtable禁止键禁止无序安全数组 + 哈希冲突:开放地址法 链表
HashMap允许键禁止无序不安全数组+ 哈希冲突:链地址法 链表/红黑树
LinkedHashMap允许键禁止有序不安全数组+哈希冲突:链地址法 链表/红黑树,额外的链表
TreeMap禁止键禁止有序(自然排序)不安全红黑树
ConcurrentHashMap允许键禁止无序安全数组+ 哈希冲突:链地址法 链表/红黑树
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_59138290

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值