【JAVA】HansMap

哈希表(散列表)
哈希表存储的键值对(key:value),针对任意的key通过哈希算法
转换为固定的位置(数组)
key-位置关系并不是一对一,多对一的关系,所以会引发哈希冲突
解决哈希冲突:
数组+链表 链地址法(第一种方式)

Map接口中为什么定义equals和hashCode方法在Map中呢?
自定义类型

resize()的条件:
1)table == null || table.length == 0
2) size > threshold

为什么不同的对象hashCode相同?
hashCode实际指的某一个对象的内存地址,而返回的一个int类型,它是一个有限的集合,
这就会导致哈希值和对象并不是一一对应的关系,所以不同对象来说hashCode有可能会相等


class MyHashMap<K,V>{

    private Node<K,V>[] table;
    private int size;

    class Node<K,V>{
        protected int hash;
        protected K key;
        protected V value;
        protected Node<K, V> next;

        public Node(int hash, K key, V value) {
            this.hash = hash;
            this.key = key;
            this.value = value;
        }
    }

    class iterator{
        public boolean hasNext(Node<K,V> table){
            Node<K,V> currentNode = table.next;
            return currentNode != null;
        }

        public Node<K,V> next(Node<K,V> table){
            Node<K,V> currentNode = null;
            if(hasNext(table)){

            }
            return null;
        }

        public Node<K,V> remove(Node<K,V> table){
            return null;
        }
    }
    public MyHashMap(int capacity){
        table = new Node[capacity];
    }

    public int hash(K key){
        int h;
        return (h = key.hashCode()) ^ (h >>> 16);
    }

//put 方法的思想:对key做null检查。如果key是null,会被存储到table[0],因为null的hash值总是0。
key的hashcode()方法会被调用,然后计算hash值。hash值用来找到存储Entry对象的数组的索引
indexFor(hash,table.length)用来计算在table数组中存储Entry对象的精确的索引。
如果两个key有相同的hash值(也叫冲突),他们会以链表的形式来存储。所以,这里我们就迭代链表。
· 如果在刚才计算出来的索引位置没有元素,直接把Entry对象放在那个索引上。
· 如果索引上有元素,然后会进行迭代,一直到Entry->next是null。当前的Entry对象变成链表的下一个节点。
· 如果我们再次放入同样的key,在迭代的过程中,会调用equals()方法来检查key的相等性(key.equals(k)),如果这个方法返回true,它就会用当前Entry的value来替换之前的value。
    public void put(K key, V value){
        //通过key得到索引位置
        int hash = hash(key);
        int index = table.length-1 & hash;

        //判断该index位置是否存在节点
        Node<K, V> firstNode = table[index];
        if(firstNode == null){
            table[index] = new Node(hash, key, value);
            size++;
        }else{
            //查找当前链表中key是否已经存在
            if(firstNode.key.equals(key)){
                firstNode.value = value;
            }else{
                Node<K, V> currentNode = firstNode;
                while(currentNode.next != null && !currentNode.key.equals(key)){
                    currentNode = currentNode.next;
                }
                if(currentNode.next == null){
                    if(currentNode.key.equals(key)){
                        currentNode.value = value;
                    }else{
                        currentNode.next = new Node(hash, key, value);
                        size++;
                    }
                }else{
                    currentNode.value = value;
                }
            }
        }
    }

    public V get(K key){
        //获取对应key所在数组的index
        int hash = hash(key);
        int index = table.length-1 & hash;
        Node<K, V> firstNode = table[index];
        if(firstNode == null) {
            return null;
        }
        if(hash == firstNode.hash && key == firstNode.key){
            return firstNode.value;
        }else{
            //遍历链表
            Node<K,V> currentNode = firstNode.next;
            while(currentNode != null){
                if(currentNode.hash == hash && currentNode.key == key){
                    return currentNode.value;
                }
                currentNode = currentNode.next;
            }
        }
        return null;
    }

    public boolean remove(K key){
        int hash = hash(key);
        int index = hash & table.length-1;
        Node<K,V> firstNode = table[index];
        if(firstNode == null){
            return false;
        }else{
            //1 2 3 4
            while(firstNode.next != null && firstNode.next.next != null){
                if(firstNode.key.equals(key) && firstNode.hash == hash){
                    firstNode.next = firstNode.next.next;
                    return true;
                } else {
                    firstNode = firstNode.next;
                }
            }
        }
        return false;
    }

    public void reSize(Node<K,V>[] table){
        Node<K,V>[] newTable = Arrays.copyOf(table,table.length*2);
        for(int i=0;i<table.length;){
            if(table[i] != null){
                Node<K,V> firstNode = table[i];
                newTable[i] = firstNode;
                while (firstNode.next != null){
                    newTable[i].next = firstNode.next;
                    firstNode = firstNode.next;
                }
            }else {
                i++;
            }
        }
    }
}

1、hashMap迭代有几种方式?写代码描述
7种 4+3(foreach 未给出)
迭代器底层运用foreach实现

    public static void main(String[] args) {
       //HashMap迭代的方式
        HashMap<String, String> map = new HashMap<>();
        map.put("name", "zhangsna");
        map.put("age", "18");
        map.put("gender", "male");
        map.put("address", "shannxi");

        //使用EntryIterator
        // map.Entry
        Iterator itr1 = map.entrySet().iterator();
        while(itr1.hasNext()){
            Map.Entry<String, String> entry = (Map.Entry<String, String>)itr1.next();
            System.out.println("key: " +entry.getKey() + " value: "+entry.getValue());
        }

        //使用keyIterator
        Iterator itr2 = map.keySet().iterator();
        while(itr2.hasNext()){
            String key = (String)itr2.next();
            System.out.println("key: "+key+ " value: "+map.get(key));
        }

        //使用valueIterator
        Iterator itr3 = map.values().iterator();
        while(itr3.hasNext()){
            String value = (String)itr3.next();
            System.out.println("value:"+value);
        }

        //for each利用EntrySet遍历
        for(Map.Entry<String, String> entry:map.entrySet()){
            System.out.println("key: "+entry.getKey()+" value: "+entry.getValue());
        }

        //jdk1.8 forEach进行遍历
        map.forEach((k,v)-> System.out.println("key: "+k+" value:"+v));
    }

2、HashTable 与 HashMap的区别与联系?
1、实现类、实现接口
HashMap继承自AbstractMap,HashTable继承自Dictionnary
实现接口都是Map、Cloneable、Serializable
2、默认容量
HashMap 16 HashTable 11
3、构造函数
HashMap在第一次put的时候初始化table
HashTale构造函数中初始化table
4、put方法
在这里插入图片描述

3、了解LinkedHashMap和TreeMap
LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,
先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,
也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。
TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。

public static void main(String[] args) {
         //LinkedHash和TreeMap
        //HashMap是无序的
        Map<String, String> map = new HashMap<>();
        map.put("aadjhd", "123");
        map.put("fdsaa", "456");
        map.put("fafafa", "789");
        map.put("fsafa", "1011");
        map.put("agwsggs" "1213");
        map.put("ffdafa", "1415");

        Iterator itr1 = map.entrySet().iterator();
        while(itr1.hasNext()){
            Map.Entry<String, String> entry = (Map.Entry<String, String>)itr1.next();
            System.out.println(entry.getValue());
        }
        System.out.println("======================");

        //LinkedHashMap是有序的,按照插入顺序去迭代的
        LinkedHashMap<String, String> map1 = new LinkedHashMap<>();
        map1.put("aadjhd", "123");
        map1.put("fdsaa", "456");/        map1.put("fafafa", "789");
        map1.put("fsafa", "1011");
        map1.put("agwsggs", "1213");
        map1.put("ffdafa", "1415");

        Iterator itr2 = map1.entrySet().iterator();
        while(itr2.hasNext()){
            Map.Entry<String, String> entry = (Map.Entry<String, String>)itr2.next();
            System.out.println(entry.getValue());
        }
        System.out.println("====================");

        //LinkedHashMap是有序的,按照自然顺序去迭代的
        TreeMap<String,String> map2 = new TreeMap<>();
        map2.put("aadjhd", "123");
        map2.put("fdsaa", "456");
        map2.put("fafafa", "789");
        map2.put("fsafa", "1011");
        map2.put("agwsggs", "1213");
        map2.put("ffdafa", "1415");

        Iterator itr3 = map2.entrySet().iterator();
        while(itr3.hasNext()){
            Map.Entry<String, String> entry = (Map.Entry<String, String>)itr3.next();
            System.out.println(entry.getValue());
        }
        System.out.println("====================");
    }

4、自定义类型作为map的key时,怎么使用?

class PhoneNumber{
    private String prefix;
    private String number;

    public PhoneNumber(String prefix, String number){
        this.prefix = prefix;
        this.number = number;
    }
//使用自定义类型去作为HashMap的key(重写equals方法)
    public boolean equals(Object obj){
        if(this == obj){
            return true;
        }
        PhoneNumber phe = (PhoneNumber)obj;
        return phe.prefix == this.prefix && ((PhoneNumber) obj).number == this.number;
    }

    public int hashCode(){
        int prefixAddress = this.prefix.hashCode();
        int numberAddress = this.number.hashCode();
        return prefixAddress+numberAddress;
    }
}

public class GY20191113 {
    public static void main(String[] args) {
        //使用自定义类型去作为HashMap的key
        HashMap<PhoneNumber, String> map = new HashMap<PhoneNumber, String>();
        map.put(new PhoneNumber("029", "85658340"), "lvting");
        map.put(new PhoneNumber("010", "83145678"), "yongxing");
        map.put(new PhoneNumber("020", "62173212"), "mengqi");
        map.put(new PhoneNumber("020", "62173212"), "huihui");
        map.put(new PhoneNumber("030", "28734878"), "mengfan");

        System.out.println(map.toString());
        System.out.println(map.get(new PhoneNumber("029", "85658340")));
        System.out.println(map.get(new PhoneNumber("020", "62173212")));
    }
}

5、解决哈希冲突的方式
(1)开放地扯法
(2)再哈希法
(3)链地址法
(4)建立一个公共溢出区

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基于C++&OPENCV 的全景图像拼接 C++是一种广泛使用的编程语言,它是由Bjarne Stroustrup于1979年在新泽西州美利山贝尔实验室开始设计开发的。C++是C语言的扩展,旨在提供更强大的编程能力,包括面向对象编程和泛型编程的支持。C++支持数据封装、继承和多态等面向对象编程的特性和泛型编程的模板,以及丰富的标准库,提供了大量的数据结构和算法,极大地提高了开发效率。12 C++是一种静态类型的、编译式的、通用的、大小写敏感的编程语言,它综合了高级语言和低级语言的特点。C++的语法与C语言非常相似,但增加了许多面向对象编程的特性,如类、对象、封装、继承和多态等。这使得C++既保持了C语言的低级特性,如直接访问硬件的能力,又提供了高级语言的特性,如数据封装和代码重用。13 C++的应用领域非常广泛,包括但不限于教育、系统开发、游戏开发、嵌入式系统、工业和商业应用、科研和高性能计算等领域。在教育领域,C++因其结构化和面向对象的特性,常被选为计算机科学和工程专业的入门编程语言。在系统开发领域,C++因其高效性和灵活性,经常被作为开发语言。游戏开发领域中,C++由于其高效性和广泛应用,在开发高性能游戏和游戏引擎中扮演着重要角色。在嵌入式系统领域,C++的高效和灵活性使其成为理想选择。此外,C++还广泛应用于桌面应用、Web浏览器、操作系统、编译器、媒体应用程序、数据库引擎、医疗工程和机器人等领域。16 学习C++的关键是理解其核心概念和编程风格,而不是过于深入技术细节。C++支持多种编程风格,每种风格都能有效地保证运行时间效率和空间效率。因此,无论是初学者还是经验丰富的程序员,都可以通过C++来设计和实现新系统或维护旧系统。3

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值