1、map接口常用的方法
public static void main(String[] args) { /* * java.util.Map接口中常用的方法 * 1、Map和Collection没有继承关系 * 2、Map以key和value的方式存储数据:键值对 * key和value都是引用数据类型 * key和value都是存储的对象的存储地址 * key起到主导地位,value是key的一个附属品 * 3、Map接口的常用方法 * V map(K key,V value) 向map集合中添加键值对 * V get(Object key) 通过key获取value * void clear() 清空map集合 * Boolean containsKey(Object key) 判断Map中是否包含某个key * contains 底层比较都是equal方法,自定义的类都需要重写equal方法 * Boolean containsValue(Object value) 判断Map中是否包含某个value * boolean isEmpty() 判断集合中元素个数是否为0 * Set<K> keySet() 获取Map集合中所有的key * V remove(Object key) 通过指定key删除这个键值对 * int size() 获取集合中键值对的个数 * Collection<V> values() 获取集合中所有的value ,返回一个Collection * Set<Map.Entry<K,V>> entrySet() 将集合中的键值对打包返回 * */ //创建集合 Map<Integer,String> map = new HashMap<>(); //向集合中添加键值对 map.put(1,"zhangsan"); map.put(2,"lili"); map.put(3,"wanghu"); //通过key获取value String value = map.get(2); System.out.println(value); //获取键值对的数量 System.out.println("键值对的数量"+map.size()); //删除元素 map.remove(1); System.out.println("键值对的数量"+map.size()); //判断包含元素 System.out.println("是否包含元素"+map.containsKey(1)); System.out.println("是否包含值"+map.containsValue("wanghu")); //获取所有value值 Collection<String> values = map.values(); //foreach for(String s:values){ System.out.println(s); } //清空集合 map.clear(); System.out.println("键值对的数量"+map.size()); //判断集合是否为空 System.out.println(map.isEmpty()); }
2、遍历map集合
/* * Map集合的遍历 * */ Map<Integer,String> map = new HashMap<>(); //从集合中添加键值对 map.put(1,"kim"); map.put(2,"jack"); map.put(3,"boy"); //第一种:遍历map集合,获取所有的key,所以的key一个set集合 Set<Integer> keys = map.keySet(); Iterator<Integer> it = keys.iterator(); while(it.hasNext()){ Integer key = it.next(); String value = map.get(key); System.out.println(key + "=" + value); } //foreach也可以 for(Integer key: keys){ System.out.println(key + "=" + map.get(key)); } System.out.println("----------------------------------------"); //第二种:Set<Map.Entry<K,V>> entrySet() //以上这个方法把Map集合直接全部转换成Set集合 //Set集合中元素的类型是:Map.Entry Set<Map.Entry<Integer,String>> set = map.entrySet(); //遍历Set集合,每次取出一个Node //迭代器 Iterator<Map.Entry<Integer,String>> it2 = set.iterator(); while(it2.hasNext()){ Map.Entry<Integer,String> node = it2.next(); Integer key = node.getKey(); String value = node.getValue(); System.out.println(key + "=" + value); } System.out.println("****************************************************"); //foreach 效率更高,获取key和value都是从集合中取,适合大量数据类型 for(Map.Entry<Integer,String> node:set){ System.out.println(node.getKey() + "=" + node.getValue()); } }
3、哈希表的数据结构
/* *HashMap集合: * 1、HashMap集合底层是哈希表/散列表的数据结构 * 2、哈希表是一个怎么样的数据结构? * 哈希表是一个数组和单向链表的结合体 * 数组:在查询效率方面较高,随机增删方面效率低 * 单向链表:在随机增删方面效率高,在查询方面效率低 * 哈希表将以上的两种数据结构融合在一起,充分发挥他们各自的优点 * 3、HashMap集合底层的源代码 * public class HashMap{ //HashMap底层实际就是一个数组 (一维数组) Node<K,V>[] table; //静态内部类 HashMap.Node static class Node<K,V>{ final int hash; //哈希值(哈希值是key的hashCode()方法的执行结果,Hash值通过算法可以转换为数组的下标 final K key; //存储到Map集合中key final V value; //存储到Map集合中那个value Node<K,V> nextl; //下一个节点的内存地址 } } //哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表(数组和链表的结合体) *4、最主要掌握的: * map.put(k,v) * v = map.get(k) * 以上两个方法的实现原理,是必须掌握的 * 5、HashMap集合的key特点 * 无序、不可重复 * 为什么无序?一维不一定挂到哪个单向链表 * 不可重复怎么保证的?equals方法 * 如果key重复了,value要覆盖 * 放在HashSet集合中的元素也需要同时重写HashCode()和equals方法 * 注意:同一个单向链表上所有节点的hash值是相同的,因为他们的数组下标是相同的 * 当时同一个链表上的equals方法返回的false,都不相等 * 6、HashMap使用不当时,无法发挥性能 * 假设所有的hashCode()方法返回固定某个值,那么会导致底层哈希表变成纯单向链表,这种情况分为:散列分布不均匀 * 什么是散列分布均匀? * 假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的 * 假设将所有的hashCode()方法返回值设为不一样的值 * 不行,因为这样的话会导致底层哈希表成为一维数组,没有链表的概念了 * 散列函数需要重写hashCode()方法 * 7、重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法 * 8、HashMap集合的默认初始化容量是16,默认加载因子是0.75 * 这个默认加载因子是当HashMap集合底层数据的容量达到75%的时候,数组开始扩容 * 重点:HashMap集合初始化容量必须是2的倍数,这是官方推荐的 * 这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的 * */
4、同时重写hashCode和equals方法
/* * 1、 * 向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,再调用equals方法 * equals方法有可能调用,也有可能不调用 * 拿put(k,v)举例,什么时候equals不会调用 * k.hashCode()方法返回哈希值 * 哈希值经过哈希算法转换成数组下标 * 数组下标位置如果是null,equals方法不会执行 * 拿get(k,v)举例,什么时候equals不会调用 * k.hashCode()方法返回哈希值 * 哈希值经过哈希算法转换成数组下标 * 数组下标位置如果是null,equals方法不会执行 * 2、注意:如果一个类的equals方法重写,那么hashCode方法必须重写 * 并且equals方法返回如果是true,hashCode()方法返回的值必须一样 * equals方法返回的true表示两个对象相同,在同一个单向链表上比较 * 那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的 * 所以hashCode()方法的返回值应该相同 * 3、hashCode方法和equals方法不用研究,直接使用idea工具生成,但是这两个方法需要同时生成 * 4、终极结论: * 放在HashMap集合key,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法 * 5、jdk中,如果哈希表单向链表中元素超过8个,单向链表会变成红黑树这种结构,当红黑树的节点数量小于8,又会重新把红黑树变成单向链表 * 这种方式也是为了提高检索效率,二叉树的检索会再次缩小扫描范围,提高效率 * 初始容量是16,加载因子是0.75 * * */
/*
*HashMap集合:
* 1、HashMap集合底层是哈希表/散列表的数据结构
* 2、哈希表是一个怎么样的数据结构?
* 哈希表是一个数组和单向链表的结合体
* 数组:在查询效率方面较高,随机增删方面效率低
* 单向链表:在随机增删方面效率高,在查询方面效率低
* 哈希表将以上的两种数据结构融合在一起,充分发挥他们各自的优点
* 3、HashMap集合底层的源代码
* public class HashMap{
//HashMap底层实际就是一个数组 (一维数组)
Node<K,V>[] table;
//静态内部类 HashMap.Node
static class Node<K,V>{
final int hash; //哈希值(哈希值是key的hashCode()方法的执行结果,Hash值通过算法可以转换为数组的下标
final K key; //存储到Map集合中key
final V value; //存储到Map集合中那个value
Node<K,V> nextl; //下一个节点的内存地址
}
}
//哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表(数组和链表的结合体)
*4、最主要掌握的:
* map.put(k,v)
* v = map.get(k)
* 以上两个方法的实现原理,是必须掌握的
* 5、HashMap集合的key特点
* 无序、不可重复
* 为什么无序?一维不一定挂到哪个单向链表
* 不可重复怎么保证的?equals方法
* 如果key重复了,value要覆盖
* 放在HashSet集合中的元素也需要同时重写HashCode()和equals方法
* 注意:同一个单向链表上所有节点的hash值是相同的,因为他们的数组下标是相同的
* 当时同一个链表上的equals方法返回的false,都不相等
* 6、HashMap使用不当时,无法发挥性能
* 假设所有的hashCode()方法返回固定某个值,那么会导致底层哈希表变成纯单向链表,这种情况分为:散列分布不均匀
* 什么是散列分布均匀?
* 假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的
* 假设将所有的hashCode()方法返回值设为不一样的值
* 不行,因为这样的话会导致底层哈希表成为一维数组,没有链表的概念了
* 散列函数需要重写hashCode()方法
* 7、重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法
* 8、HashMap集合的默认初始化容量是16,默认加载因子是0.75
* 这个默认加载因子是当HashMap集合底层数据的容量达到75%的时候,数组开始扩容
* 重点:HashMap集合初始化容量必须是2的倍数,这是官方推荐的
* 这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的
* */