Java中的Map入门

1.1. Map接口(重点)

1.1.1. 认识Map(理解)

Map,翻译为映射,在数学中的解释为:

设A、B是两个非空集合,如果存在一个法则f,使得A中的每个元素a,按法则f,在B中有唯一确定的元素b与之对应,则称f为从A到B的映射,记作f:A→B。

image.png

也就是说映射表示两个集合之间各自元素的一种“对应”的关系,在面向对象中我们使用Map来封装和表示这种关系。

image.png

从定义和结构图上,可以看出Map并不是集合,而表示两个集合之间的一种关系,故Map没有实现Collection接口。

在Map中,要求A集合中的每一个元素都可以在B集合中找到唯一的一个值与之对应。这句话可以解读为一个A集合元素只能对应一个B集合元素,也就说A集合中的元素是不允许重复的,B集合中的元素可以重复,也可不重复。那么不难推断出A集合应该是一个Set集合,B集合应该是List集合。 [图片上传失败...(image-2b76fc-1536844564929)]

我们把A集合中的元素称之为key,把B集合中的元素称之为value。

image.png

其实能看出一个Map其实就有多个key-value(键值对)组成的,每一个键值对我们使用Entry表示。

image.png

不难发现,一个Map结构也可以理解为是Entry的集合,即Set<Entry>。

image.png

一般的,我们依然习惯把Map称之为集合,不过要区分下,Set和List是单元素集合,Map是双元素集合。

  • 单元素集合:每次只能存储一个元素,比如Set和List。

  • 双元素集合:每次需要存储两个元素(一个key和一个value),比如Map。

注意:

  • Map接口并没有继承于Collection接口也没有继承于Iterable接口,所以不能直接对Map使用for-each操作。

  • 如果不能理解Map的结构,就直接记住Map每次需要存储两个值,一个是key,一个是value,其中value表示存储的数据,而key就是这一个value的名字。

1.1.2. Map常用的API方法(记住)

添加操作

  • boolean put(Object key,Object value):存储一个键值对到Map中

  • boolean putAll(Map m):把m中的所有键值对添加到当前Map中

删除操作

  • Object remove(Object key):从Map中删除指定key的键值对,并返回被删除key对应的value

修改操作

  • 无专门的方法,可以调用put方法,存储相同key,不同value的键值对,可以覆盖原来的。

查询操作

  • int size():返回当前Map中键值对个数

  • boolean isEmpty():判断当前Map中键值对个数是否为0.

  • Object get(Object key):返回Map中指定key对应的value值,如果不存在该key,返回null

  • boolean containsKey(Object key):判断Map中是否包含指定key

  • boolean containsValue(Object value):判断Map中是否包含指定value

  • Set keySet():返回Map中所有key所组成的Set集合

  • Collection values():返回Map中所有value所组成的Collection集合

  • Set<Entry> entrySet():返回Map中所有键值对所组成的Set集合

注意,标红的是重度使用的方法。

1.1.3. HashMap(重点)

HashMap底层基于哈希表算法,Map中存储的key对象的hashCode值决定了在哈希表中的存储位置,因为Map中的key是Set,所以不能保证添加的先后顺序,也不允许重复。

需求1:操作Map接口常用方法

public class HashMapDemo1{

    public static void main(String[] args) {

        Map<String, String> map = new HashMap<>();

        map.put("girl1", "西施");

        map.put("girl2", "王昭君");

        map.put("girl3", "貂蝉");

        map.put("girl4", "杨玉环");

        System.out.println("map中有多少键值对:"+map.size());

        System.out.println(map);

        System.out.println("是否包含key为girl1:"+map.containsKey("girl1"));

        System.out.println("是否包含value为貂蝉:"+map.containsValue("貂蝉"));

        //替换key为girl3的value值

        map.put("girl3", "小乔");

        System.out.println(map);

        //删除key为girl3的键值对

        map.remove("girl3");

        System.out.println(map);

    }

}

Map的迭代遍历:

//获取Map中所有的key

Set<String> keys = map.keySet();

System.out.println("Map中所有key:"+keys);

//获取Map中所有的value

Collection<String> values = map.values();

System.out.println("Map中所有value:"+values);

//获取Map中所有的key-value(键值对)

Set<Entry<String, String>> entrys = map.entrySet();

for (Entry<String, String> entry : entrys) {

    String key = entry.getKey();

    String value = entry.getValue();

    System.out.println(key+"->"+value);

}

需求2:统计一个字符串中每隔字符出现次数

public class HashMapDemo2{

    public static void main(String[] args) {

        String str = "ABCDEFABCDEABCDABCABA";

        //把字符串转换为char数组

        char[] charArray = str.toCharArray();

        //Map的key存储字符,value存储出现的次数

        Map<Character, Integer> map = new HashMap<>();

        //迭代每一个字符

        for (char ch : charArray) {

            //判断Map中是否已经存储该字符

            if (map.containsKey(ch)) {

                Integer count = map.get(ch);

                //如果已经存储该字符,则把出现次数加上1

                map.put(ch, count+1);

            }else {

                //如果没有存储该字符,则把设置次数为1

                map.put(ch, 1);

            }

        }

        System.out.println(map); 

    }

}

1.1.4. TreeMap(了解)

TreeMap底层基于红黑树算法,因为Map中的key是Set,所以不能保证添加的先后顺序,也不允许重复,但是Map中存储的key会默认使用自然排序(从小到大),和TreeSet一样,除了可以使用自然排序也可以自定义排序。

需求:测试HashMap和TreeMap中key的顺序

public class App {

    public static void main(String[] args) {

        Map<String, String> map = new HashMap<>();

        map.put("girl4", "杨玉环");

        map.put("girl2", "王昭君");

        map.put("key1", "西施");

        map.put("key3", "貂蝉");

        System.out.println(map);

        //-------------------------------------------

        map = new TreeMap<>(map);

        System.out.println(map);

    }

}

输出结果:

{key1=西施, girl4=杨玉环, key3=貂蝉, girl2=王昭君}

{girl2=王昭君, girl4=杨玉环, key1=西施, key3=貂蝉}

2. 集合框架工具类和方法

2.1. Arrays(掌握)

Arrays类是数据的工具类,其中有一个方法比较常用。

  • public static <T> List<T> asList(T... a):该方法可以把一个Object数组转换为List集合。
public class ArraysDemo{

    public static void main(String[] args) {

        //把Integer[]转换为List<Integer>

        List<Integer> list1 = Arrays.asList(1, 2, 3);

        System.out.println(list1);

        //把String[]转换为List<String>

        List<String> list2 = Arrays.asList("A", "B", "C");

        System.out.println(list2);

    }

}

注意通过Arrays.asList方法得到的List对象的长度是固定的,不能增,也不能减。

2.2. Collections(了解)

Collections是集合的工具类,封装了Set、List、Map操作的工具方法,比如拷贝、排序、搜索、比较大小等。

2.3. 斗地主发牌案例(了解)

按照斗地主游戏的规则,模拟对54张扑克牌的洗牌和发牌以及对手中牌排序操作。

具体规则:

  • 将54张不同花色的扑克牌打乱(♠♣♥♦☻)

  • 三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌

  • 查看三人各自手中的牌(按照牌的大小排序)、底牌

  • 手中扑克牌从大到小的摆放顺序:大王、小王、2、A、K、Q、J、10、9、8、7、6、5、4、3

首先我们定义好基本的代码测试框架:

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

    }

}

接下来,使用一个列表来记录四中花色:

        //花色♠♣♥♦
        List<String> colors = new ArrayList<>();
        colors.add("♠");
        colors.add("♣");
        colors.add("♥");
        colors.add("♦");

然后,想办法添加牌面。牌面有2~A;一共四种花色,所以我们可以使用一个列表,来添加4份2-A的牌面:

        //数字345678910JQKA2
        List<String> numbers = new ArrayList<>();
        Collections.addAll(numbers, 
"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2");
        numbers.addAll(numbers);//把13张复制成26张
        numbers.addAll(numbers);//把26张复制成52张

牌面最终要和花色对应,为了后面的发牌操作方便,我们定义一个Map<Integer,String>,key是我们定义的排序,可以简单的理解为我们为每一张牌定义了一个编号,value就是具体的排面+花色,相当于每一张牌和一个具体的序号一一对应:

        //大小王: 大☻ 小☻
        //定义Map,用数字来记录每一张牌
        Map<Integer, String> map = new HashMap<>();
        Integer squence = 0;//牌的顺序
        //加入其它牌
        for (String number : numbers) {
            for (String color : colors) {
                //一副牌54张,序号范围在[0,51]之间
                if (squence <= 51) {
                    map.put(squence, number + "(" + color + ")");
                    squence++;
                }
            }
        }
        map.put(squence, "小☻");
        squence++;
        map.put(squence, "大☻");
        squence++;

洗牌:

        //使用0~53来记录每一张牌
        List<Integer> cards = new ArrayList<>(map.keySet());
        Collections.shuffle(cards);
        System.out.println(cards);

接下来定义三个玩家的牌列表,然后为最后剩下的底牌定义一个列表在存放:

        //三个玩家和底牌
        List<Integer> player1 = new ArrayList<Integer>();
        List<Integer> player2 = new ArrayList<Integer>();
        List<Integer> player3 = new ArrayList<Integer>();
        List<Integer> end = new ArrayList<Integer>();

发牌:

        //发牌
        for (int index = 0; index < cards.size(); index++) {
            if (index < 51) {
                int mod = index % 3;//index除以3的余数
                int card = cards.get(index);
                if (mod == 1) {
                    player1.add(card);
                } else if (mod == 2) {
                    player2.add(card);
                } else {
                    player3.add(card);
                }
            } else {
                end.add(cards.get(index));
            }
        }

每个玩家手牌排序:

        System.out.println(player1);
        System.out.println(player2);
        System.out.println(player3);
        System.out.println(end);
        //排序
        Collections.sort(player1);
        Collections.sort(player2);
        Collections.sort(player3);
        Collections.sort(end);

注意,到这里为止,每个玩家手上的牌,仍然不是具体的牌,只是每张牌对应的序号,接下来我们就要通过序号找到对应的牌:

        //各自手上的牌
        List<String> player1Cards = new ArrayList<>();
        List<String> player2Cards = new ArrayList<>();
        List<String> player3Cards = new ArrayList<>();
        List<String> endCards = new ArrayList<>();

        for (Integer key : player1) {
            player1Cards.add(map.get(key));
        }
        for (Integer key : player2) {
            player2Cards.add(map.get(key));
        }
        for (Integer key : player3) {
            player3Cards.add(map.get(key));
        }
        for (Integer key : end) {
            endCards.add(map.get(key));
        }

看牌:

        //看牌
        System.out.println("玩家1:" + player1Cards);
        System.out.println("玩家2:" + player2Cards);
        System.out.println("玩家3:" + player3Cards);
        System.out.println("底牌  :" + endCards);

到此,发牌操作结束。

集合框架小结

List、Set、Map选用
一般的在存储元素时候,是否需要给元素起一个名字:

  • 需要:此时使用Map。
  • 不需:存储的元素使用需要保证先后添加的顺序
  • 需要:此时使用List
  • 不需:此时使用Set(如果需要保证集合元素不重复,也选用Set)

若要获得最好的学习效果,需要配合对应教学视频一起学习。需要完整教学视频,请参看https://ke.qq.com/course/272077

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341