Java学习笔记:Map集合

1,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;//下一个结点的内存地址
      }
}

哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表。(数组和链表的结合体)
在这里插入图片描述

2,遍历HashMap

public class test25<o> {

    public static void main(String[] args) {
        HashMap<Integer, String> p = new HashMap();

        p.put(1, "a");
        p.put(2, "b");
        p.put(3, "c");
         
        //第一种
        //通过Key获得Value
        for (int i = 1; i <= p.size(); i++) {
            System.out.println(i + "=" + p.get(i));
        }
        //第二种
         //将Key转为Set, 类型为Integer,再利用迭代器遍历
         //每个key,通过key获得value;
        Set<Integer> keys = p.keySet();
        Iterator<Integer> it = keys.iterator();

        while (it.hasNext()) {
            Integer key = it.next();
            System.out.println(key + "=" + p.get(key));
        }

        //第三种
        //把Map转化成Set,其中类型为Map.Entry <Integer, String>,
        //再利用跌代器去遍历每个结点,通过getValue(),getKey()来获得Value和Key
        Set<Map.Entry <Integer, String>> set = p.entrySet();
        Iterator<Map.Entry<Integer, String>> its = set.iterator();
        while(its.hasNext() ){
            Map.Entry<Integer,String> node = its.next() ;
            String value = node.getValue() ;
            Integer key = node.getKey();
            System.out.println(key + "=" + value);
        }

        //第四种
        //直接将Map全部转化为Set,然后直接将对应结点通过getValue(),getKey()来获得Value和Key
        for(Map.Entry<Integer, String> set2: p.entrySet()){
            String value1 = set2.getValue();
            Integer key1 = set2.getKey() ;

            System.out.println(key1 + "=" + value1);
        }

    }
}


3,map.put(K, V)的实现原理:

1)先将K,V封装到对象当中。
2)底层会调用K的hashCode()方法得出hash值,然后通过哈希函数/哈希算法,将hash值转换成数组下标,下标位置上如果没有如何元素,就把Node添加到这个位置上了。如果说下标对应的位置上有链表,此时会拿着K和链表上每一个结点中的K进行equals,如所有的equals方法返回都是false,那么这个新结点将被添加到链表的末尾,如果其中有一个equals返回了true,那么这个结点的value将会被覆盖。

public class Test25_1 {
    public static void main(String[] args) {
        //测试hashMap集合key部分的元素特点
        //Integer是key,它的hashCode和equals都重写了
        Map<Integer, String> map = new HashMap<Integer,String>();
        map.put(1111, "zhangsan");
        map.put(6666, "sd");
        map.put(8888, "sss");
        map.put(2222, "rrr");
        map.put(2222, "ffff");
          //ffff覆盖了rrr
        for(Map.Entry<Integer, String> s: map.entrySet()){
            System.out.println(s);
        }
    }
}

4,V = map.get(K)实现原理

先调用K的hashCode()方法得出哈希值,通过哈希算法转换成数组下标,通过数组下标快速定位到某个位置上,如果这个位置上什么都没有,返回NULL,如果这个位置上有单向链表,那么会拿着参数K和链表上的每一个K进行equals,如果所有equals返回false,那么get方法返回NULL,只要其中有一个结点的K和参数K equals的时候返回true,那么此时的这个结点的value就是我们要找的value,get方法最终返回这个要找的value。

5,为什么哈希表的随机增删,以及查询效率都很高?

  • 增删是在链表上完成。
  • 查询也不需要都扫描,只需要部分扫描。

重点:通过讲解可以得出HashMap集合的key,会先后调用两个方法,一个方法的hashCode(),一个方法是equals(),那么这两个方法都要重写。

6,哈希表HashMap使用不当无法发挥性能!
假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。这种情况我们成为:散列分布不均匀。
假设有100个元素,10个单链表,那么其中一个单链表上全为这100个元素
散列均匀分布需要你重写hashCode()有一定的技巧。

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

8,
HashMap集合默认容量为16,当存储数到达容量的75%时,数组便会自动扩容。
HashMap集合扩容是:原容量2。
HashTable集合默认容量为11。
HashTable集合扩容是:原容量
2 + 1。

重点:HashMap集合初始化容量必须是2的倍数,这是因为达到散列均匀,为了提高存储效率。

9,重写equals和hashCode方法

public class Test25_1 {
    public static void main(String[] args) {
        Student s1 = new Student("ss");
        Student s2 = new Student("ss");

        System.out.println(s1.equals(s2) );
        Set p = new HashSet();
        p.add(s1);
        p.add(s2);
        System.out.println(p.size() );
    }
}

class Student
{
    public String name;

    public Student(String name){
        this.name = name;
    }
    @Override
    public int hashCode() {
        return Objects.hash(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 ) ;
    }
}

10,JDK8之后,在哈希表单向链表上元素如果超过8个,那么单向链表这种数据结构会变成红黑树这种数据结构,检索效率大大提高。

11,

  • Hashtable 集合中的key和values不能为空
  • HashMap 集合中的key和values可以为空

12,

1)TreeSet集合底层实际上是一个TreeMap。
2)TreeMap集合底层是一个自平衡二叉树。
3)放到TreeSet集合中的元素,等同于放到TreeMap集合的Key部分了。
4)TreeSet集合中的元素无序不可以重复,但可以按照元素大小进行自动排序,称为可排序集合。

public class Test25_1 {
    public static void main(String[] args) {
        TreeSet<String> ts = new TreeSet();
        ts.add("z");//如果是自定义类型,必须制定对象之间的比较规则
        ts.add("a");//否则无法排序
        for (String s : ts) {
            System.out.println(s);
        }
    }
}

13,TreeSet集合(自定义对象,需要制定比较规则)
直接创建一个实现Comparable接口的类

public class Test25_1 {
    public static void main(String[] args) {
       Customer c1 = new Customer(2);//自定义类型
       Customer c2 = new Customer(1);

       TreeSet<Customer> p = new TreeSet();

       p.add(c1);//开始排序
       p.add(c2);//在TreeSet集合中根据Customer类中的compareTo方法排序
      //TreeSet底层比较方式为k.compareTo(m);
       for(Customer a : p){
           System.out.println(a );
       }
    }
}

class Customer implements Comparable<Customer>
{
    public int num;
    public Customer(int num) {
         this.num = num;
    }
    //重写Comparable接口中的compareTo方法
    public int compareTo(Customer c){
        return this.num - c.num;//写排序规则
    }//返回>0,会继续在右子树上找,反之,在左子树上找。

    public String toString(){
        return "Customer = " + num;
    }
}

另一种方法制定比较规则(使用比较器的方式)
另外创建一个实现Comparator接口的比较器

public class Test25_1 {
    public static void main(String[] args) {
       Customer c1 = new Customer(2);
       Customer c2 = new Customer(1);

       CustomerComparator c = new CustomerComparator();
       //传入比较器
       TreeSet<Customer> p = new TreeSet(c);

       p.add(c1);
       p.add(c2);

       for(Customer a : p){
           System.out.println(a );
       }
    }
}

class Customer
{
    public int num;
    public Customer(int num) {
         this.num = num;
    }

    public String toString(){
        return "Customer = " + num;
    }
}

//单独在这里编写一个比较器
//比较器实现java.util.Comparator接口。
// Comparable是java.lang包下的,Comparator是java.util包下的
class CustomerComparator implements Comparator<Customer>{
    public int compare(Customer c1, Customer c2){
             return c1.num  - c2.num ;
    }
}

Comparable接口:当比较规则不会发生改变的时候适合使用
Comparator接口:当比较规则经常改变的时候适合使用,因为只需要实现不同的接口就可以改变不同的规则,符合OCP原则

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值