手工实现HashMap(简单版)- get put remove size方法

本文深入讲解如何手工实现HashMap,包括get()、put()、remove()和size()等核心方法。通过对链表插入的改进,解决了原实现中元素数量受限的问题。文章提供了完整的代码示例,适合初学者学习。

手工实现HashMap ,包括get()   put()   remove()   size()方法,当然,这是线程不安全的,不过可以学习。

本文代码是在https://blog.youkuaiyun.com/weixin_43003240/article/details/87534836 这篇文章上进行改进的,其博客的71行

iterLast = newNode;  这个是有问题的,要对链表进行插入才行,此作者直接进行赋值,如果你用很多个元素进行测试,可以看到,此作者运行出的结果最大只有16个,就是因为没有对链表进行插入。因此,本文对其进行改进如下(也就是三行代码的事情):

//采用头插法进行插入元素
Node2 first = table[newNode.hash];
newNode.next = first;
table[newNode.hash] = newNode;
package com.itisyue.base.map;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

class Node2 {
    int hash;
    Object key;
    Object value;
    Node2 next;
}

public class MyHashMap {
    private Node2[] table;//位桶数组。bucket array
    private Long size=0L;//存放的键值对的个数

    public MyHashMap() {
        table = new Node2[16];//长度一般定义成2的整数幂
    }

    public Object get(Object key) {
        int hash = myHash(key.hashCode(), table.length);
        Object value = null;

        if (table[hash] != null) {
            Node2 temp = table[hash];

            while (temp != null) {
                if (temp.key.equals(key)) {//如果相等,则说明找到了键值对,返回相应的value
                    value = temp.value;
                    break;
                } else {
                    temp = temp.next;
                }
            }
        }
        return value;
    }

    public void put(Object key, Object value) {
        //定义新的节点对象
        Node2 newNode = new Node2();
        newNode.hash = myHash(key.hashCode(), table.length);
        newNode.key = key;
        newNode.value = value;
        newNode.next = null;

        Node2 temp = table[newNode.hash];

        Node2 iterLast = null;//正在遍历的最后一个元素

        boolean keyRepeat = false;

        if (temp == null) {
            //此处数组元素为空,则直接将新节点放进去
            table[newNode.hash] = newNode;
            size++;
        } else {
            //此处数组元素不为空。则遍历对应链表。。
            while (temp != null) {
                //判断key如果重复,则覆盖
                if (temp.key.equals(key)) {
                    keyRepeat = true;
                    System.out.println("key重复了!");
                    temp.value = value;//只是覆盖value即可。其他的值(hash,key,next)保持不变。
                    break;
                } else {
                    //key不重复,则遍历下一个
                    iterLast = temp;
                    temp = temp.next;
                }
            }
            if (!keyRepeat) {
            	//采用头插法进行插入元素
				Node2 first = table[newNode.hash];
				newNode.next = first;
				table[newNode.hash] = newNode;

                //iterLast = newNode;
                size++;
            }
        }
    }


	public Object remove(Object key) {
		//定义新的节点对象
		Node2 newNode = new Node2();
		newNode.hash = myHash(key.hashCode(), table.length);
		newNode.key = key;
		newNode.value = "";
		newNode.next = null;

		Node2 temp = table[newNode.hash];

		Node2 preNode = null;//key匹配的前一个节点

		boolean keyRepeat = false;

		if (temp == null) {
			return null;
		} else {
			preNode = temp;
			Long current = 0L;
			//此处数组元素不为空。则遍历对应链表。。
			while (temp != null) {
				//判断key相等
				if (temp.key.equals(key)) {
					if(current==0L){
						//说明是第一个元素
						table[newNode.hash].next = temp.next;
					}else{
						preNode.next = temp.next;//删除当前节点
					}
					size--;
					return temp.value;
				} else {
					//key不相等,则遍历下一个
					preNode = temp;
					temp = temp.next;
					current++;
				}
			}
		}
		return null;
	}

    public Long size(){
		return size;
	}

    public static int myHash(int v, int length) {
        System.out.println(v+" hash in myHash:" + (v & (length - 1)));
        //System.out.println(v+" hahs in myHash:" + (v % (length - 1)));
        return v & (length - 1);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("{");
        //遍历bucket数组
        for (int i = 0; i < table.length; i++) {
            Node2 temp = table[i];
			StringBuilder sb1 = new StringBuilder("{");
            //遍历链表
            while (temp != null) {
                sb.append(temp.key + ":" + temp.value + ", ");
				sb1.append(temp.key + ":" + temp.value + ", ");
                temp = temp.next;
            }
            if(sb1.length()>1){
				sb1.setCharAt(sb1.length() - 2, '}');
			}else{
            	sb1.append(" }");
			}
			System.out.println("table["+i+"] node is : " + sb1.toString());
        }
        sb.setCharAt(sb.length() - 1, '}');
        return sb.toString();
    }

    public static void main(String[] args) {
        MyHashMap m = new MyHashMap();

		Map<Object,Object> hashMap = new HashMap<>();

        for(int i=0;i<160;i++){
        	m.put(i,"data" + i);
			//hashMap.put(i,"data" + i);
		}
        Object deleteKey = -1;
		System.out.println("删除元素 key="+deleteKey+", value="+m.remove(deleteKey));
        System.out.println(m);

        System.out.println("元素个数:" + m.size());


//        hashMap.remove(0);
//		Iterator it = hashMap.entrySet().iterator();
//		while (it.hasNext()) {
//			Map.Entry entry = (Map.Entry) it.next();
//			int code = entry.hashCode();
//			Object key = entry.getKey();
//			Object value = entry.getValue();
//			System.out.println("code:"+code+"---key:" + key + "---" + "value:" + value);
//		}
//		System.out.println(hashMap);
    }
}

 

### LRU 缓存机制的设计与实现 LRU(Least Recently Used,最近最少使用)缓存是一种常用的数据结构,用于存储有限数量的键值对。其核心机制是:当缓存容量达到上限时,优先移除最近最少使用的数据。为了高效实现 `get` 和 `put` 操作,通常采用 **哈希表 + 双向链表** 的组合结构。 #### 数据结构选择 - **双向链表**:用于维护缓存中键值对的访问顺序,最近使用的节点放在链表头部,最久未使用的节点放在尾部。 - **哈希表(HashMap)**:用于实现 O(1) 时间复杂度的键值查找,存储键与链表节点的映射关系。 #### 核心操作 1. **get(key)**: - 如果 `key` 不存在于哈希表中,返回 `-1`。 - 如果 `key` 存在,将对应的节点移动到链表头部,并返回其值。 2. **put(key, value)**: - 如果 `key` 已存在,更新其值并将该节点移动到链表头部。 - 如果 `key` 不存在: - 创建新节点并插入链表头部。 - 如果当前缓存已满,删除链表尾部节点(即最久未使用的节点)。 #### Java 实现代码 以下是一个完整的 Java 实现: ```java import java.util.HashMap; class LRUCache { class DLinkedNode { int key; int value; DLinkedNode prev; DLinkedNode next; } private HashMap<Integer, DLinkedNode> cache = new HashMap<>(); private int size; private int capacity; private DLinkedNode head, tail; private void addNode(DLinkedNode node) { node.prev = head; node.next = head.next; head.next.prev = node; head.next = node; } private void removeNode(DLinkedNode node) { DLinkedNode prev = node.prev; DLinkedNode next = node.next; prev.next = next; next.prev = prev; } private void moveToHead(DLinkedNode node) { removeNode(node); addNode(node); } private DLinkedNode popTail() { DLinkedNode res = tail.prev; removeNode(res); return res; } public LRUCache(int capacity) { this.size = 0; this.capacity = capacity; head = new DLinkedNode(); tail = new DLinkedNode(); head.next = tail; tail.prev = head; } public int get(int key) { DLinkedNode node = cache.get(key); if (node == null) return -1; moveToHead(node); return node.value; } public void put(int key, int value) { DLinkedNode node = cache.get(key); if (node == null) { DLinkedNode newNode = new DLinkedNode(); newNode.key = key; newNode.value = value; cache.put(key, newNode); addNode(newNode); size++; if (size > capacity) { DLinkedNode tail = popTail(); cache.remove(tail.key); size--; } } else { node.value = value; moveToHead(node); } } } ``` #### 时间复杂度分析 - **get 操作**:O(1),通过哈希表直接查找,再通过双向链表调整位置。 - **put 操作**:O(1),插入或更新节点时,通过哈希表和链表操作实现。 #### 空间复杂度 - **O(capacity)**,缓存最多存储 `capacity` 个键值对。 #### 应用场景 LRU 缓存机制广泛应用于以下场景: - 操作系统中的页面置换算法。 - 数据库查询缓存。 - Web 应用中的热点数据缓存。 - 浏览器历史记录管理。 #### 性能优化 - **并发访问控制**:在多线程环境下,可使用 `ConcurrentHashMap` 和同步机制确保线程安全。 - **内存优化**:对于大规模数据缓存,可考虑使用更高效的内存管理方式,如堆外内存或分片缓存。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值