java基础语法(二十五)Map接口(一)

Map简单回顾

  1. Map和Collection没有继承关系。
  2. Map集合以key和value的方式存储数据:键值对
    key和value都是引用数据类型。
    key和value都是存储对象的内存地址。
    key起到主导的地位, value是key的一个附属品。
    下图数map的继承结构图
    map继承结构图

常用方法

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
Set keySet() 获取Map集合中所有的key(所有的键是一个set集合)
V remove(Object key) 通过key删除键值对
int size() 获取Map集合中键值对的个数
Collection values() 获取Map集合中所有的value,返回的是一个Collection
Set<Map.Entry<K,V>>entrySet():
将Map集合转成Set集合
假设现在有一个Map集合,如下所示:
map1集合对象

keyvalue
1zhangsan [注意:Map集合通过entrySet()方法转换成的这个Set集合, Set集合中元素的类型是Map.Entry<K, V>]
2lisi [Map.Entry和String-样,都是一种类型的名字,只不过:Map.Entry是静态内部类,是Map 中的静态内部类]
3wangwu
4zhaoliu

Set set = map1.entrySet();
set集合对象
1=zhangsan
2=lisi
3=wangwu
4=zhaoliu

public class MapTest01 {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(new Integer(1),"zhangsan");
        map.put(2,"lisi");//2在这里自动装箱
        map.put(3,"wangwu");
        map.put(4,"zhaoliu");
        //通过key获取value
        String value = map.get(1);
        System.out.println(value);
        //获取Map集合中键值对的个数
        System.out.println("键值对的个数"+map.size());
        //通过key删除key-value
        map.remove(2);
        System.out.println("键值对的个数"+map.size());
        //判断Map中是否包含某个key
        //contains方法底层调用的都是equals进行比对的,所以自定义的类型都是需要重写equals方法
        System.out.println(map.containsKey(4));//true
        //判断Map中是否包含某个value
        System.out.println(map.containsValue("wangwu"));//true
        System.out.println(map.containsValue(new String("wangwu")));//true
        //获取所有的value
        Collection<String> values = map.values();
        for(String s:values){
            System.out.println(s);
        }
        //清空Map集合
        map.clear();
        System.out.println("键值对的个数"+map.size());//0
        //判断Map集合中元素个数是否为0
        System.out.println(map.isEmpty());//true
        //判断是否为空
        System.out.println(map.isEmpty());//true
    }
}

Map集合的遍历

两种方式:
第一种:获取所有的key,通过遍历key来遍历value
第二种:Set<Map.Entry<K,V>>entrySet()

public class MapTest02 {
    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");
        //遍历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()
        //以上这个方法是把Map集合直接全部转换成Set集合
        //Set集合中元素的类型是Map.Entry
        Set<Map.Entry<Integer,String>>set=map.entrySet();
        //遍历Set集合,每一次取出一个Node
        //迭代器
        Iterator<Map.Entry<Integer,String>> it2=set.iterator();
        while (it2.hasNext()){
            Map.Entry<Integer,String> node=it2.next();
            //取出节点调用getKey、getValue方法
            Integer key=node.getKey();
            String value =node.getValue();
            System.out.println(key+"="+value);
        }
        //foreach
        //这种方式效率比较高,因为获取key和value都是直接从node对象中获取的属性值。
        //这种方式比较适合大数据量
        for (Map.Entry<Integer,String>node:set){
            System.out.println(node.getKey()+"="+node.getValue());
        }

    }

}

Hashtable

Hashtable的key可以为null吗?
Hashtable的key和value都是不能为null的。
HashMap集合的key和value都是可以为null的。

Hashtable方法都带有synchronized :线程安全的。
线程安全有其它的方案,这个Hashtable对线程的处理
导致效率较低,使用较少了。
Hashtable和HashMap一样,底层都是哈希表数据結构。
Hashtable的初始化容量是11,默认加载因子是:0.75f
Hashtable的扩容是:原容量*2+1

public class HashtableTest01 {
    public static void main(String[] args) {
        Map map= new Hashtable();
        //map.put(null,"123");//java.lang.NullPointerException
        //map.put(100,null);//java.lang.NullPointerException

    }
}

HashMap

HashMap集合:

  1. HashMap 集合底层是哈希表/散列表的数据結构。
    2.哈希表是一个怎样的数据結构呢?
    哈希表是一个数组和单向链表的结合体。
    数组:在查询方面效率很高,随机增删方面效率很低。
    单向链表:在随机增删方面效率较高,在查询方面效率很低。
    哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。
  2. 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)
以上这两个方法的实现原理,是必须掌握的。
5、HashMap 集合的key部分特点:
无序,不可重复。
为什么无序?因为不一定挂到哪个单向链表上。
不可重复是怎么保证的? equals方法来保HashMap集合的key不可重复。
如果key重复了,value会覆盖。

放在HashMap集合key部分的元素其实就是放到HashSet集合中了。
所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法。
6、哈希表HashMap使用不当时无法发挥性能!
假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了
纯单向链表。这种情况我们成为:散列分布不均匀。
什么是散列分布均匀?
假设有100个元素, 10个单向链表,那么每个单向链表上有10个节点,这是最好的,
是散列分布均匀的。
假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题?
不行,因为这样的话导致底层哈希表就成为一维数组了 ,没有链表的概念了。
也是散列分布不均匀。
7.重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法。
8. HashMap集合的默认初始化容量是16。默认加载因子是0.75
这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。

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

9、向Map集合中存,以及从WMap集合中取,都是先调用key的hashCode方法,然后再调用equals方法!
equals方法有可能调用,也有可能不调用。
拿put(k,v)举例,什么时候equals不会调用?
k.hashCode()方法返回哈希值,
哈希值经过哈希算法转换成数組下标。
数组下标位置上如果是null , equals不需要执行。
拿get(k)举例,什么时候equals不会调用?
k.hashCode()方法返回哈希值,
哈希值经过哈希算法转换成数組下标。
数组下标位置上如果是null , equals不需要执行。

10、注意:如果一个类的equals方法重写了,那么hashCode()方法必须重写。
并且equals方法返回如果是true,hashCode()方法返回的值必须一样。
equals方法返回true表示两个对象相同,在同一个单向链表上比较。
那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。
所以hashCode()方法的返回值也应该相同。
11、 hashCode()方法和equals()方法不用研究了。直接使用IDEA工具生成。但是这两个方法需要同时生成。
12、重要结论:
放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。
Student类:

public class Student {
    private String name;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //equals(如果学生名字一样,表示同一个学生)
    /*public boolean equals(Object obj){
        if(obj==null || !(obj instanceof  Student)) return false;
        if(obj == this) return true;
        Student s =(Student)obj;
        return (this.name.equals(s.name));
      }*/

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(name, student.name);
    }

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

测试:

public class HashMapTeat02 {
    public static void main(String[] args) {
        Student s1 = new Student("zhangsan");
        Student s2 = new Student("zhangsan");
        //重写equals方法之前是false
        //System.out.println(s1.equals(s2));//false

        //重写equals方法之后是true
        System.out.println(s1.equals(s2));//true

        System.out.println("s1的hashCode"+s1.hashCode());//1355531311(重写hashCode()之后 -1432604525)
        System.out.println("s2的hashCode"+s2.hashCode());//488970385(重写hashCode()之后 -1432604525)

        // s1.equals(s2)结果已经是true了,表示s1和s2是一样的,相同的,那么往Hashset集合中放的话,
        //按说只能放进去1个。(HashSet集合特点 :无序不可重复)
        Set<Student> students = new HashSet<>();
        students.add(s1);
        students.add(s2);
        //System.out.println(students.size()); //这个结果按说应该是1.但是结果是2.显然不符合HashSet集合存储特点
        System.out.println(students.size());//重写equals()、hashCode()之后,值是1

    }
}

HashMap集合key部分允许为null

public class HashMapTeat03 {
    public static void main(String[] args) {
        Map map = new HashMap();

        //HashMap集合允许key为null
        map.put(null,null);
        System.out.println(map.size());//1

        // key重复的话value是覆盖!
        map.put(null, 100);
        System.out.println(map.size()); //1

        //通过key获取value
        System.out.println(map.get(null));//100

    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值