05_Map

Map

Map的特点

  • Map是Map体系的顶级接口,用来存储键值对数据
  • Map存储的数据,有一些子接口有序,有一些无序
    • 无序HashMapHashtable
    • 有序LinkedHashMapTreeMap
  • Map存储的数据,不能重复(指的是key)
  • Map存储的数据,有一些允许为null,有一些不允许。(指的key)
    • 允许存储nullHashMapLinkedHashMap
    • 不允许存储nullHashtableTreeMap

Map的API

增删改查的接口

V put(K key, V value);  // 添加键值对

void putAll(Map<? extends K,? extends V> m); // 将一个map的所有键值对都放入这个map

V get(Object key)// 根据一个key,获取value,如果key不存在,返回null

V remove(Object key)// 删除map中所包含的这个key

boolean containsKey(Object key): // 判断map中是否包含这个key

boolean containsValue(Object value): // 判断map中是否包含这个value

辅助接口

void clear() : // 清空map

boolean equals(Object o)// 判断两个map是否相等

int hashCode(): // 返回此映射的哈希码值。

boolean isEmpty(): // map中是否有元素

int size()// 返回键-值映射关系数。

遍历方法

Set<Map.Entry<K,V>> entrySet()// 返回此映射中包含的映射关系的 Set 视图。
// 拿到的是entry,entry是存储键值对的东西

Set<K> keySet()// 返回此映射中包含的键的 Set 视图。就是获取所有的key

Collection<V> values()// 返回此映射中包含的值的 Collection 视图。就是获取所有的value

若想访问键和值,可采用下面两种方法:

  • Map中使用比较多的一类方法
    • 直接获取一个EntrySet对象,然后遍历这个对象
    • 可以从entry获取key、value
  • Map遍历的第二种方法
      1. 获取keySet
      1. 遍历这个keySet
      • 通过get()·得到value

eg:


Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
    String key = entry.getKey();
    String value = entry.getValue();
    System.out.println(key + "---" + value);
}

--------------------------------------------------------

Set<String> set = map.keySet();
for (String key : set) {
    String value = map.get(key);
    System.out.println(key + "---" + value);
}

--------------------------------------------------------

Collection<String> values = map.values();
for (String value : values) {
    System.out.println(value);
}

HashMap

HashMap的底层结构

数组+链表+红黑树

想往HashMap中添加一个键值对的流程:

  • 首先,会对键,计算得到一个key的int类型的值(其实就是hash的过程)
  • 对数组长度取余,得到下标的位置(即在数组上的位置)
  • 如果数组位置上没有元素,则直接插入(key-value
  • 如果数组位置上有元素,挨个比较,如果不相同,就采用拉链(链表)的方法,存进去
    • 如果链表数目过长,则转换成红黑树

HashMap的特点

  1. HashMap是Map接口的子实现。用来存储key-value数据
  2. 底层结构,是数组+链表+红黑树
  3. 数组默认长度16,扩容机制是2倍。
  4. 存储元素是无序的。对于key来说的
  5. 不允许存储重复元素, 重复是指的它的键
  6. 允许存储null值作为key。
  7. 允许存储null值作为value。

HashMap的一些常见问题

  1. HashMap的初始容量是16,扩容机制是2倍。决定了数组的长度是2的幂次,方便了取余操作
  2. HashMap底层会维护一个加载因子,用来表示存储到多少就会扩容
    • 比如:默认的数组长度是16。 加载因子是0.75f
    • 数组中存储的key-value数据数目超过阈值, 就要引发数组扩容
  3. 链表转换为红黑树的时机:当某个下标位置, 链表长度, 超过8达到9个时候(算上新加的结点), 就要由链表转化为红黑树。
  4. 红黑树什么时候会转换为链表:
    • 第一个情况:删除数据的时候。 要删除的数据在红黑树上, 删除数据导致红黑树上数据量变少, 由红黑树转化为链表
    • 第二个情况:扩容的时候。一个红黑树再扩容之后, 被拆成两部分, 任一部分数据量过少, 也会由红黑树转化为链表
  5. 扩容后的位置:如果当前落在x位置,扩容后会落在x+length位置

HashMap的添加流程

  1. 现在需要添加一个键值对
    • eg:zs, 18
  2. 首先计算一个int值
    • (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
  3. 对这个int值取余,得到这个key在数组上面的下标
    • a%b=a&(b-1)
  4. 判断数组位置上是否有元素
    • 如果数组上没有元素,则直接新建节点,然后插入到数组。
      • Node里面包括:key,value,hash,next(Node)
    • 如果数组位置有元素,比较是否相等
      • 怎么判断相等:p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))
        • 如果相等,则直接使用新值替换旧值,返回旧值。
        • 如果不相等,则挨个进行比较。
  5. 如果判断不存在,就挂上去。如果链表长度太长(超过8到达9)会转化为红黑树
  6. 添加了元素,也可能到达扩容点(数组长度 * 0.75)
    • 扩容后的位置:x —> x + length
  7. 扩容后,如果这个位置是红黑是,有可能转化为链表

HashMap的构造方法

HashMap() 
         // 构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。 

HashMap(int initialCapacity) 
         // 构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。 
         // 这里的指定的容量是指会找一个2的幂次方大小
         // 例如:传10回去找16;传16也会去找16

HashMap(int initialCapacity, float loadFactor) 
         // 构造一个带指定初始容量和加载因子的空 HashMap。 

HashMap(Map<? extends K,? extends V> m) 
         // 构造一个映射关系与指定 Map 相同的新 HashMap。
         // 如果传入一个Map,会把传入的Map复制一份,传给新的Map

HashMap的API

//---------------------------新增,删除,查找数据接口
V put(K key, V value): 
					// 添加键值对

void putAll(Map<? extends K,? extends V> m)// 将一个map的所有键值对都放入这个map

V get(Object key)// 根据一个key,获取value,如果key不存在,返回null

V remove(Object key)// 删除map中所包含的这个key

boolean containsKey(Object key): 
					// 判断map中是否包含这个key

boolean containsValue(Object value): 
					// 判断map中是否包含这个value

//---------------------------辅助接口
void clear() : 
					// 清空map

boolean equals(Object o)// 判断两个map是否相等

int hashCode(): 
					//返回此映射的哈希码值。

boolean isEmpty(): 
					// map中是否有元素

int size()// 返回键-值映射关系数。

//---------------------------视图方法
Set<Map.Entry<K,V>> entrySet()// 返回此映射中包含的映射关系的 Set 视图。

Set<K> keySet()// 返回此映射中包含的键的 Set 视图。

Collection<V> values()// 返回此映射中包含的值的 Collection 视图。

最终业务代码的关键

  1. 你需要遍历,使用合适的容器来存储
    • List<>Map<>

HashTable

  1. HashTable是Map接口的一个子实现
  2. HashTable底层结构是数组+链表 (和HashMap在jdk1.8之前是一样的)
  3. 底层数组默认的初始长度11 ; 默认的扩容机制为2倍+1
  4. 存储元素无序
  5. 不允许存储重复的key;(对key的重复的定义和HashMap一样)
  6. 不允许存储null作为key, 也不允许存储null作为value
  7. 线程安全
  8. jdk1.0时候出现, (HashMap是jdk1.2时候出现, HashMap的出现就是为了取代Hashtable的)

面试题:说一下HashMap和HashTable的异同?

首先先说一下共同点:
9. 两个都是Map的子实现。都用来存储key-value数据
10. 在1.8之前,两者底层结构都是数组+链表。但在1.8之后,HashMap变成了数组+链表+红黑树
11. 存储元素无序,都不能存储重复元素。HashMap允许存储null。Hashtable不允许存储null
12. HashMap线程不安全,Hashtable线程安全。
13. HashMap在1.2出现,就是为了替代Hashtable的。新写代码不使用Hashtable


LinkedHashMap

LinkedHashMap的特点

  1. LinkedHashMap是HashMap的一个子类
  2. LinkedHashMap底层基本上完全复用了父类HashMap的结构/参数/方法
  3. LinkedHashMap在HashMap的基础上, 额外的维护了一个双向链表, 以保证迭代顺序
  4. LinkedHashMap存储元素有序
  5. LinkedHashMap不允许存储重复数据
  6. LinkedHashMap允许存储null,与HashMap一致
  7. LinkedHashMap线程不安全

LinkedHashMap的构造方法

LinkedHashMap() 
          // 构造一个带默认初始容量 (16) 和加载因子 (0.75) 的空插入顺序 LinkedHashMap 实例。 

LinkedHashMap(int initialCapacity) 
         // 构造一个带指定初始容量和默认加载因子 (0.75) 的空插入顺序 LinkedHashMap 实例。 

LinkedHashMap(int initialCapacity, float loadFactor) 
         // 构造一个带指定初始容量和加载因子的空插入顺序 LinkedHashMap 实例。 

LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) 
         // 构造一个带指定初始容量、加载因子和排序模式的空 LinkedHashMap 实例。 
         // 排序模式:LRU(最近最少使用算法)

LinkedHashMap(Map<? extends K,? extends V> m) 
         // 构造一个映射关系与指定映射相同的插入顺序 LinkedHashMap 实例。

LinkedHashMap的API

与HashMap的API相同


TreeMap

TreeMap的特点

  1. TreeMap是Map接口的子实现
  2. TreeMap的数据结构红黑树
  3. TreeMap存储数据大小有序
  4. TreeMap不允许存储重复的key (什么叫重复: key的大小一样)
  5. TreeMap不允许存储null作为key,但允许存储null作为value

TreeMap的构造方法

TreeMap() 
         // 使用键的自然顺序构造一个新的、空的树映射。 

TreeMap(Comparator<? super K> comparator) 
         // 构造一个新的、空的树映射,该映射根据给定比较器进行排序。 
         /*
         其中如果使用自己的类,来当作TreeMap的key
         有两个可行的路径:
         1. 实现Comparable的接口
         2. 传入Comparator接口的实现
         */

TreeMap(Map<? extends K,? extends V> m) 
         // 构造一个与给定映射具有相同映射关系的新的树映射,该映射根据其键的自然顺序 进行排序。 

TreeMap(SortedMap<K,? extends V> m) 
         // 构造一个与指定有序映射具有相同映射关系和相同排序顺序的新的树映射。 
         // 不仅拷贝数据,还拷贝排序规则
        

TreeMap的API

----------------------Map接口继承来的--------------------
boolean containsKey(Object key)
//        如果此映射包含指定键的映射关系,则返回 true。

boolean containsValue(Object value)
//        如果此映射为指定值映射一个或多个键,则返回 true。

V put(K key, V value)
//        将指定值与此映射中的指定键进行关联。

void putAll(Map<? extends K,? extends V> map)
//        将指定映射中的所有映射关系复制到此映射中。

V remove(Object key)
//        如果此 TreeMap 中存在该键的映射关系,则将其删除。

V get(Object key)
//        返回指定键所映射的值,如果对于该键而言,此映射不包含任何映射关系,则返回 null。

void clear()
//        从此映射中移除所有映射关系。

Object clone()
//        返回此 TreeMap 实例的浅表副本。

int size()
//        返回此映射中的键-值映射关系数。

Collection<V> values()
//        返回此映射包含的值的 Collection 视图。

Set<K> keySet()
//        返回此映射包含的键的 Set 视图。

Set<Map.Entry<K,V>> entrySet()
//        返回此映射中包含的映射关系的 Set 视图。      

------------------------TreeMap定义大小操作相关的api------------------------

Map.Entry<K,V> ceilingEntry(K key): 
// 大于等于给定key的最小键值对

K ceilingKey(K key): 
// 大于等于给定key的最小key

Map.Entry<K,V> floorEntry(K key): 
// 小于等于key的最大的键值对

K floorKey(K key): 
// 小于等于key最大的key

Map.Entry<K,V> higherEntry(K key):
// 大于给定key的最小键值对

K higherKey(K key): 
// 大于给定key的最小key

Map.Entry<K,V> lowerEntry(K key): 
// 小于key的最大的键值对

K lowerKey(K key): 
// 小于key最大的key

Map.Entry<K,V> firstEntry(): 返回最小的键值对

K firstKey(): 返回最小的key

Map.Entry<K,V> lastEntry(): 返回最大的键值对

K lastKey(): 返回最大的key

Map.Entry<K,V> pollFirstEntry(): 删除最小的键值对

Map.Entry<K,V> pollLastEntry(): 删除最大的键值对


---------------------视图方法-----------------------------

//        NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)
//        返回此映射的部分视图,其键的范围从 fromKey 到 toKey。
//        SortedMap<K,V> subMap(K fromKey, K toKey)
//        返回此映射的部分视图,其键值的范围从 fromKey(包括)到 toKey(不包括)。
//        SortedMap<K,V> tailMap(K fromKey)
//        返回此映射的部分视图,其键大于等于 fromKey。
//        NavigableMap<K,V> tailMap(K fromKey, boolean inclusive)
//        返回此映射的部分视图,其键大于(或等于,如果 inclusive 为 true)fromKey。
//        SortedMap<K,V> headMap(K toKey)
//        返回此映射的部分视图,其键值严格小于 toKey。
//        NavigableMap<K,V> headMap(K toKey, boolean inclusive)
//        返回此映射的部分视图,其键小于(或等于,如果 inclusive 为 true)toKey。

-------------------------一些特殊的api: 了解-------------------------------

//        NavigableSet<K> descendingKeySet()
//        返回此映射中所包含键的逆序 NavigableSet 视图。
//        NavigableMap<K,V> descendingMap()
//        返回此映射中所包含映射关系的逆序视图。
//        NavigableSet<K> navigableKeySet()
//        返回此映射中所包含键的 NavigableSet 视图。
//        Comparator<? super K> comparator()
//        返回对此映射中的键进行排序的比较器;如果此映射使用键的自然顺序,则返回 null。

Properties

Hashtable的子类

当做配置文件使用,只能存入String类型的

Properties的方法

// 注意,只能存入String类型的key,value。否则存储为properties文件的时候,会报错
// 新增key,value 使用的方法
setProperty()
    
// 根据key查询value的方法,使用
getProperty()

注:

  • 要知道properties 可以当作一个配置文件使用
  • 需要知道怎么来加载一个properties文件进来

eg:

public class Demo {
    public static void main(String[] args) throws IOException {
//        Properties properties = new Properties();
//
//        // 使用Properties都往里面存储字符串
//        properties.setProperty("username","admin");
//        properties.setProperty("password","admin");
//
//        System.out.println(properties);

        // 可以从properties配置文件中去加载
        Properties properties = new Properties();
        // load方法 可以帮助我们加载
        // 可以去相对路径下面去找
        properties.load(new FileInputStream("D:\\1.properties"));

        System.out.println(properties);

        // 也可以写出去
        properties.setProperty("address", "anhui");
        properties.store(new FileWriter("D:\\2.properties"),
                "comment");
        // 后面的comment是备注

    }
}

  • 32
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

coo1heisenberg

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

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

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

打赏作者

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

抵扣说明:

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

余额充值