Map集合总结

Map是什么

概述:map是一个集合
将键映射到值的对象
一个映射不能包含重复的键
每个键最多只能映射到一个值
Map接口和Collection接口的不同
Map是双列的,Collection是单列的
Map的键唯一,Collection的子体系Set是唯一的
Map集合的数据结构针对键有效,跟值无关;Collection集合的数据结构是针对元素有效

HashMap怎么用

/**
 * 散列表 : 可以理解为数组保存元素是个链表
 *
 *  散列表中保存的是键值对(K和V)
 *
 *  hashCode : hash算法,是把不定长的数据改变为定长的数据,是一种安全的加密算法,但不保证唯一
 *  		同一个对象生成多次hash值,那么值一定是相同的,
 *  		不同对象也有可能生成相同的hash值
 *
 *  添加过程 :
 *  		1 先调用添加的K,调用hashCode生成hash值
 *  		2 根据hash值计算数组下标
 *  		3 判断数组中该下标对应的位置上是否有元素
 *  		3.1 如果没有保存数据,就把该对象放到对应的下标中
 *  		3.2 如果保存了数据,此时调用添加的K的equals方法,和数组中该下标对应的所有数据的key进行比较
 *  		3.3 如果和数组下标对应的链表中 的数据 都不相等,就把该数据添加到对应的链表中
 *  		3.4 如果和链表中的数据一致了,则key不添加,把value值替换(用新的替换原来的)
 *  		4 java1.8新改动,如果该链表中,节点个数大于7,则该链表被转换为红黑树
 *
 *  在java把 没有散列表这个说法,只是把散列表封装为了HashMap和HashTable,并且HashTable已经过时
 *  	并且 HashMap的默认容量为 16
 *
 * HashSet 底层就是一个HashMap,并且只是Map的key部分,没有value
 *
 */
public class HashSet_1 {
    public static void main(String[] args) {
//        创建HashSet对象
        HashSet<String> set = new HashSet<String>();
        set.add("xxx");
        set.add("xxx");
        set.add("x1");
        set.add("x2");
        set.add("x3");
        set.add("x4");
        set.add("x5");
        System.out.println(set.size());
    }
}

简单练习

import java.util.HashSet;
import java.util.Set;

public class HashSet_2 {
    public static void main(String[] args) {
        // 规定姓名相同认为是同一个对象
        User u1 = new User(18,"lisa");
        User u2 = new User(27,"lisa");
        Set<User> users = new HashSet<>();
        users.add(u1);
        users.add(u2);
        System.out.println(users.size());
        for (User user : users){
            //User{age=18, name='lisa'}说明不添加而不是替换
            System.out.println(user);
        }
    }
}
class User{
    private int age;
    private String name;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;

        User user = (User) o;

        return name.equals(user.name);
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

Map特性好处

1.包含键值对
2.键唯一
3.键对应的值唯一

Map继承体系图

Map接口常用方法

V put​(K key, V value) 向Map集合中添加键值对

V get​(Object key)通过可以获取value

void clear()清空Map集合

boolean containsKey(Object key)判断Map中是否包含某个key

boolean containsValue(Object value)判断Map中是否包含某个value

boolean isEmpty()判断Map集合中元素个数是否为0

Set<K> keySet()获取Map集合所有的key(所有的键是一个set集合)

V remove​(Object key) 通过key删除键值对

int size() 获取Map集合中键值对的个数。

Collection<V> values() 获取Map集合中所有的value,返回一个Collection

Set<Map.Entry<K,​V>> entrySet() b 将Map集合转换成Set集合

注意:

Map.Entry和String一样,都是一种类型的名字,只不过:Map.Entry是静态内部类,是Map中的静态类
Map集合通过entrySet()的方法转换成的这个Set集合,Set集合中的元素的类型都是Map.Entry<K,V>

 遍历Map集合

import java.util.*;

public class MapText {
    public static void main(String[] args) {

        //第一种方式:获取所有Key,通过遍历key。来遍历value
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"zhangsan");
        map.put(2,"lisi");
        map.put(3,"wangwu");
        map.put(4,"zhaoliu");

 第一种方式:获取所有Key,通过遍历key。来遍历value

   //遍历Map集合
        //获取所有的key,所有的key是一个Set集合
        Set<Integer> keys = map.keySet();
        //遍历key,通过key返回value
        //迭代器也可以
        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));
        }

第二种方法:Set<Map.Entry<K,​V>> entrySet() b 将Map集合转换成Set集合,Set集合中元素的类型是:Map.Entry

Map.Entry和String一样,都是一种类型的名字,只不过:Map.Entry是静态内部类,是Map中的静态类

 Set<Map.Entry<Integer,String>> set = map.entrySet();
        //遍历Set集合,每一次取出一个Node
        //迭代器
        Iterator<Map.Entry<Integer,String>> it = set.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }

或者 

        Set<Map.Entry<Integer,String>> set = map.entrySet();
        //遍历Set集合,每一次取出一个Node
        //迭代器
        Iterator<Map.Entry<Integer,String>> it = set.iterator();
        while (it.hasNext()){
            Map.Entry<Integer,String> node = it.next();
            Integer key = node.getKey();
            String value = node.getValue();
            System.out.println(key + "=" + value );
        }

foreach:

  • 这种方式效率比较高,因为获取key和value都是直接从node对象中获取的属性值
  • 这种方式比较适合大数据

HashMap

HashMap集合底层是哈希表/散列表的数据结构。

在JDK8之后,如果哈希表单向链表中元素超过8个,单向链表这种数据结构会变成红黑树数据结构。当红黑树上的节点数量小于6时,会重新把红黑树变成单向链表数据结构。

这种方式也是为了提高检索效率,二叉树的检索会再次缩小扫描范围。提高效率。

 哈希表

哈希表是一个一维数组,这个数组中每一个元素是一个单向链表。(数组和链表的结合体) 

哈希表是一个数组和单向链表的结合体

数组:在查询方面效率很高,随机增删效率很低。
单向链表:在随机增删方面效率很高,在查询方面效率很低
哈希表将以上的两种数据结构融合在一起,充分发挥了它们各自的优点。
对于哈希表数据结构来说:

如果o1和o2的hash值相同,一定是放到同一个单向链表上。
当然如果o1和o2的hash值不相同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”

HashMap简单使用

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
 * Map : 无序 , key不可重复,value值可重复
 *
 * Map和Collection不一样,但是操作基本上是一样的
 *
 * 集合保存单个对象,而map保存键值对映射关系
 *
 * 常用方法 :
 * 		put(K,V) : 添加数据
 * 		remove(K) : 删除数据
 * 		clear() : 清空
 * 		size() : 个数
 * 		isEmpty() : 判断是否为空
 * 		get(K) : 根据Key获取value
 * 		values() : 获取所有的value,返回集合
 * 		containsKey(K) : 判断是否包含某个key
 * 		containsValue(V) : 判断是否包含某个value
 * 		Set keySet() : 获取map中所有的key,返回set
 * 		Set entrySet() : 获取map中的键值对,返回set
 */
public class HashMap_ {
    public static void main(String[] args) {
        // 创建map
        Map<String,Integer> map = new HashMap<String,Integer>();
        map.put("a",32);
        map.put("a",14);
        map.put("A",1);
        map.put("A",5);
        map.put("65",7);
        map.put("65",45);
        //打印个数
        System.out.println(map.size());
        // 根据key获取value , 2 因为key重复,value替换
        System.out.println(map.get("a"));
        // 是否包含某个value
        System.out.println(map.containsKey("65"));
        System.out.println(map.containsValue(5));
        // 根据key删除该映射关系(K和V都删除,在链表中把该节点删除)
//        map.remove("a");
        //判断个数
        System.out.println(map.size());
        // map不能直接遍历
        // 获取所有的value
        Collection values = map.values();
//        遍历键值对中的值
        for (Object obj : values){
            System.out.println(obj);
        }
        //获取所有的key
        Set<String> set = map.keySet();
        for (String key : set){
            System.out.println(key);
        }
        // 将map转换为set,并把key和value封装到了entry类对象中,然后把entry类对象保存到set中即可
        Set<Map.Entry<String,Integer>> entries = map.entrySet();
        for (Map.Entry<String,Integer> entry:entries){
            System.out.println(entry);
            // getKey 是获取key,getValue 是获取value
            System.out.println(entry.getKey()+"->"+entry.getValue());
        }
    }
}

为什么哈希表的随机增删,以及查询效率都很高?

增删是在链表上完成的。
查询也不需要都扫描,只需要部分扫描。

 HashMap集合的key特点

无序不可重复

为什么无序? 因为不一定挂在哪个单向链表上。
不可重复是怎么保证的? equals()方法来保证HashMap集合的key不可重复。如果key重复了,value会覆盖。
放在HashMap集合key部分的元素其实就是放到HashSet集合中了。

所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法
哈希表HashMap使用不当时无法发挥性能

假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。这种情况我们称为:散列分布不均匀

什么实际散列分布均匀

假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的。

 假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗?

不可以,因为这样的话导致底层哈希表就变成了一维数组了,没有链表的概念了。也是散列分布不均匀。

 

必须同时重写hashCode和equals方法

放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法

并且 equals() 方法返回如果是true,hasCode() 方法返回的值必须一样

HashMap集合容量

HashMap集合的默认初始化容量是16默认加载因子是0.75

这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。

 重点:HashMap集合的初始化容量必须是2的倍数,这也是官方推荐的,这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必需的。

HashMap集合key允许为null

注意:但是HashMap集合keynull只能有一个

HashMap和Hashtable的区别

Hashtable的keyvalue都是不能为null的。
HashMap集合的keyvalue都是可以为null的。

Hashtable集合初始化容量是11集合扩容是:原容量*2+1

Properties

Properties是一个Map集合继承Hashtable,Properties的keyvalue都是String类型;

Properties被称为属性类对象

Properties是线程安全的。

import java.util.Properties;

public class PropertiesText {
    public static void main(String[] args) {
        //创建一个Properties对象
        Properties pro = new Properties();

        //需要掌握Properties的两个方法,一个存,一个取
        pro.setProperty("url","jdbc:mysql://localhost:3306/bjpowernode");
        pro.setProperty("driver","com.mysql.jdbc.Driver");
        pro.setProperty("username","root");
        pro.setProperty("password","123");

        //通过key获取value
         String url = pro.getProperty("url");
         String driver = pro.getProperty("driver");
         String username = pro.getProperty("username") ;
         String password = pro.getProperty("password");

        System.out.println(url);
        System.out.println(driver);
        System.out.println(username);
        System.out.println(password);
    }
}

 在这里插入图片描述

//Properties : key和value强制要求必须是字符串
import java.util.Properties;
public class Properties_ {
    public static void main(String[] args) {
        Properties ppts = new Properties();
        // 添加数据
        ppts.setProperty("driver", "mysql");
        ppts.setProperty("username", "root");
        // 获取数据
        System.out.println(ppts.getProperty("driver"));//mysql
        System.out.println(ppts.getProperty("username"));//root
        // 不存在的key  得到null
        System.out.println(ppts.getProperty("rwer"));//null
        // 有个方法重载,第二个参数为默认值,假如根据key找不到数据的时候,返回该默认值,而不是null
        // 并不会把该键值对添加进去

        System.out.println(ppts.getProperty("rwer","默认值"));      //默认值
    }
}

TreeMap

TreeMap集合底层实际上是一个二叉树。

//导入三个核心类包
import java.util.Comparator;
import java.util.Set;
import java.util.TreeMap;
public class TreeMap_ {
    public static void main(String[] args) {
        //TreeMap : 保存的元素可以按照一定的规则进行排序
//        排序 :
//        1 要添加的元素 实现了Comparable接口
//        2 编写比较器类,实现Comparator接口
        TreeMap<Integer,String> map = new TreeMap<Integer,String>(new Comparator<Integer>() {
            @Override

            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
//treeMap 在添加的时候 会自动调用key对象的compareTo方法,是用key进行比较,而不是value
        map.put(1,"a");
        map.put(2,"a");
        map.put(14,"a");
        map.put(13,"a");
        map.put(51,"a");
        Set set = map.entrySet();
        for (Object obj : set){
//            51=a
//            14=a
//            13=a
//            2=a
//            1=a
            System.out.println(obj);
        }
    }
}

ToList

import java.util.*;
/**
 * Map转换为List存储,并且以value进行排序
 *
 * 因为map没有办法以value排序,因为treeMap中只是按照key排序的,所以想要以value排序,需要转换为list
 */
public class ToList {
    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<String,Integer>();
//        添加数据
        map.put("a",13);
        map.put("hhh",1);
        map.put("bbb",3);
        map.put("asd",1);
        map.put("kpl",41);
        // 把K和V封装到entry中,然后保存到set中
        Set<Map.Entry<String,Integer>> set = map.entrySet();
        // set转换为list
        List<Map.Entry<String,Integer>> list = new ArrayList<Map.Entry<String,Integer>>(set);
//        匿名类
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                // 按照value值进行比较  o1-o2升序
                return o1.getValue() - o2.getValue();
            }
        });
        //[asd=1, hhh=1, bbb=3, a=13, kpl=41]
        System.out.println(list);
    }
}

练习题

需求

/**
 * 给出了一个字符串,只包括字母 "akshdasdbqwehhuigfahsbd"
 *
 * 计算字符串中每个字符出现的个数,并以value进行降序排序,加入value相等,则以字符升序排序
 *
 *     最终输出格式为 z(5)a(3)b(3)c(2)d(1)
 *
 * 1 既然是字符和次数,肯定是键值对存储,key表示字符,value表示个数
 * 2 遍历字符串,把每个字符取出
 * 3 判断map中是否包含这个key,如果包含 value+1 , 不包含,添加进去value默认为1
 * 4 通过entrySet转换为set
 * 5 通过new ArrayList(set) 转换为list
 * 6 通过collections.sort() 编写匿名内部类进行排序
 *
 */
public class Test_ {
    public static void main(String[] args) {
//        创建HashMap集合对象    key是Character包装类型,值是Integer包装类型
        Map<Character,Integer> map = new HashMap<Character,Integer>();
        String str = "aksUaIsdOhdasdGDJawIpoUqSHoiGhuigfaIaUwJsOfIhsKbKJLKd";
//        遍历字符串
        for (int i = 0;i<str.length();i++){
//            获取每一个字符,调用charAt()方法
            char c = str.charAt(i);
//            判断条件是'a'到'z'或'A'到'Z'的范围区间
            if (c>='a'&&c<='z'||c>='A'&&c<='Z'){
//                再判断是否包含键值对中的键(key)
                if (map.containsKey(c)){
//                    获取集合中的字符元素赋给int类型,it来接收,it就是value
                    int it = map.get(c);
//                    包含 那么 value值  it就+1  键保持不变
                    map.put(c,it+1);
                }else {
//                    不包含,添加进去value(it)默认为1
                    map.put(c,1);
                }
            }
        }
        //        通过entrySet转换为set
        Set<Map.Entry<Character,Integer>> entries = map.entrySet();
//        通过new ArrayList(set) 转换为list
        List<Map.Entry<Character,Integer>> list = new ArrayList<Map.Entry<Character,Integer>>(entries);
//        通过collections.sort() 编写匿名内部类进行排序
        Collections.sort(list, new Comparator<Map.Entry<Character, Integer>>() {
            @Override
            public int compare(Map.Entry<Character, Integer> o1, Map.Entry<Character, Integer> o2) {
                return o2.getValue()-o1.getValue();
            }
        });
        System.out.println(list);
//        将集合元素保存的<K,V>遍历 K就是字母 V就是出现的次数
        for (Map.Entry<Character,Integer> entry : list){

//            打印K,V
            System.out.println("字母"+entry.getKey()+"出现了("+entry.getValue()+")次");
        }
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

让火车在天上飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值