数据结构思维笔记(十)Map接口

本文探讨了基于键值对List实现的MyLinearMap,分析了其时间复杂度,并对比了哈希表在Map接口实现上的优势。通过详细讲解findEntry和equals方法,揭示了线性时间和常数时间操作的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在接下来的几个练习中,我介绍了Map接口的几个实现。其中一个基于哈希表,这可以说是所发明的最神奇的数据结构。另一个是类似的TreeMap,不是很神奇,但它有附加功能,它可以按顺序迭代元素。

我们从一个Map开始,它使用键值对的List实现。


1.实现MyLinearMap

简单来说,就是继承Map,放入List中,来看下初始结构

public class MyLinearMap<K, V> implements Map<K, V> {

    private List<Entry> entries = new ArrayList<Entry>();

MyLinearMap对象具有单个实例变量,entries,这是一个EntryArrayList对象。每个Entry都包含一个键值对。这里是定义:

public class Entry implements Map.Entry<K,V> {
        private K key;
        private V value;

        public Entry(K key, V value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(V newValue) {
            this.value = newValue;
            return value;
        }
    }

2.分析MyLinearMap

两个私有方法FindEntry用于根据key值,找到对应的实体,equal用于比较,下边是实现:

private Entry findEntry(Object target) {
    for (Entry entry: entries) {
        if (equals(target, entry.getKey())) {
            return entry;
        }
    }
    return null;
}

private boolean equals(Object target, Object obj) {
    if (target == null) {
        return obj == null;
    }
    return target.equals(obj);
}

equals运行时间取决于值的大小,跟数目无关,是常数时间O(1).

findEntry运行时间取决于数目,最好的情况下,可以一开始就找到,但是平均下来的话,仍要遍历整个map,是线性时间,时间复杂度为O(n).

大部分的MyLinearMap核心方法使用findEntry,包括putget,和remove。这就是他们的样子:

public V put(K key, V value) {
    Entry entry = findEntry(key);
    if (entry == null) {
        entries.add(new Entry(key, value));
        return null;
    } else {
        V oldValue = entry.getValue();
        entry.setValue(value);
        return oldValue;
    }
}

public V get(Object key) {
    Entry entry = findEntry(key);
    if (entry == null) {
        return null;
    }
    return entry.getValue();
}

public V remove(Object key) {
    Entry entry = findEntry(key);
    if (entry == null) {
        return null;
    } else {
        V value = entry.getValue();
        entries.remove(entry);
        return value;
    }
}

这三个方法中,调用findEntry是线性时间,其余操作都为常数时间,所以总的时间复杂度为线性时间

总而言之,核心方法都是线性的,这就是为什么我们将这个实现称为MyLinearMap

如果我们知道输入的数量很少,这个实现可能会很好(很好的原因就是,数目少了有更大机率查找为常数时间),但是我们可以做得更好。实际上,Map所有的核心方法都是常数时间的实现。当你第一次听到这个消息时,可能似乎觉得不可能。实际上我们所说的是,你可以在常数时间内大海捞针,不管海有多大。这是魔法。

我们不是将条目存储在一个大的List中,而是把它们分解成许多短的列表(这样查找时就为常数时间)。对于每个键,我们将使用哈希码(在下一节中进行说明)来确定要使用的列表。 使用大量的简短列表比仅仅使用一个更快,但正如我将解释的,它不会改变增长级别;核心功能仍然是线性的。但还有一个技巧:如果我们增加列表的数量来限制每个列表的条目数,就会得到一个恒定时间的映射。你会在下一个练习中看到细节,但是首先要了解哈希!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值