一. Map和Set的概念

1.什么是Map和Set ?

Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。

Map和Set在Java标准库中的四种对应的类:TreeMap,TreeSet和HashMap,HashSet,其在实现过程中具体体现为 二叉搜索树哈希表

我们可以回顾一下我们已经掌握的常见的搜索方式有:

  1. 直接遍历,时间复杂度为O(N),元素如果比较多效率会非常慢;
  2. 二分查找,时间复杂度为java map set 详解 java set与map_java map set 详解,但搜索前必须要求序列是有序的。

上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如:

  1. 根据姓名查询考试成绩;
  2. 通讯录,即根据姓名查询联系方式;
  3. 不重复集合,即需要先搜索关键字是否已经在集合中。

而且我们可能在查找时需要进行一些插入和删除的操作,即实现动态查找,那上述两种方式就不太适合了,本文介绍的Map和Set是一种适合动态查找的集合容器。

2.模型定义

一般把搜索的数据称为关键字(Key),关键字对应的称为值(Value),我们将其称之为Key-value的键值对,所以模型会有两种:

  1. 纯 key 模型,比如:
    有一个英文词典,快速查找一个单词是否在词典中;
    快速查找某个名字在不在通讯录中。
  2. Key-Value 模型,比如:
    统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:<单词,单词出
    现的次数>;
    梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号。

而Map中存储的就是key-value的键值对,Set中只存储了Key。这里我们需要注意的是:

  1. 关键字Key是不允许出现重复的;
  2. 键值对key-value是根据key来找value,知道key值可以确定唯一的value。

所以在选择使用Map和Set哪种数据结构时,如果我们只是需要判断key是否存在,使用Set数据结构即可;如果是需要根据key的值来查找value的内容,就需要使用Map数据结构!

二.Map和Set的使用

Map 和 Set 的用法大同小异,主要的差别是这两种结构的底层实现是完全不同的!

java map set 详解 java set与map_数据结构_02

Map的使用

1.关于Map的说明

Map是一个接口类,该类没有继承自Collection,该类中存储的是<K,V>结构的键值对,并且K一定是唯一的,不能重复。

2.关于Map.Entry<K, V>的说明

Map.Entry<K, V> 是Map内部实现的用来存放<key, value>键值对映射关系的内部类,该内部类中主要提供了<key, value>的获取,value的设置以及Key的比较方式。

java map set 详解 java set与map_java_03


注意:Map.Entry<K,V>并没有提供设置Key的方法。3.Map的常用方法说明

java map set 详解 java set与map_System_04


注意:

  1. Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者
    HashMap;
  2. Map中存放键值对的Key是唯一的,value是可以重复的;
  3. 在Map中插入键值对时,key不能为空,否则就会抛NullPointerException异常,但是value可以
    为空;
  4. Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复);
  5. Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复);
  6. Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然
    后再来进行重新插入;
  7. TreeMap和HashMap的区别:

代码演示:

public class testMap {
    public static void main(String[] args) {
        Map<String ,String> map = new HashMap<>();
        // 1. 使用 put 方法插入键值对
        map.put("及时雨", "宋江");
        map.put("黑旋风", "李逵");
        map.put("行者", "武松");
        map.put("及时雨", "宋公明");
        System.out.println(map);
    }
}

运行结果:

java map set 详解 java set与map_java map set 详解_05


根据运行结果能发现:

  1. Map 内部的元素之间的先后顺序和插入顺序关系不大~
  2. 当 put 的时候发现 key 已经存在, 此时就会覆盖原有的 value~
public class testMap {
    public static void main(String[] args) {
        Map<String ,String> map = new HashMap<>();
        // 1. 使用 put 方法插入键值对
        map.put("及时雨", "宋江");
        map.put("黑旋风", "李逵");
        map.put("行者", "武松");
        map.put("及时雨", "宋公明");
        System.out.println(map);
        //2.使用get方法,根据key值获取value
        String value1 = map.get("行者");
        String value2 = map.get("小李广");
        System.out.println(value1 +"  "+ value2);
    }
}

运行结果:

java map set 详解 java set与map_java_06


根据运行结果能发现:

  1. 如果key值不存在,get方法返回的是null。
public class testMap {
    public static void main(String[] args) {
        Map<String ,String> map = new HashMap<>();
        // 1. 使用 put 方法插入键值对
        map.put("及时雨", "宋江");
        map.put("黑旋风", "李逵");
        map.put("行者", "武松");
        map.put("及时雨", "宋公明");
        map.put("小李广", "花荣本荣");
        System.out.println(map);
        //3.使用getOrDefault方法,根据key值获取value,如果key不存在,返回默认值
        String value3 = map.getOrDefault("小李广","花荣(默认值)");
        String value4 = map.getOrDefault("豹子头"," 林冲(默认值)");
        System.out.println(value3+" "+value4);
    }
}

运行结果:

java map set 详解 java set与map_java map set 详解_07


根据运行结果能发现:

  1. 我们还可以使用 getOrDefault 来根据 key 获取 value,如果 key 不存在, getOrDefault
    返回一个默认值。
public class testMap {
    public static void main(String[] args) {
        Map<String ,String> map = new HashMap<>();
        // 1. 使用 put 方法插入键值对
        map.put("及时雨", "宋江");
        map.put("黑旋风", "李逵");
        map.put("行者", "武松");
        map.put("及时雨", "宋公明");
        map.put("小李广", "花荣本荣");
        System.out.println(map);
        // 4. 使用 isEmpty 判定空
        System.out.println("判断Map是否为空?:"+map.isEmpty());
        // 5. 使用 size 获取到键值对的个数
        System.out.println("获取Map的大小:"+map.size());
        // 6. 使用 clear 清空所有的键值对
        map.clear();
        System.out.println("使用clear()后判断Map是否为空?:"+map.isEmpty());
        System.out.println("使用clear()后获取Map的大小:"+map.size());
    }
}

运行结果:

java map set 详解 java set与map_数据结构_08


根据运行结果能发现:

  1. 使用 size ()函数,获取到的Map的大小是键值对的个数!

map.entrySet( ): 这个函数的作用是,把Map这样的键值对结构进行了转换,转换成了一个Set,Set里面的每一个元素都是一个Entey(相当于Set的key),每一个Entry里面又包含了key和value~~

public class testMap {
    public static void main(String[] args) {
        Map<String ,String> map = new HashMap<>();
        // 1. 使用 put 方法插入键值对
        map.put("及时雨", "宋江");
        map.put("黑旋风", "李逵");
        map.put("行者", "武松");
        map.put("及时雨", "宋公明");
        map.put("小李广", "花荣本荣");
        System.out.println(map);

         //7. 遍历 Map (Map 设计出来不是为了遍历!!)
           // 遍历 Map 是比较复杂的, 需要把 Map 转换成 Set 再遍历
        System.out.println("遍历Map,将键值对打印输出:");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
         //8. 还可以单独的获取到所有的 key 和所有的 value
        System.out.println("打印输出所有的key:");
        for (String key : map.keySet()) {
            System.out.print(key+" ");
        }
        System.out.println("\n"+"打印输出所有的value:");
        for (String value : map.values()) {
            System.out.print(value+" ");
        }
    }
}

运行结果:

java map set 详解 java set与map_java map set 详解_09


注意:

  1. keySet( ) 方法是把所有的key值单独存储在Set结构中,而values( )方法是把所有的value值放到Collection结构中,这么,这么做的原因是:key值是不允许重复的,value值是可以重复的。
  2. 需要强调的是entrySet( ),keySet( ),values( )方法是非常低效的方法,我们谨慎使用,尤其是在Map本身元素非常多的情况下~~

Set的使用

1.关于Set的说明

Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key。

2.Set的常用方法说明

java map set 详解 java set与map_java_10


注意:

  1. Set是继承自Collection的一个接口类;
  2. Set中只存储了key,并且要求key一定要唯一;
  3. Set的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的;
  4. Set最大的功能就是对集合中的元素进行去重;
  5. 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在
    HashSet的基础上维护了一个双向链表来记录元素的插入次序;
  6. Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入;
  7. Set中不能插入值为null 的key。;
  8. TreeSet和HashSet的区别:

代码演示

public class testSet {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        // 1. 使用 add 插入元素
        set.add("C");
        set.add("C++");
        set.add("C++");
        set.add("Java");
        set.add("Python");
        System.out.println(set);

        //2. 使用 contains 方法判定元素是否存在 [重要]
        boolean ret1 = set.contains("java");
        boolean ret2 = set.contains("Java");
        System.out.println("判断是否包含java:"+ret1);
        System.out.println("判断是否包含Java:"+ret1);

        // 3. 使用 remove 删除元素
        set.remove("Java");
        System.out.println("删除Java后的Set:"+set);

        // 4. 使用 isEmpty 来判定空
        System.out.println("判断Set是否为空:"+set.isEmpty());

        // 5. 使用 size 获取元素个数
        System.out.println("获取Set中元素个数:"+set.size());

        // 6. 使用 clear 清空元素
        set.clear();
        System.out.println("使用clear()后判断是否为空:"+set.isEmpty());
        System.out.println("使用clear()后获取Set中元素个数:"+set.size()); 
    }
}

运行结果:

java map set 详解 java set与map_java_11

根据运行结果能发现:

  1. 如果插入重复的 key, 实际只保存一份,借助 Set 的这个特性, 就可以用于 “去重”。
  2. Set结构中的元素可以直接增删查,但是没办法直接修改。
public class testSet {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        // 1. 使用 add 插入元素
        set.add("C");
        set.add("C++");
        set.add("C++");
        set.add("Java");
        set.add("Python");
        System.out.println(set);

         // 7. 遍历
         // a) 直接使用 for-each 遍历 [语法糖]
        System.out.println("使用for-each循环遍历:");
        for (String key : set) {
            System.out.print(key+" ");
        }
        //  b) 使用迭代器来遍历
        Iterator<String> it = set.iterator();
        System.out.println("\n"+"使用迭代器循环遍历:");
        while (it.hasNext()) {
            System.out.print(it.next()+" ");
        }
    }
}

运行结果:

java map set 详解 java set与map_其他_12

根据运行结果能发现:

  1. 如果这个类能支持迭代器就可以 for-each。

至此,Map和Set的入门知识就已经写完了,期待一下接下来的搜索树哈希表吧!!!