java基础之集合——Map集合

2 篇文章 0 订阅

Map集合

键映射到值的对象,Map集合可以多个值,但键必须唯一!

Map和Collection集合的区别:

Collection:只能存储一种类型 Collection

Map集合:可以两种类型的,键的类型,值的类型 Map<K,V>

遍历方式不同

Collection:5种方式

详见集合的遍历

Map:两种方式:

方式1:获取所有的K的集合(键的集合)
通过键获取值

方式2: 获取所有的键值对对象Map.Entry<K,V> (“结婚证”)
通过键值对对象获取所有的键(“结婚证男方”)
通过键值对对象获取所有的值(“结婚证女方”)

有内在联系:

TreeSet集合:Collection---->间接的使用到了TreeMap集合的put方法

HashSet阶:Collection---->间接使用到了HashMap的put方法

Map集合的功能:
V put(K key,V value);//添加键值对元素

/*注意事项:
如果key是第一次添加,那么返回的结果为null
如果key是否重复添加,第二次添加,返回的上一次添加的键对应的值
*/
V remove(Object key);//删除指定的键,返回被删除键对应的值
void clear()

boolean containsKey(Object key);//是否包含指定的键 (使用居多)
boolean containsValue(Object value);//是否包含指定的值

高级功能(遍历):

方法1:

Set<K> keySet();//获取当前Map集合中的所有的键的集合  (将所有的丈夫集中起来,找对应的妻子)
V get(Object key);//通过键获取值

方式2:

//获取所有的结婚证  (键值对对象)
Set<Map.Entry<K,V>> entrySet();

//通过键值对象 获取键 /获取值(通过结婚证找男方/女方)
K getKey();
V getValue();

eg.

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapTest {

    public static void main(String[] args) {

        //创建Map集合对象
        Map<String, String> map = new HashMap<>();

        //添加元素
        map.put("令狐冲", "东方不败");
        map.put("杨过", "小龙女");
        map.put("陈玄风", "梅超风");
        map.put("郭靖", "黄蓉");

        //方法1:
        // Set<K> keySet()  :获取当前Map集合中的所有的键的集合
        Set<String> keySet = map.keySet();
        //增强for遍历
        for (String key : keySet) {
            String value = map.get(key);
            System.out.println(key + "-" + value);
        }

        System.out.println("————————————————");

        //方式2:
        //Set<Map.Entry<K,V>> entrySet()
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        //增强for:遍历键值对对象获取到
        for (Map.Entry<String, String> entry : entrySet) {
            System.out.println(entry.getKey()+"-"+entry.getValue());
        }

    }

}

运行结果:

令狐冲-东方不败
陈玄风-梅超风
杨过-小龙女
郭靖-黄蓉
————————————————
令狐冲-东方不败
陈玄风-梅超风
杨过-小龙女
郭靖-黄蓉

Properties类

目前只需要掌握Properties属性类对象的相关方法即可。

Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型。

Properties被称为属性类对象。

Properties是线程安全的。

public class PropertiesTest01 {
    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);

    }
}

Collections工具类

针对集合操作工具类

提供静态功能

public static <T extends Comparable<? super T>> void sort(List<T> list);//按照自然升序排序(针对List集合排序)
public static <T> void sort(List<T> list,Comparator<? super T> c);//按照比较器排序针对List集合
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>;//获取当前自然顺序中List的最大值
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T>;//最小值
public static void reverse(List<?> list);//对List集合顺序反转
public static void shuffle(List<?> list);//随机置换

eg.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Test01 {

    public static void main(String[] args) {

        //创建List集合
        List<Integer> list = new ArrayList<>() ;

        //添加元素
        list.add(10) ;
        list.add(50) ;
        list.add(15) ;
        list.add(25) ;
        list.add(5) ;
        list.add(12) ;

        System.out.println(list);//[10, 50, 15, 25, 5, 12]

        System.out.println("---------------------------------");

        //排序
        Collections.sort(list);
        System.out.println(list);//[5, 10, 12, 15, 25, 50]
        
        System.out.println("----------------------------------");

        //最大值
        Integer max = Collections.max(list);
        System.out.println(max);//50

        System.out.println("----------------------------------");

        //最小值
        System.out.println(Collections.min(list));//5

        System.out.println("-----------------------------------");

        //反转
        Collections.reverse(list);
        System.out.println(list);//[50, 25, 15, 12, 10, 5]

        System.out.println("------------------------------------");

        //乱序
        Collections.shuffle(list);
        System.out.println(list);//[10, 25, 15, 12, 50, 5]

    }

}

集合排序

public static <T extends Comparable<? super T>> void sort(List<T> list):按照自然升序排序(针对List集合排序)
public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合

List<Student>:针对List进行自然升序排序,

eg.

主要条件:按照学生的年龄从小到大排

次要条件:年龄相同,比较姓名内容是否相同!

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

        //创建List集合对象
        List<Student> list = new ArrayList<>() ;
        
        //添加
        list.add(new Student("gaogaoyuan",42)) ;
        list.add(new Student("gaogaoyuan",40)) ;
        list.add(new Student("liushishi",42)) ;
        list.add(new Student("wanglihong",45)) ;
        list.add(new Student("wenzhang",38)) ;
        list.add(new Student("huazi",35)) ;
        list.add(new Student("huazi",32)) ;
        list.add(new Student("zhangjunjie",20)) ;

        //排序
        //Collections.sort(list); //自然升序排序:针对集合当前存储的类型必须实现Comparable

        //使用比较器排序:针对List集合
        //public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int num  = s1.getAge() - s2.getAge() ;
                int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ;
                return num2;
            }
        });

        for(Student s:list){
            System.out.println(s.getName()+"---"+s.getAge());
        }

    }
}
import java.util.Comparator;

//实现接口
//public class Student implements Comparable<Student> {
public class Student {
    private String name ;
    private int age ;

    public Student() {
    }

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

    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{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

   /* @Override
    public int compareTo(Student s) {

        //主要条件:按照学生的年龄从小到大排序
        int num = this.age - s.age ;
        //如果年龄相同,姓名比(字典顺序)
        int num2 = (num==0)?(this.name.compareTo(s.name)):num ;
        return num2;
    }*/
    
}

集合总结

ArrayList集合:

1、默认初始化容量10(底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量10。)

2、集合底层是一个Object[]数组。

3、构造方法:
new ArrayList();
new ArrayList(20);

4、ArrayList集合的扩容:
增长到原容量的1.5倍。
ArrayList集合底层是数组,怎么优化?
尽可能少的扩容。因为数组扩容效率比较低,建议在使用ArrayList集合
的时候预估计元素的个数,给定一个初始化容量。

5、数组优点:
检索效率比较高。(每个元素占用空间大小相同,内存地址是连续的,知道首元素内存地址,
然后知道下标,通过数学表达式计算出元素的内存地址,所以检索效率最高。)

6、数组缺点:
随机增删元素效率比较低。
另外数组无法存储大数据量。(很难找到一块非常巨大的连续的内存空间。)

7、向数组末尾添加元素,效率很高,不受影响。

8、面试官经常问的一个问题?
这么多的集合中,你用哪个集合最多?
答:ArrayList集合。
因为往数组末尾添加元素,效率不受影响。
另外,我们检索/查找某个元素的操作比较多。

9、ArrayList集合是非线程安全的。(不是线程安全的集合。)

泛型

1、JDK5.0之后推出的新特性:泛型

2、泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考的。(运行阶段泛型没用!)

3、使用了泛型好处是什么?
第一:集合中存储的元素类型统一了。
第二:从集合中取出的元素类型是泛型指定的类型,不需要进行大量的“向下转型”!

4、泛型的缺点是什么?
导致集合中存储的元素缺乏多样性!
大多数业务中,集合中元素的类型还是统一的。所以这种泛型特性被大家所认可。

HashMap集合:

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

2、哈希表是一个怎样的数据结构呢?

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

3、HashMap集合底层的源代码:

 public class HashMap{
     // HashMap底层实际上就是一个数组。(一维数组)
     Node<K,V>[] table;
     // 静态的内部类HashMap.Node
     static class Node<K,V> {
         final int hash; // 哈希值(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数/算法,可以转换存储成数组的下标。)
         final K key; // 存储到Map集合中的那个key
         V value; // 存储到Map集合中的那个value
         Node<K,V> next; // 下一个节点的内存地址。
     }
 }

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

4、最主要掌握的是:

​ map.put(k,v)
​ v = map.get(k)
​ 以上这两个方法的实现原理,是必须掌握的。

​ map.put(k,v)的实现原理:
​ 第一步:先将k,v封装到Node对象当中。
​ 第二步:底层会调用k的hashCode0方法得出hash值,然后通过哈希函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上了。如果说下标对应的位置上有链表,此时会拿着k和链表上每一个节点中的k进行equals ,如果所有的equals方法返回都是false ,那么这个新节点将会被添加到链表的末尾。如果其中有一个equals返回了true,那么这个节点的value将会被覆盖。

​ v = map.get(k)的实现原理:
​ 先调用k的hashCode0方法得出哈希值,通过哈希算法转换成数组下标,通过数组下标快速定位到某个位置上,如果这个位置上什么也没有,返回null.如果这个位置上有单向链表,那么会拿着参数k和单向链表上的每个节点中的k进行equals ,如果所有equals方法返回false ,那么get方法返回null ,只要其中有一个节点的k和参数k equals的时候返回true ,那么此时这个节点的value就是我们要找的value , get方法最终返回这个要找的value。

5、HashMap集合的key部分特点:

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

6、哈希表HashMap使用不当时无法发挥性能!

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

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

​ 假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题?
​ 不行,因为这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。也是散列分布不均匀。

​ 散列分布均匀需要你重写hashCode()方法时有一定的技巧。

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

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

​ 这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始
​ 重点,记住:HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,
​ 这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。

java.util.Map接口中常用的方法

​ 1、Map和Collection没有继承关系。

​ 2、Map集合以key和value的方式存储数据:键值对

​ key和value都是引用数据类型。
​ key和value都是存储对象的内存地址。
​ key起到主导的地位,value是key的一个附属品。

​ 3、Map接口中常用方法:

​ V put(K key, V value) 向Map集合中添加键值对
​ V get(Object key) 通过key获取value
​ void clear() 清空Map集合
​ boolean containsKey(Object key) 判断Map中是否包含某个key
​ boolean containsValue(Object value) 判断Map中是否包含某个value
​ boolean isEmpty() 判断Map集合中元素个数是否为0
​ V remove(Object key) 通过key删除键值对
​ int size() 获取Map集合中键值对的个数。
​ Collection values() 获取Map集合中所有的value,返回一个Collection

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

HashMap集合补充

1、向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法!

equals方法有可能调用,也有可能不调用。

​ 拿put(k,v)举例,什么时候equals不会调用?
​ k.hashCode()方法返回哈希值,
​ 哈希值经过哈希算法转换成数组下标。
​ 数组下标位置上如果是null,equals不需要执行。

​ 拿get(k)举例,什么时候equals不会调用?

​ k.hashCode()方法返回哈希值,
​ 哈希值经过哈希算法转换成数组下标。
​ 数组下标位置上如果是null,equals不需要执行。

2、注意:如果一个类的equals方法重写了,那么hashCode()方法必须重写。

并且equals方法返回如果是true,hashCode()方法返回的值必须一样。
equals方法返回true表示两个对象相同,在同一个单向链表上比较。
那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。
所以hashCode()方法的返回值也应该相同。

3、hashCode()方法和equals()方法不用研究了,直接使用IDEA工具生成,但是这两个方法需要同时生成。

4、终极结论:
放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。

5、对于哈希表数据结构来说:

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

扑克牌

eg.1

import java.util.ArrayList;
import java.util.Collections;

public class Test06 {

    public static void main(String[] args) {

        ArrayList<String> arrayList = new ArrayList<>();

        String[] colors = {"♥", "♦", "♠", "♣"};

        String[] numbers = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};

        for (String number : numbers) {
            for (String color : colors) {
                arrayList.add(color + number);

            }
        }
        arrayList.add("大王");
        arrayList.add("小王");

        //洗牌
        Collections.shuffle(arrayList);

        ArrayList<String> player1 = new ArrayList<>();
        ArrayList<String> player2 = new ArrayList<>();
        ArrayList<String> player3 = new ArrayList<>();
        ArrayList<String> dipai = new ArrayList<>();

        for (int i = 0; i < arrayList.size(); i++) {
            if (i <arrayList.size()-3) {
                if ((i) % 3 == 0) {
                    player1.add(arrayList.get(i));
                } else if ((i) % 3 == 1) {
                    player2.add(arrayList.get(i));
                } else{
                    player3.add(arrayList.get(i));
                }
            } else {
                dipai.add(arrayList.get(i));
            }
        }

        System.out.print("player1:");
        Sort(player1);

        System.out.print("player2:");
        Sort(player2);

        System.out.print("player3:");
        Sort(player3);

        System.out.print("dipai:");
        Sort(dipai);

    }

    public static void Sort(ArrayList<String> arrayList){

        for (String s:arrayList) {
            System.out.print(s+"\t");
        }
        System.out.println();
    }

}

运行结果:

player1:♥6 ♣10 ♣J ♦5 ♥2 ♠J ♠3 ♣7 ♣6 ♠8 ♠Q ♥9 ♣4 ♠5 ♦K ♥7 ♦7
player2:♥5 ♣8 ♣3 ♦4 ♦3 ♥10 ♦J ♥3 ♣A ♠K ♥4 ♦6 大王 ♠7 小王 ♦2 ♠A
player3:♦A ♣2 ♣Q ♣9 ♥A ♥8 ♣K ♠6 ♦Q ♦8 ♠2 ♠9 ♠10 ♦10 ♣5 ♥Q ♦9
dipai:♥K ♠4 ♥J

eg.2

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;

public class Test06_2 {

    public static void main(String[] args) {

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

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

        String[] colors = {"♥", "♦", "♠", "♣"};

        String[] numbers = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};

        int index = 0;

        for (String number:numbers) {
            for (String color:colors) {
                map.put(index,color+number);
                list.add(index);
                index++;
            }
        }


        list.add(index);
        map.put(index++,"大王");
        list.add(index);
        map.put(index,"小王");

        //洗牌
        Collections.shuffle(list);

        TreeSet<Integer> player1= new TreeSet<>();
        TreeSet<Integer> player2= new TreeSet<>();
        TreeSet<Integer> player3= new TreeSet<>();
        TreeSet<Integer> dipai= new TreeSet<>();

        for (int i = 0; i < list.size(); i++) {
            if (i <list.size()-3) {
                if ((i) % 3 == 0) {
                    player1.add(list .get(i));
                } else if ((i) % 3 == 1) {
                    player2.add(list .get(i));
                } else{
                    player3.add(list .get(i));
                }
            } else {
                dipai.add(list .get(i));
            }
        }

        Sort("palyer1",player1,map);

        Sort("palyer2",player2,map);

        Sort("palyer3",player3,map);

        Sort("底牌",dipai,map);

    }

    public static void Sort(String name,TreeSet<Integer> set,HashMap<Integer,String> map ){

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

        for (Integer i:set) {
            System.out.print(map.get(i)+"\t");
        }

        System.out.println();
    }

}

运行结果:

palyer1的牌是:♦2 ♠2 ♦3 ♠3 ♣3 ♦6 ♣6 ♥7 ♦7 ♥8 ♠9 ♦10 ♥Q ♦Q ♣Q ♦K ♠K
palyer2的牌是:♥A ♠A ♥3 ♠4 ♣4 ♦5 ♠5 ♣5 ♣7 ♦9 ♣9 ♠10 ♦J ♠J ♣J ♣K 大王
palyer3的牌是:♦A ♣A ♥2 ♣2 ♥4 ♦4 ♥5 ♥6 ♠7 ♠8 ♥9 ♥10 ♣10 ♥J ♠Q ♥K 小王
底牌的牌是:♠6 ♦8 ♣8

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值