热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java源码解析之HashMap

本文对HashMap的源码进行了简略分析。本文基于Java7的JDK1.7.0_79-64分析,由于Java8有改变,之后会基于Java8另写文章解析。由于作者文笔拙劣,分析粗鄙,

一、HashMap类声明:

HashMap继承于AbstractMap并且实现了接口Map,Cloneable,Serializable。

public class HashMap
    extends AbstractMap
     implements Map, Cloneable, Serializable
{}

二、HashMap类层次:

HashMap实现了三个接口,继承一个抽象类。除此之外我们应该知道Object是所有类的超类。之所以有一个AbstractMap抽象类,是为了提供一个Map接口实现的骨架,并提供一些实现,最少化实现Map的实现类。

关于序列化,克隆接口另写文章介绍,敬请期待。

技术分享

三、HashMap存储结构图:

图片来自:http://github.thinkingbar.com/hashmap-analysis/, 敬谢。

技术分享

四、HashMap变量说明:

//默认的初始化容量,必须是2的的次方数,默认是16;在初始化HashMap没有指定容量时使用
static final int DEFAULT_INITIAL_CAPACITY = 1 <<4;

//最大容量2^30,用于table数组,下面做的说明大家不要和size混淆
//我们知道int是32位的,最大正整数是2^31-1,
//另外我们分析源码会发现在resize的时候将阈值设为了Integer.MAX_VALUE,即2^31-1
//所以HashMap实际用到的最大size为2^31-1
static final int MAXIMUM_CAPACITY = 1 <<30;

//默认的装载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;

//空数组,被所有HashMap共享,一般只是作为table的默认值
static final Entry[] EMPTY_TABLE = {};

//用于存储HashMap中的桶,长度总是2的次方数,会在第一次put的时候分配内存
//transient表明序列化的时候table不会序列化
transient Entry[] table = (Entry[]) EMPTY_TABLE;

//size表示HashMap中存储的映射个数
transient int size;

//HashMap实际使用的阈值变量
//一开始这个值是默认的阈值,
//但是当分配table内存是,值为容量*装载因子,即(capacity * load factor)
int threshold;

//装载因子,用以表示table装满程度
//当没有指明装载因子时使用DEFAULT_LOAD_FACTOR
final float loadFactor;

//用以记录HashMap自创建以来结构发生变化的次数
//结构发生变化指:1.增加映射, 2.移除映射, 3.rehash
//这个值会使由这个HashMap得到的迭代器(iterators)快速失败(fail-fast)
//因为在生成迭代器的时候复制了一份modCount当时的值,如果在这之后HashMap发生了结构变化,
//那么这个迭代器中的值就不等于modCount,迭代器就抛出ConcurrentModificationException
//但是通过这个迭代器去改变HashMap的结构是可以的
transient int modCount;

//可选哈希阈值默认值2^31-1
//目的是为了减少String键值的弱哈希计算的冲突
//JVM首先读取系统属性jdk.map.althashing.threshold,
//值为-1时,禁用该值;值为小于0时,抛出异常;值为正则启用可选哈希
//源码分析时会进一步讲解
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

//哈希种子,在分配table存储时会计算该值
transient int hashSeed = 0;

//存储映射的Set变量
private transient Set> entrySet = null;

五、HashMap方法解析:

1、构造方法:

/**
 * 通过容量和装载因子初始化HashMap
 */
public HashMap(int initialCapacity, float loadFactor) {
        //以下对容量值进行检查,确保在(0, MAXIMUM_CAPACITY]之间
        if (initialCapacity <0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        //以下对装载因子进行检查,要求是正的Float数字
        //思考装载因子并没有限制在小于1的范围内,可见可以是大于1的数字,只是这个时候再也反映不出
        //table的装满程度,同时put的冲突几率将增高,装载因子失去设计时的意义
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        this.loadFactor = loadFactor;
        threshold = initialCapacity; //初始阈值为容量值
        /**
         * HashMap中此init方法的方法体是空的,子类有需要可以实现,
         * 主要是为了执行子类的钩子,用的是模板方法设计模式
         * 这个方法总是在构造方法的最后一步执行,伪构造方法(clone,readObject)也会调用执行
         */
        init(); /
}

/**
 * 使用容量和默认的装载因子初始化HashMap
 */
public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

/**
 * 使用默认的容量和装载因子初始化HashMap
 */
public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}

2、内部类:

/**
 * 调用处是在虚拟机启动好之后
 */
private static class Holder {
        static final int ALTERNATIVE_HASHING_THRESHOLD;

        static {
            //获得系统属性jdk.map.althashing.threshold
            //此种方式调用时不进行安全检查,doPrivileged里面的代码享有特权
            String altThreshold = java.security.AccessController.doPrivileged(
                new sun.security.action.GetPropertyAction(
                    "jdk.map.althashing.threshold"));

            int threshold;
            try {
                //当系统属性值有被设置,那么获得该值,否者使用可选哈希阈值默认值
                threshold = (null != altThreshold)
                        ? Integer.parseInt(altThreshold)
                        : ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;

                //如果系统属性设置为-1,则赋予Integer最大正整数
                if (threshold == -1) {
                    threshold = Integer.MAX_VALUE;
                }

                //小于0,抛出异常
                if (threshold <0) {
                    throw new IllegalArgumentException("value must be positive integer.");
                }
            } catch(IllegalArgumentException failed) {
                throw new Error("Illegal value for ‘jdk.map.althashing.threshold‘", failed);
            }

           //最后存储为可选哈希值
            ALTERNATIVE_HASHING_THRESHOLD = threshold;
        }
}

static class Entry implements Map.Entry {
        final K key; //键值,注意这里key是final的,说明一旦赋值不允许修改,强调key值设计原则
        V value; //映射值
        Entry next; //关联的下一个Enrty引用
        int hash; //哈希码

        Entry(int h, K k, V v, Entry n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

        public final K getKey() {
            return key;
        }

        public final V getValue() {
            return value;
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }

        public final int hashCode() {
            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
        }

        public final String toString() {
            return getKey() + "=" + getValue();
        }

        /**
         * 此方法被调用,当已存在的Entry中的value被修改的时候
         * 子类需要实现
         */
        void recordAccess(HashMap m) {
        }

        /**
         * 此方法被调用,当这个Entry被移除
         * 子类需要实现
         */
        void recordRemoval(HashMap m) {
        }
}

/**
 * 迭代器实现抽象类,只有next()未实现,留待子类实现
 */
private abstract class HashIterator implements Iterator {
        Entry next;        // 下一个entry
        int expectedModCount;   // 用于快速失败机制
        int index;               // 桶的位置索引
        Entry current;     // 当前entry

        HashIterator() {
            expectedModCount = modCount; //赋予当时HashMap的modCount值
            if (size > 0) {
                Entry[] t = table;
                //找到table数组中按序不为null的那个桶
                while (index null)
                    ;
            }
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Entry nextEntry() {
            //这句很重要,当生成迭代器后,HashMap结构发生了变化,则迅速失败
            if (modCount != expectedModCount) 
                throw new ConcurrentModificationException();
            Entry e = next;
            if (e == null)
                throw new NoSuchElementException();

            if ((next = e.next) == null) {
                Entry[] t = table;
                while (index null)
                    ;
            }
            current = e;
            return e;
        }

        public void remove() {
            if (current == null)
                throw new IllegalStateException();
           //这句很重要,当生成迭代器后,HashMap结构发生了变化,则迅速失败
           if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object k = current.key;
            current = null;
            HashMap.this.removeEntryForKey(k);
            //移除后要更新expectedModCount,否者再次调用会迅速失败
            expectedModCount = modCount;
        }
}

private final class ValueIterator extends HashIterator {
        public V next() {
            return nextEntry().value;
        }
 }

private final class KeyIterator extends HashIterator {
        public K next() {
            return nextEntry().getKey(); //强调key值设计理念
        }
}

private final class EntryIterator extends HashIterator> {
        public Map.Entry next() {
            return nextEntry();
        }
}

/**
 * 用于生成key的set集合,所有方法都委托给HashMap的方法
 */
private final class KeySet extends AbstractSet {
        public Iterator iterator() {
            return newKeyIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            return HashMap.this.removeEntryForKey(o) != null;
        }
        public void clear() {
            HashMap.this.clear();
        }
}

/**
 * 用于生成value集合,所有方法都委托给HashMap的方法
 */
private final class Values extends AbstractCollection {
        public Iterator iterator() {
            return newValueIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsValue(o);
        }
        public void clear() {
            HashMap.this.clear();
        }
}

/**
 * 用于生成entry的set集合,所有方法实现都委托给HashMap的方法
 */
private final class EntrySet extends AbstractSet> {
        public Iterator> iterator() {
            return newEntryIterator();
        }
        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry) o;
            Entry candidate = getEntry(e.getKey());
            return candidate != null && candidate.equals(e);
        }
        public boolean remove(Object o) {
            return removeMapping(o) != null;
        }
        public int size() {
            return size;
        }
        public void clear() {
            HashMap.this.clear();
        }
}

3、put方法:

/**
 * 将键值key与映射值value组成一个映射存储在HashMap中
 * 允许key为null,因为key唯一,所以最多有一个这样的映射
 * 允许有多个value为null的映射,但是key不同
 */
public V put(K key, V value) {
        //在第一次put的时候会检查到table为空并初始化
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        //如果key为null就调用专用的方法进行put,然后返回
        if (key == null)
            return putForNullKey(value);
        //根据键值key进行哈希计算得到哈希码
        int hash = hash(key);
        //根据哈希码计算应当将该映射放在table的哪个索引链表下
        int i = indexFor(hash, table.length);
        //遍历第i个链表查找是否存在于key相同的entry,存在则替换entry的value值并返回旧的value值
        for (Entry e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this); //put替换存在的entry的value值须调用,子类实现的方法
                return oldValue;
            }
        }

        //如果不存在为key的entry,则添加新的entry到HashMap中
        //因为HashMap结构发生变化,则modCount加1
        //返回null
        modCount++;
        addEntry(hash, key, value, i);
        return null;
}

/**
 * put键值key为null的时候调用的方法
 * 直接在table的第0个索引的链表上查找替换或添加 
 */
private V putForNullKey(V value) {
        for (Entry e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
}

/**
 * 在table的第bucketIndex个链表上添加哈希码为hash,键值为key,映射值为value的entry
 */
void addEntry(int hash, K key, V value, int bucketIndex) {
        //首先检查HashMap的size是否已经到达或者大于阈值,并且第bucketIndex个索引的链表不为null
        //那么HashMap需要rehash尝试申请更多table空间,注意是尝试,不一定能分配到的
        //同时我们也能知道如果第bucketIndex个索引的链表为null,即时超过阈值也不会去申请空间
        //注意size指的是HashMap实际的entry数量,threshold是table的装满程度的具体阈值
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length); //尝试请求申请当前table长度的2倍空间
            //重新计算put映射的key的哈希码
            //因为这个方法是给所有key的添加使用的,所以要考虑可以为null的情况
            hash = (null != key) ? hash(key) : 0;
            //重新计算put的映射应该放在申请新空间后的table的哪个索引链表上
            bucketIndex = indexFor(hash, table.length);
        }
        
        //创建Entry对象,并插入到第bucketIndex个索引链表的第一个位置
        createEntry(hash, key, value, bucketIndex);
}

/**
 * 创建Entry对象,并插入到第bucketIndex个索引链表的第一个位置,size加1
 */
void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
}

4、get方法:

/**
 * 获取HashMap中键值为key的entry的value值
 * 如果返回null,有可能存储的value就是null,也有可能没有key对应entry,需要结合containsKey检查
 */
public V get(Object key) {
        if (key == null)
            return getForNullKey();  //key为null,调用专用方法
        Entry entry = getEntry(key);

        return null == entry ? null : entry.getValue();
}

private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        //直接到table的第0个索引链表下查找
        for (Entry e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
}

final Entry getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        //首先根据key计算哈希码,然后根据哈希码找到table下的索引链表,最后查找
        int hash = (key == null) ? 0 : hash(key);
        for (Entry e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
}

5、remove方法:

/**
 * 从HashMap中移除键值为key的entry,并且返回对应的value值
 */
public V remove(Object key) {
        Entry e = removeEntryForKey(key);
        return (e == null ? null : e.value);
}

final Entry removeEntryForKey(Object key) {
        if (size == 0) {
            return null;
        }
        //首先根据key计算哈希码,然后根据哈希码找到table下的索引链表 
        int hash = (key == null) ? 0 : hash(key);
        int i = indexFor(hash, table.length);
        Entry prev = table[i];
        Entry e = prev;

       //查找链表
        while (e != null) {
            Entry next = e.next;
            Object k;
            //找到该key对应的entry
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) {
               //结构该表,计数加1;同时HashMap大小减一
                modCount++;
                size--;
                //如果移除的是table索引的第一个entry,则直接修改table[i]
                if (prev == e)
                    table[i] = next;
                //不是第一个entry,则将移除entry的前一个entry的next指向移除entry的next
                else
                    prev.next = next;
                //记录移除的entry
                e.recordRemoval(this);
                return e;
            }
            //顺序后移,再次循环
            prev = e;
            e = next;
        }

        //返回移除的entry
        return e;
}

6、一些公用方法:

/**
 * 在HashMap使用之前需要做一次膨化处理
 */
private void inflateTable(int toSize) {
        //找到大于等于toSize且是2的次方数的最小数
        //所以我们需要知道table数组的长度一定是2的次方数
        int capacity = roundUpToPowerOf2(toSize);

        //计算阈值
        //取二者中较小的那个
        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        table = new Entry[capacity];
        //初始化哈希种子
        initHashSeedAsNeeded(capacity);
}

private static int roundUpToPowerOf2(int number) {
        return number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (number > 1) ? Integer.highestOneBit((number - 1) <<1) : 1;
}

/**
 * 初始化hashSeed,但是貌似hashSeed一直是0,不知道其中缘由
 */
final boolean initHashSeedAsNeeded(int capacity) {
        boolean currentAltHashing = hashSeed != 0;
        boolean useAltHashing = sun.misc.VM.isBooted() &&  //检查虚拟机是否已启动
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        boolean switching = currentAltHashing ^ useAltHashing;
        if (switching) {
            hashSeed = useAltHashing
                ? sun.misc.Hashing.randomHashSeed(this)  //生成随机种子
                : 0;
        }
        return switching;
}

final int hash(Object k) {
        int h = hashSeed;
        //当哈希种子不为0且k是String时调用特殊的哈希算法
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        // 下面的方法可以使在table每个位置的冲突次数都是一个常数值,在装载因子为0.75的时候为8
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
}

static int indexFor(int h, int length) {
        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
        // 因为table的length总是2的次方数,所以下面的方法是对h在length上做模运算
        // 比如50 % 16 = 2 = 000010 = 110010 & 001111
        return h & (length-1);
}

/**
 * resize table的大小
 */
void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        //如果容量已经到达最大容量值,就将阈值设为Integer最大值,返回
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        //将当前table内容转换到新的容量table中
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

/**
 * 将旧table中的entry转化到新table中去,rehash指明是否需要重新计算哈希码
 */
void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry e : table) {
            while(null != e) {
                Entry next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
}

7、其他方法:略

六、说明:

本文对HashMap的源码进行了简略分析。本文基于Java7的JDK1.7.0_79-64分析,由于Java8有改变,之后会基于Java8另写文章解析。
由于作者文笔拙劣,分析粗鄙,纰漏之处还望各位不吝斧正。

Java源码解析之HashMap


推荐阅读
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
author-avatar
U友50141148
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有