目录
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()方法的执行结果,
- //哈希值通过哈希函数/算法,可以转换存储成数组的下标。
- Final K key; //存储到Map集合中的那个key
- V value; //存储到Map集合中的那个value
- Node<K,V> next; //下一个节点的内存地址。
- }
- }
- public class HashMap{
- 4、HashMap集合的key部分特点:
- 无序,不可重复。
- 为什么无序?
- 因为不一定挂到哪个单向链表上。
- 不可重复是怎么保证的?
- equals方法来保证HashMap集合的key不可重复,如果key重复了,value会覆盖。
- 5、哈希表HashMap使用不当时无法发挥性能!
- 假设将所有的hashCode()方法返回值固定位某个值,那么会导致底层哈希表变成了纯单项链表。
- 这种情况我们称为:散列分布不均匀。
- 什么是散列分布均匀?
- 假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀。
- 假设将所有的hashCode()方法返回值都设定位不一样的值,可以吗,有什么问题?
- 不行,因为这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。也是散列分布不均匀。
- 散列分布均匀需要重写hashCode()方法时有一定的技巧。
- 6、重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法。
- 7、HashMap集合的默认初始化容量是16,默认加载因子是0.75.
- 这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。扩容之后的容量时原容量的2倍。
- 重点:
- HashMap集合初始化容量必须是2的倍数,这也是官方推荐的。
- 这是为了达到散列均匀,为了提高HashMap检索的存取效率,所以是必须的。
- 8、向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后在调用equals方法。当下标位置上是null时,equals不需要执行。
终极结论:
放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。
注意:
同一个单向链表上所有节点的hash相同,因为他们的数组下标是一样的。
但同一个链表上k和k的equals方法肯定返回的时false,都不相等。
对于哈希表数组结构来说:
如果o1和o2的hash值相同,一定是放到同一个单向链表上。
如果o1和o2的hash值不同,但时由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”。
Hashtable集合的特点:
- Hashtable的key可以为null吗?
- Hashtable的key和value都是不能为null的。
- HashMap集合的key和value都是可以为null的。
- Hashtable方法都带有synchronized:是线程安全的。
- 线程安全有其他的方案,这个Hashtable对线程的处理导致效率较低,使用较少了。
- Hashtable和HashMap一样,底层都是哈希表数据结构。
- Hashtable的初始化容量是11,默认加载因子是:0.75f
- Hashtable的扩容是:原容量 * 2 + 1
Properties集合的特点:
- Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型。
- Properties被称为属性类对象。
- Properties是线程安全的。
- Properties还可以用于从XXX.properties文件中,加载数据到Properties类对象,进行读取和修改
Properties的常用方法:
- put(); //添加数据
- remove(K k); //删除数据
- get(K k); //查找指定k的value
- getProperty(k); //根据指定的key来获取value
- setProperty(k,v); //调用Hashtable的方法put,添加元素。
自平衡二叉树:
1、TreeSet/TreeMap是自平衡二叉树,遵循左小右大原则存放。
2、遍历二叉树的时候有三种方式:
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
注意:前中后说的是根的位置。
3、TreeSet集合/TreeMap集合采用的是:中序遍历方式
Iterator迭代器采用的是中序遍历方式。
4、存放的过程就是排序的过程,取出来就是自动按照大小顺序排列的。
TreeSet和TreeMap排序的结论:
第一种方式:放在集合中的元素实现java.util.Comparable接口。
第二种方式:在结构TreeSet或者TreeMap集合的时候给他传一个比较器对象。
Comparable和Comparator怎么选择呢?
当比较规则不会发生改变的时候,或者说当比较规则只有一个的时候,建议实现Comparable接口。
如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。
Comparator接口的设计符合OCP原则。