Map集合 特点、底层分析、常用方法、TreeMap的底层分析、Collections常用方法介绍

100 阅读6分钟

Map集合

Map接口的特点

  1. Map中key value可以是任意数据l类型,会封装到HashMap$Node中(接口的多态-是一种数据类型,HashSet的内部类)
  2. Map中不允许Key重复,value可以重复
  3. key可以为null,但只能有一个,value可以为null,并不影响
  4. key、value 是一对一的关系,通过key总能找到对应的value
  5. Map存放数据的key-value示意图,一对k-v是放在HashMap$Node中,因为Node实现了Entry接口
  6. 线程不安全

HashMap中keyvalue.png

Map的底层:

table表(数组+双向链表+红黑树的结构)来组织HashMap$Node,把Node封装成Entry,存放到EntrySet集合中,除此之外可提供Keyset,可以单独把key存在Set集合中,value也是一样的提供一个values存储在Collection集合中

Map集合的常用方法

  1. put 添加
  2. remove 根据键删除值
  3. get 根据键获取值
  4. size 获取元素个数
  5. isEmpty 判断个数是否为0
  6. clear 清除
  7. comtainsKey 查找键是否存在

Map的六大遍历方式

//        方式一 通过Map.keySet使用iterator遍历
        Iterator<String > iterator1 = map.keySet().iterator();
        Iterator<String > iterator2 = map.values().iterator();
        while (iterator1.hasNext()) {
            String next =  iterator1.next();
            Object o = map.get(next); // 获取值
            System.out.println(o);
//            System.out.println(next);
        }
//      方式二 通过Map.entrySet使用iterator遍历
        Iterator<Map.Entry<String ,String >> entry = map.entrySet().iterator();
        while (entry.hasNext()) {
            Object next =  entry.next();
            System.out.println(next);
        }
//      方式三 通过Map.keySet遍历
        for (String key : map.keySet()
             ) {
            System.out.println("key:" + key +" value:"+ map.get(key));
        }
//        方式四 通过For-Each迭代entries,使用Map.entrySet遍历
        for (Map.Entry<String , String > entry1: map.entrySet()
             ) {
            System.out.println("key:" + entry1.getKey() +" value:"+ map.get(entry1.getKey()));

        }
//        方式五 使用lambda表达式forEach遍历
        map.forEach((k,v)-> System.out.println("key = "+k+",value="+v));

HashMap底层原理

结论

扩容机制:与HashSet完全一直

  1. HashMap底层维护了Node类型的数组table,默认为null

  2. 当创建对象时,将加载因子初始化为0.75

  3. 当添加key-value时,通过key的hash值得到table的索引,然后判断索引是否有该元素,如果没有就添加,如果有就通过equals判断key是否相等,如果相等,就替换val值。如果不想等需要判断是树结构还是链表结构,做出相应的处理,如果添加时发现容量不够,就会进行扩容。

  4. 第一次添加,需要扩容table容量为16,临界值为12

  5. 以后扩容则需要扩容table值的两倍32,临界值为原来的2倍,24,一次类推

  6. 在java8之后,如果一条链表的元素超过8 ,并且table的大小超过64就会树化(红黑树)

            // 树化的条件  
    
    		// 扩容条件
            if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
                        resize(); 
    
    		// 树化代码	
            else if ((e = tab[index = (n - 1) & hash]) != null) {
                TreeNode<K,V> hd = null, tl = null;
                do {
                    TreeNode<K,V> p = replacementTreeNode(e, null);
                    if (tl == null)
                        hd = p;
                    else {
                        p.prev = tl;
                        tl.next = p;
                    }
                    tl = p;
                } while ((e = e.next) != null);
                if ((tab[index] = hd) != null)
                    hd.treeify(tab);
            }
    

HashMap底层结构.png

HashTable

1. 结论

  1. HashTable的键和值都不能为null
  2. HashTable是线程安全,HashMap是线程不安全
  3. 使用方法和Hashmap使用基本一致
  4. 初始大小为11,扩容2n+1,临界值也是容量*0.75

properties

  1. 继承HashTable并实现了Map接口,也是键值对形式来存储数据。
  2. 特点与HashTable类似
  3. properties还可以用于从xxx.properties文件中,加载数据到properties类对象,并进行读取和修改
  4. xxx.properties文件通常用于配置文件,与io流有关

工作中集合的选择

集合的使用

  1. 先判断存储的类型(一组对象或一组键值对)
2.一组对象:Collection接口 允许重复: List 
3.增删多: LinkedList (OBject [] elementData) [底层维护了一个双向链表] 
4.改查多: ArrayList [底层维护Object类型的可变数组] 初始值:10 初始值:10 扩容大小 
1.5

  ArrayList 调用的是Arrays.CopyOf方法,当插入数据,导致size + 1 > 
  elementData.length,也就是目前数组长度不够时,需要进行扩容。

   Vector:在多线程中使用,扩容机制为初始容量10,扩容大小为2n

不允许重复: Set;

无序: HashSet [底层是HashMap,维护了一个哈希表即(数组+链表+红黑树)]

排序: TreeSet
插入和取出顺序一致: LinkedHashSet,维护数组+双向链表

3.  一组键值对:Map

键无序: HashMap[底层是:哈希表 jdk7:数组+链表,jdk8:数组+链表+红黑树]

1. HashMap扩容 第一次扩容为16,加载因子为0.75,扩容发生为加载因子*容量,达到这个值就进行

扩容,当一条链表的数量达到8也会进行数组扩容,当数组扩容到64,就会进行红黑树化,之后数组不再
进行扩容。

键排序: TreeMap

键插入和取出顺序一致: LinkedHashMap

读取文件:Properties

### TreeSet

*   最大 特点:可以排序
*   底层是TreeMap

```java
public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}

自定义TreeMap排序

package JavaTest;

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetTest {
    public static void main(String[] args) {
        // 匿名内部类重写compareTo排序规则
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
//                return ((String) o1 ).compareTo((String) o2);
//              按照长度大小进行排序,规则长度相等就是相同元素
                return ((String) o1 ).length() - ((String) o2).length();
            }
        });
        treeSet.add("aaaa");
        treeSet.add("lzq");
        treeSet.add("zs");
        treeSet.add("ls");

        System.out.println(treeSet);
    }
}

底层源码的实现机制

  • 核心代码
1. 构造器把传入的比较器对象,赋给了 TreeSet 的底层的 TreeMap 的属性 this.comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
2. 在 调用 treeSet.add("tom"), 在底层会执行到
if (cpr != null) {//cpr 就是我们的匿名内部类(对象)
do {
parent = t;
//动态绑定到我们的匿名内部类(对象)compare
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果相等,即返回 0,这个 Key 就没有加入
return t.setValue(value);
} while (t != null);
}

TreeMap

  • 核心代码
1. 构造器. 把传入的实现了 Comparator 接口的匿名内部类(对象),传给给 TreeMap 的 comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
2. 调用 put 方法
2.1 第一次添加, 把 k-v 封装到 Entry 对象,放入 root
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
2.2 以后添加
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do { //遍历所有的 key , 给当前 key 找到适当位置
parent = t;
cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类的 compare
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果遍历过程中,发现准备添加 Key 和当前已有的 Key 相等,就不
return t.setValue(value);
} while (t != null);
}
*/

Collections工具类

介绍

  1. Collections是一个操作Set、List、Map等集合的工具类
  2. 提供了一系列的静态方法,对集合元素进行排序,查询、修改等操作

常用方法

  1. reverse(list) 反转集合中元素的顺序
  2. shuffle(list) 对list集合元素进行随机排序
  3. sort(list)根据元素的自然顺序对指定list集合元素进行升序
  4. swap(list , int i, int j ) 根据制度list集合中的i出元素 , 和j出元素进行交换顺序
  5. max(list) 根据元素的自然顺序,返回给定集合中的最大元素
  6. max(list,new Comparator) 根据Comparator指定的顺序,返回给定集合中的最大元素
  7. min(list)
  8. ming(list,Comparator)
  9. copy(list,dest,list src) 将src中的内容赋值到desc中,dest中的空间不能小于src集合的空间,否则会报数组长度越界,IndexoutOfBoundsException
  10. replaceAll(list,oldVal,newVal);