Java集合-Map

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

/**

* 测试Map集合的常用方法

* add、remove、get、containKey、keySet、entrySet

*/

public class TestMap {

    public static void main(String[] args) {

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

        // put的返回值是被替代的V,如果原来没有就是null

        System.out.println(map.put("1001", "张三"));

        System.out.println(map.put("1001", "张三丰"));

        map.put("1002", "李四");

        map.put("1003", "王五");

        System.out.println(map);

        // remove返回被删除的V,Key不存在则返回null

        System.out.println(map.remove("1005"));

        System.out.println(map.remove("1003"));

        System.out.println(map);

        // get返回key对应的value, 无则null

        System.out.println(map.get("1002"));

        System.out.println(map.get("1003"));

        // containsKey 包含key返回true

        System.out.println(map.containsKey("1002"));

        System.out.println(map.containsKey("1003"));

        System.out.println("遍历方式1:将所有key取出来存到set集合");

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

        for (String key : keys) {

            System.out.println("K:"+key+" V:"+map.get(key));

        }

        System.out.println("遍历方式2:将所有Entry<K,V>对象取出来存到set集合");

        for (Map.Entry<String, String> entry : map.entrySet()) {

            System.out.println(entry.getKey() + "=" + entry.getValue());

        }

    }

}

运行结果

Copy

null

张三

{1003=王五, 1002=李四, 1001=张三丰}

null

王五

{1002=李四, 1001=张三丰}

李四

null

true

false

遍历方式1:将所有key取出来存到set集合

K:1002 V:李四

K:1001 V:张三丰

遍历方式2:将所有Entry<K,V>对象取出来存到set集合

1002=李四

1001=张三丰

HashMap(重点)#

基于哈希表的实现的Map接口。 此实现提供了所有可选的Map操作,并允许null值和null键。

底层原理#

底层是哈希表,查询速度超级快!

JDK8之前:数组+链表

JDK8之后:数组+链表 / 数组+红黑树

HashMap面试需要更深的理解,深入底层!

目前停留于了解原理,掌握运用。

测试代码#

存储自定义对象#

首先创建一个学生类

Copy

package map;

public class Student{

    private int id;

    private String name;

    private int age;

    public Student() {

    }

    public Student(int id, String name, int age) {

        this.id = id;

        this.name = name;

        this.age = age;

    }

    public int getId() {

        return id;

    }

    public void setId(int id) {

        this.id = id;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    @Override

    public String toString() {

        return "Student{" +

                "id=" + id +

                ", name='" + name + '\'' +

                ", age=" + age +

                '}';

    }

}

测试类

Copy

package map;

import java.util.HashMap;

/**

* 若以自定义类作键存放,必须说明键重复的规则

* 实体类重写hashCode方法和equals方法

* 比如学生类中,同名不一定是同一个对象,同学号说明是同一个学生

*/

public class TestHashMap {

    public static void main(String[] args) {

        Student student1 = new Student(1001, "张三", 18);

        Student student2 = new Student(1002, "李四", 17);

        Student student3 = new Student(1001, "张三", 19);

        Student student4 = new Student(1004, "李四", 20);

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

        map.put(student1, "第一名");

        map.put(student2, "第二名");

        map.put(student3, "第三名");

        map.put(student4, "第四名");

        System.out.println(map);

    }

}

运行结果

Copy

{Student{id=1004, name='李四', age=20}=第四名, Student{id=1002, name='李四', age=17}=第二名, Student{id=1001, name='张三', age=18}=第一名, Student{id=1001, name='张三', age=19}=第三名}

此时观察student1 和 student3 的学号相同,对象重复,为什么还会添加到map中呢?

image-20200709162818912

有提示要重写Student类的hashCode方法和equals方法。

修改后的Student.java

Copy

package map;

import java.util.Objects;

public class Student{

    private int id;

    private String name;

    private int age;

    public Student() {

    }

    public Student(int id, String name, int age) {

        this.id = id;

        this.name = name;

        this.age = age;

    }

    // ...get/set省略

    @Override

    public boolean equals(Object o) {

        if (this == o) return true;

        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        return id == student.id;

    }

    @Override

    public int hashCode() {

        return Objects.hash(id);

    }

}

再次运行测试类的代码得到的结果

Copy

{Student{id=1001, name='张三', age=18}=第三名, Student{id=1002, name='李四', age=17}=第二名, Student{id=1004, name='李四', age=20}=第四名}

分析:第一次添加1001学生时,value是第一名,再次遇到1001学生,覆盖了原来的value

深入理解HashMap#

更加详细的学习会写到另一篇

LinkedHashMap#

哈希表和链表实现的Map接口,具有可预测的迭代次序。有序集合!

Copy

public class LinkedHashMap<K,V>extends HashMap<K,V>implements Map<K,V>{}

底层原理#

哈希表 -> 继承自HashMap

链表 -> 记录元素顺序

测试代码#

Copy

import java.util.*;

public class TestLinkedHashMap {

    public static void main(String[] args) {

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

        map.put("1001", "张三");

        map.put("1002", "李四");

        map.put("1003", "王五");

        map.put("1004", "赵六");

        System.out.println("map的数据无序,取出的结果与存入顺序不一致:");

        System.out.println(map);

        System.out.println();

        LinkedHashMap<String, String> link = new LinkedHashMap<>();

        link.put("1001", "张三");

        link.put("1002", "李四");

        link.put("1003", "王五");

        link.put("1004", "赵六");

        System.out.println("在map外层加一层链表记录元素存入的顺序:");

        System.out.println(link);

    }

}

运行结果

Copy

map的数据无序,取出的结果与存入顺序不一致:

{1004=赵六, 1003=王五, 1002=李四, 1001=张三}

在map外层加一层链表记录元素存入的顺序:

{1001=张三, 1002=李四, 1003=王五, 1004=赵六}

Hashtable#

Hashtable 和 Vector 一样都是上古级别的集合,在jdk1.2版本之后被更先进的HashMap及ArrayList集合取代。

值得注意的是,Hashtable的子类Properties目前依旧活跃着,它是唯一和IO流结合的集合。

Hashtable 与 HashMap 的区别#

底层都是哈希表,元老级更注重线程安全。

Hashtable

线程安全—>单线程集合—>速度慢

不能存储 null ,K,V都不可以

HashMap

线程不安全—>多线程集合—>速度快

可以存储 null

K 只能存储一个 null ,保证K的唯一性

V 可以存储任意个 null

测试代码#

Copy

import java.util.HashMap;

import java.util.Hashtable;

public class TestHashtable {

    public static void main(String[] args) {

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

        map.put(null, null);

        map.put("A", null);

        map.put(null, "B");

        System.out.println(map);

        Hashtable<String, String> table = new Hashtable<>();

        //table.put(null, null);//NullPointerException

        //table.put("A", null);//NullPointerException

        //table.put(null, "B");//NullPointerException

        System.out.println(table);

    }

}

运行结果

Copy

{null=B, A=null}

{}

HashMap中以null作为键时,再次添加null的键会覆盖之前的value值。

Hashtable只要涉及到null,就会报空指针异常。

集合练习#

判断字符串中某个字符出现的次数

Copy

import java.util.HashMap;

import java.util.LinkedHashMap;

import java.util.Map;

import java.util.Scanner;

/**

* 判断字符串中某个字符出现的次数

*/

public class MapPractice {

    public static void main(String[] args) {

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

//        Scanner sc = new Scanner(System.in);

//        String str = sc.nextLine();

        String str = "aaabbcdaabb";

        char[] chars = str.toCharArray();

        for (char ch : chars) {

            if (map.get(ch) == null) {

                map.put(ch, 1);

            } else {

                int count = map.get(ch);

                map.put(ch, ++count);

            }

        }

        // 使用linkedHashMap保证从前往后统计的顺序

        System.out.println(map);

    }

}

斗地主发牌

Copy

import java.util.*;

/**

* 扑克发牌并按大小排序

*/

public class PracticePoker {

    public static void main(String[] args) {

        // 存放索引的map,一个索引对应一张牌,按照一定规则定制

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

        // 存放牌色♠♥♦♣、数字的列表

        List<String> colors = new ArrayList<>();

        List<String> numbers = new ArrayList<>();

        Collections.addAll(colors, "♠", "♥", "♣", "♦");

        Collections.addAll(numbers, "2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");

        // 建立索引与牌的映射 0-"大王" 1-"小王" 2-"2♠" 3-"2♥" ...

        int idx = 0;

        map.put(idx++, "大王");

        map.put(idx++, "小王");

        for (String number : numbers) {

            for (String color : colors) {

                map.put(idx++, number + color);

                //System.out.print(number + color + " ");

            }

        }

        // 发牌发的是索引,便于比大小 , 因为set是无序的,不能洗牌

        Set<Integer> idxSet = map.keySet();

        ArrayList<Integer> idxList = new ArrayList<>(idxSet);

        // System.out.println("洗牌前" + idxList);

        Collections.shuffle(idxList);

        // System.out.println("洗牌后" + idxList);

        // 玩家的牌用列表接收,便于排序

        ArrayList<Integer> playA = new ArrayList<>();

        ArrayList<Integer> playB = new ArrayList<>();

        ArrayList<Integer> playC = new ArrayList<>();

        ArrayList<Integer> special = new ArrayList<>();

        // 发牌

        special.add(idxList.get(0));

        special.add(idxList.get(1));

        special.add(idxList.get(2));

        for (int i = 3; i < idxList.size(); i++) {

            if (i % 3 == 0) {

                playA.add(idxList.get(i));

            } else if (i % 3 == 1) {

                playB.add(idxList.get(i));

            } else if (i % 3 == 2) {

                playC.add(idxList.get(i));

            }

        }

        // 排序

        Collections.sort(playA);

        Collections.sort(playB);

        Collections.sort(playC);

        Collections.sort(special);

        // 看牌

        lookPoker("刘德华", playA, map);

        lookPoker("周润发", playB, map);

        lookPoker("周星驰", playC, map);

        lookPoker("底牌", special, map);

    }

    public static void lookPoker(String name, List<Integer> idxList, Map<Integer, String> poker) {

        System.out.print(name + "的牌为:");

        for (Integer idx : idxList) {

            System.out.print(poker.get(idx) + " ");

        }

        System.out.println();

    }

}

亚马逊测评 www.yisuping.com

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