1. Map接口
Map接口是Map体系的根接口,定义了键值对的容器;Map接口允许存储 无序、无下标、键不可重复,值可重复的 键值对。
1)Map接口的实现类:
HashMap:基于哈希表(数组+链表/红黑树)实现的键值对存储结构;
TreeMap:基于红黑树实现的键值对存储结构。
注意:Map接口未实现任何接口。
2)Map接口的常用方法:
V put(K key, V value) // 将对象存入到集合中,关联键值,key重复则覆盖原值。
Object get(Object key) // 依据键获取对应的值
Set<K> keySet() // 返回所有的key的Set
Collection<V> values() // 返回包含所有value的Collection集合
Set<Map.Entry<K,V>> entrySet // 返回所有键值对key-value的Set集合
3)Map接口的遍历方式:
Map虽然没有实现Iterable接口,但其中提供了forEach循环。
· 通过keySet()方法遍历;
· 通过entrySet()方法遍历;
public class Test{
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
// 添加元素
map.put("张三",23);
map.put("李四",24);
map.put("王五",25);
// 遍历
// keySet()遍历
for (String k:map.keySet()) {
System.out.println(k + map.get(k));
}
// entrySet()遍历
for (Map.Entry<String,Integer> entry: map.entrySet()) {
System.out.println(entry.getKey()+entry.getValue());
}
}
}
1.1 实现类HashMap
1)概述:
基于哈希表的Map集合,运行效率较快,但线程不安全。HashMap的查询、添加、删除操作的时间复杂为O(1),其底层通过数组+链表/红黑树的方式实现。允许用null 作为key 或 value。其无参构造器默认构造一个初始容量为16,默认扩容因子0.75的空HashMap。
key 与 value 的对应关系:在HashMap中key与value为一对一映射关系,Map接口不允许出现key重复,但允许value重复,因此一个key只能对应一个value,但一个value可以对应多个key。
key-value的存储:
· hashcode:通过HashMap中的hashCode()方法计算出的值(Java官方直接调用Object中的Native方法hashCode()计算);
// HashMap中Node的计算方式
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
· hash:基于hashCode经过一定方法计算而成的;
// Java官方的hash计算方式
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
可以看出:
当 key 为空的时候,hash = 0;否则 hash 为 key的hashcode 与自身右移16位的结果进行异或运算。
· index(数组索引):Java官方通过位运算&计算 length-1 与 hash值得到的(相较于取模速度更快)。
对于一对key-value键值对,首先通过key计算其数组索引index,该index上没有key-value键值对则直接赋值。如果有,判断两个键值对基于key计算的hashcode是否相同,相同则用新的键值对覆盖原键值对,否则将新键值对以链表元素的方式添加进数组中。
2)HashMap的遍历
相比较于Map,HashMap在其子类KeySet与EntrySet中均实现了迭代器,可以通过迭代器遍历HashMap中的对象。也可以通过keySet()、entrySet()方法通过foreach循环遍历。
// 通过键集合keySet遍历HashMap(forEach循环)
// 假设key的类型为char
for( char k:map.keySet()){
System.out.println(map.get(k));
}
3)HashMap源码分析:
· static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 初始容量16
· static final float DEFAULT_LOAD_FACTOR = 0.75f; // 扩容因子0.75
当链表长度大于8,且散列表中的元素个数>64,将链表转换为红黑树;当中元素个数<6时,红黑树退化为链表。
HashMap中的地址碰撞:
解决方法:链地址法(链表),加入红黑树是为了在元素过多的时候提高查询效率。
核心:理解HashMap的存储、查询、添加与删除。
· V put(K key, V value)方法:
在HashMap的构造方法中并不会创建数组,而是在首次添加(put)时创建。对于put方法,首先通过传入的key计算索引index,判断该位置上是否有元素,没有则添加;有,并且key算出的hashcode相同,覆盖;有,但是key算出的hashcode不相同,添加到链中。
如果 key 为null,添加到 index = 0位置 或 index = 0的冲突链上。
· V get(K key, V value)方法:
对于put方法,首先通过传入的key计算索引index,判断该位置上是否有元素,没有则返回null;有元素则遍历该index上的链表,通过key的equals方法判断是否相等,返回相等的元素。
4)HashTable:
线程安全,运行效率慢,不允许null作为key或者是value。
Properties是HashTable的子类,要求key和value都是String,通常用于配置文件的读取。
1.2 实现类TreeMap
1)概述:
实现了SortedMap接口(Map的子接口),可以对key自动排序,要求传入Comparator用于比较entry的大小。底层存储结构为红黑树。
2)TreeMap的遍历:
TreeMap只能通过keySet()、entrySet()进行遍历。
2. Collections工具类
1)概述:
java中的工具类(实现类),类名为java.util.Conllections,定义了除了存取以外的集合的常用方法(主要是static方法与在collection对象上操作的方法)。
2)常用方法:
public static void reverse(List<?> list) // 反转集合中的元素的顺序
public static void shuffle(List<?> list) // 随机重置集合元素的顺序
public static void sort(List<T> list) // 升序排序(元素类型必须实现Comparable接口 )