Map接口
Map接口,双列集合(每个元素,包含一个键(key),一个值对象(Value),键与值之间的对应关系叫做映射),Map中每个元素的键不允许重复,访问时只要指定键,就可以找到对应的值
Map中特有的方法有:
put(K key, V value)
: 向map中添加一个映射关系,如果key已存在,则更新其对应的valueputAll(Map<? extends K,? extends V> m)
: 将指定map中的所有映射关系复制到当前map中remove(Object key)
: 根据指定的key删除对应的映射关系containsKey(Object key)
: 判断map中是否包含指定的keycontainsValue(Object value)
: 判断map中是否包含指定的valueget(Object key)
: 根据指定的key获取对应的value,如果key不存在,则返回nullisEmpty()
: 判断map是否为空size()
: 获取map中映射关系的数量values()
: 返回map中所有value的集合keySet()
: 返回map中所有key的集合
代码:
import java.util.Map;
import java.util.HashMap;public class MapExample {public static void main(String[] args) {// 创建一个HashMap实例Map<String, Integer> map = new HashMap<>();// put方法 - 添加或更新键值对map.put("one", 1);map.put("two", 2);map.put("three", 3);// get方法 - 根据键获取值Integer value = map.get("two");System.out.println("Value for 'two': " + value);// remove方法 - 根据键删除键值对map.remove("two");System.out.println("Map after removing 'two': " + map);// containsKey方法 - 检查map中是否包含某个键boolean hasOne = map.containsKey("one");System.out.println("Does map contain 'one'? " + hasOne);// containsValue方法 - 检查map中是否包含某个值boolean hasValueTwo = map.containsValue(2);System.out.println("Does map contain value 2? " + hasValueTwo);// isEmpty方法 - 检查map是否为空boolean isEmpty = map.isEmpty();System.out.println("Is map empty? " + isEmpty);// size方法 - 获取map中键值对的数量int size = map.size();System.out.println("Size of map: " + size);// keySet方法 - 返回map中所有键的集合for (String key : map.keySet()) {System.out.println("Key: " + key + ", Value: " + map.get(key));}// values方法 - 返回map中所有值的集合for (Integer value : map.values()) {System.out.println("Value: " + value);}// putAll方法 - 将另一个map的所有映射复制到当前mapMap<String, Integer> anotherMap = new HashMap<>();anotherMap.put("four", 4);anotherMap.put("five", 5);map.putAll(anotherMap);System.out.println("Map after putAll: " + map);}
}
HashMap实现类
HashMap是Map接口的一个实现类,HashMap集合用于存储键值映射的关系,其中没有重复的键,元素无序(其大部分方法都来源于Map接口)
HashMap的底层逻辑:
-
数组+链表(或红黑树):内部使用一个数组来存储键值对。每个数组元素是一个桶(bucket),桶中可能包含一个链表(或红黑树),用于存储具有相同哈希值的键值对。当哈希冲突发生时(即多个键具有相同的哈希值),它们会被放在同一个桶的链表中
-
哈希函数:使用哈希函数来计算键的哈希值。哈希函数的目标是将键均匀分布在数组的各个位置,以便减少冲突和查找时间。哈希函数的选择对于的性能至关重要
-
哈希冲突解决:当哈希冲突发生时,采用链地址法(chaining)来解决。即,所有具有相同哈希值的键值对被放入同一个桶的链表中。如果链表过长(默认阈值为8),会将其转换为红黑树,以提高搜索效率
-
扩容:当中的元素数量超过数组大小的一定比例(加载因子,默认为0.75)时,会进行扩容。扩容时,它会创建一个新的更大的数组,并将原有数组中的元素重新计算哈希值并放入新数组中。扩容是一个相对耗时的操作,因此在初始化时可以指定一个合适的初始容量和加载因子,以减少扩容次数
以下是部分底层代码:
public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {// 默认初始容量和加载因子static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16static final float DEFAULT_LOAD_FACTOR = 0.75f;// Node类,用于存储键值对static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> left;Node<K,V> right;Node(int hash, K key, V value, Node<K,V> left, Node<K,V> right) {this.hash = hash;this.key = key;this.value = value;this.left = left;this.right = right;}// ... 其他方法 ...}// 用于存储桶的数组transient Node<K,V>[] table;// 用于快速失败的迭代器transient Iterator<Map.Entry<K,V>> iterator;// 其他字段和方法...// 计算键的哈希值,并根据哈希值确定其应该放在数组中的索引final int indexFor(int h, int length) {return h & (length-1);}// 添加或更新键值对public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}// 根据键获取值public V get(Object key) {Node<K,V> e;return (e = getNode(hash(key), key)) == null ? null : e.value;}// 删除键值对public V remove(Object key) {Node<K,V> e;return (e = removeNode(hash(key), key, null, false, true)) == null ?null : e.value;}// 其他方法,如扩容、调整大小、计算哈希值等...
}
TreeMap实现类
TreeMap
是 Java 中Map 接口的一个实现类,它使用红黑树(Red-Black tree)作为底层数据结构来存储键值对。TreeMap的特点是元素按键的自然顺序或者构造器传入的 Comparator
指定的顺序进行排序。因此,TreeMap
不允许使用null
作为键,因为 null
没有自然顺序,且无法与 Comparator
比较
TreeMap
实现了 NavigableMap
接口,这意味着它支持一系列导航方法,这些方法允许程序员以某种顺序遍历键集、值集或键值对集
下面简单概述 TreeMap
的一些主要特性和方法:
主要特性
- 排序:
TreeMap
中的元素按键进行排序。如果没有提供比较器Comparator()
,则使用键的自然顺序 - 不允许
null
键:因为null没有排序依据 - 效率:由于底层使用红黑树,
TreeMap
的插入、删除和查找操作的平均时间复杂度都是 O(log n)。 - 线程不安全:
HashMap
TreeMap
与 类似,TreeMap
也不是线程安全的。在多线程环境下使用需要额外的同步措施
主要方法
put(K key, V value)
: 向TreeMap
中插入或更新一个键值对。get(Object key)
: 根据键获取对应的值。remove(Object key)
: 删除指定的键值对。firstKey()
: 返回当前映射中的第一个(最小)键。lastKey()
: 返回当前映射中的最后一个(最大)键。subMap(fromKey, toKey)
: 返回此映射部分的视图,其键的范围从 (包含)到 (不包含)。fromKey
toKey
headMap(toKey)
: 返回此映射部分的视图,toKey
其键小于tailMap(fromKey)
: 返回此映射部分的视图,fromKey
其键大于或等于
示例
import java.util.TreeMap;public class TreeMapExample {public static void main(String[] args) {TreeMap<String, Integer> treeMap = new TreeMap<>();// 添加元素treeMap.put("apple", 1);treeMap.put("banana", 2);treeMap.put("cherry", 3);// 输出映射System.out.println(treeMap); // {apple=1, banana=2, cherry=3}// 获取第一个和最后一个键System.out.println("First Key: " + treeMap.firstKey()); // appleSystem.out.println("Last Key: " + treeMap.lastKey()); // cherry// 遍历键for (String key : treeMap.keySet()) {System.out.println("Key: " + key + ", Value: " + treeMap.get(key));}// 使用子映射TreeMap<String, Integer> subMap = treeMap.subMap("apple", "cherry");System.out.println("SubMap: " + subMap); // {apple=1, banana=2}// 移除元素treeMap.remove("banana");System.out.println("After removal: " + treeMap); // {apple=1, cherry=3}}}
HashTable实现类
HashTable与HashMap类十分相似,区别是前者是线程安全的,但HashTable存取元素缓慢,基本上已经被HashMap取代,重要的是,HashTable有一个子类在实际应用中使用广泛,其为Properties其主要用于储存字符串类型的键和值,在实际开发中经常使用Properties存储置顶项