ConcurrentHashMap和HashTable区别?(超详细解释)

本文详细解析了哈希表的基本概念,重点比较了HashMap、Hashtable和ConcurrentHashMap之间的区别,包括线程安全、哈希碰撞处理、效率提升以及在多线程环境下的应用。ConcurrentHashMap结合了高效性和线程安全性,是HashMap的改进版本。
摘要由CSDN通过智能技术生成

为了完整理解这个问题,我们的学习顺序为:

1、什么是哈希表

2、HashMap为什么效率高于Hashtable?

3、HashMap和Hashtable的区别

4、ConcurrentHashMap和HashTable区别?(重点!)

5、面试总结(重点!)

一、什么是哈希表

01、基本概念

重点关注:槽位、哈希碰撞

哈希表或者散列表

给定表M,若存在函数h(key),使得对任意给定的关键字值key,代入函数后能得到包含该key的记录,这个记录指的是:在表中的地址,则称表M为哈希表或者散列表。函数h(key)为哈希函数或者散列函数

哈希函数的好坏会直接影响哈希表的优劣。


槽位(slot)或者桶(bucket)

散列表是由哈希函数和数组共同构建的一种数据结构,元素由键和值组成。散列表中的一个位置称为槽位(slot)或者(bucket),用于保存键值对。


哈希碰撞(hash collision)

由哈希函数映射的数组下标(称为散列地址-桶号),决定了给定的键存于散列表的哪个桶中。如果不同的key映射到了相同的数组下标,则发生了哈希碰撞(hash collision)。

数组中的元素的数据结构可以是一个链表或者一棵红黑树,链表或者红黑树就是用来解决哈希碰撞的!

  • 在Java1.7阶段,是不存在树的,即挂载到数组同一个位置的多个元素,通过next属性,构成了一个单向链表
    • 数组中每一个元素是一个Node,有四个属性:
      • hash:当前位置值的hash值
      • key:当前位置的键
      • value:当前位置存储的值
      • next:下一个Node
  • 在Java1.8中,当单向链表中的元素大于等于8时,单项链表会变成一棵树,该树为红黑树,而又当数量小于6时,则又转化为链表存储。该转换操作是由final void treeifyBin(Node<K,V>[] tab,int hash)函数实现的。


哈希表的容量

散列表所拥有的桶数被称为散列表的容量(capacity)

02、哈希表示例

我们要在哈希表中执行插入操作:

图中,0-5标记部分即代表哈希表,令关键字 key=kobe,则通过哈希函数f(key)得到其槽位为1,故把 kobe 放在数组下标为1的桶中。

如果不同的 key 映射到了同一数组下标,就将其放入对应数组下标的单链表中。

Java 的集合中给出了底层结构采用哈希表数据结构的实现类,按照时间顺序分别为第一代Hashtable、第二代 HashMap和第三代 ConcurrentHashMap。它们的相同点:底层结构都是哈希表,都是用来存储 key-value 映射,都实现了 Map 接口。

HashTable已经不建议使用了,因为有一种效率高一些,同时又线程安全的Map:ConcurrentHashMap!

二、HashMap为啥比Hashtable效率高?

01、HashTable有锁机制

HashTable线程安全,有锁机制

1、HashTable线程安全的策略实现代价太大了,简单粗暴。get/put所有相关操作都是synchronized的,这相当于个整个Hash表加了一把大锁。

2、当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程使用 put 添加元素,或者使用 get,会导致程序出现问题,相当于所有的操作串行化,竞争会越来越激烈效率越低。

package hashMapAndConcurrentHashMap;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;

public class test1 {
    public static void main(String[] args) {
        HashMap<Object, Object> hashMap = new HashMap<>();
//        ConcurrentHashMap<Object, Object> hashMap = new ConcurrentHashMap<>();
//        Hashtable<Object, Object> hashMap = new Hashtable<>();
        new Thread(() -> {
            for (int i = 1; i < 500000; i++) {
                hashMap.put(new Integer(i),i);
            }
            System.out.println("t1 over");
        }).start();
        new Thread(() -> {
            for (int i = 500001; i < 1000000; i++) {
                hashMap.put(new Integer(i),i);
            }
            System.out.println("t2 over");
        }).start();
        new Thread(() -> {
            for (int i = 500001; i < 1000000; i++) {
                hashMap.put(new Integer(i),i);
            }
            System.out.println("t3 over");
        }).start();
        new Thread(() -> {
            for (int i = 500001; i < 1000000; i++) {
                hashMap.put(new Integer(i),i);
            }
            System.out.println("t4 over");
        }).start();
        new Thread(() -> {
            for (int i = 0; i < 5000000; i++) {
                Object o = hashMap.get(i);
                System.out.println(o+":::hashtable111");
            }
        }).start();
    }
}

可以发现,当使用HashMap的时候,可能会出现,get和put冲突!而Hashtable和ConcurrentHashMap由于锁的机制,所以不会冲突。

02、HashTable底层算法效率低

从两个方向说明这个观点

02-01、获取hash方式

其实就是获取槽位的方式

HashTable使用取模的方式获取槽位,HashMap使用位运行获取槽位

public synchronized V put(K key, V value) {
    // 注意value不能为空值(HashMap的value可以放空值)
    if (value == null) {
        throw new NullPointerException();
    }
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    // 查找元素是否已经在table中,存在就返回原entry的value
    for(; entry != null ; entry = entry.next) {
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }
    // 不存在就去添加
    addEntry(hash, key, value, index);
    return null;
}

(hash & 0x7FFFFFFF) % tab.length,可以看出Hashtable也是通过取余方式确定key所在的槽,由于Hashtable数组长度不是2的N次幂所以只能使用%方式取余,并且Hashtable也没有使用扰动函数混合键hashCode高低位。addEntry 方法如下

private void addEntry(int hash, K key, V value, int index) {
    modCount++;
    Entry<?,?> tab[] = table;
    if (count >= threshold) {
        // 如果超过阈值,则重新刷新表
        rehash();
        tab = table;
        // 通过key的hashcode计算位置
        hash = key.hashCode();
        index = (hash & 0x7FFFFFFF) % tab.length;
    }
    // 创建新的entry并插入
    Entry<K,V> e = (Entry<K,V>) tab[index];
    tab[index] = new Entry<>(hash, key, value, e);
    count++;
}

HashTable遇到hash碰撞时使用链表解决,并没有红黑树

02-02、槽位冲突

HashTable使用链表数据结构来存储槽位相同的数据,HashMap同样也使用链表但在数据量多的情况下会升级成红黑树数据结构

  • 因为链表的查找时间复杂度为O(N),而红黑树查找的时间复杂度为O(logN)
  • 红黑树时间复杂度低

有了上面的基础知识,我们再来对比HashMap和Hashtable的区别!

三、HashMap和Hashtable的区别

首先需要了解两个前提:

HashMap、Hashtable其实都是一个可以扩容的动态数组。

HashMap和Hashtable的底层实现都是数组+链表结构实现

01、线程安全

两者最主要的区别在于Hashtable是 线程安全,而HashMap则非线程安全

Hashtable的实现方法里面都添加了synchronized关键字来确保 线程同步

我们平时使用时若无特殊需求建议使用HashMap,因为HahsMap效率高于Hashtable!

那么如何解决HashMap线程不安全的弊端呢?

在多线程环境下若使用HashMap,需要使用Collections.synchronizedMap()方法来获取一个线程安全的集合。

Map<Object, Object> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
synchronizedMap.put("ceshi","111");

Collections.synchronizedMap()实现原理是Collections定义了一个SynchronizedMap的内部类,这个类实现了Map接口,在调用方法时使用synchronized来保证线程同步,当然了实际上操作的还是我们传入的HashMap实例

简单的说就是Collections.synchronizedMap()方法帮我们在操作HashMap时自动添加了synchronized来实现线程同步,类似的其它Collections.synchronizedXX方法也是类似原理

02、key是否允许为null

HashMap可以使用null作为key,而Hashtable则不允许null作为key

虽说HashMap支持null值作为key,不过建议还是尽量避免这样使用,因为一旦不小心使用了,若因此引发一些问题,排查起来很是费事

HashMap以null作为key时,总是存储在table数组的第一个节点上

03、实现的接口不同

HashMap是对Map接口的实现,HashTable实现了Map接口和Dictionary抽象类

04、初始容量不同

HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75
HashMap扩容时是当前容量翻倍即:capacity2,Hashtable扩容时是容量翻倍+1即:capacity2+1

05、包含的contains方法不同

HashMap是没有contains方法的,而包括containsValue和containsKey方法;

hashtable则保留了contains方法,效果同containsValue,还包括containsValue和containsKey方法。

contains方法是查询当前集合中是否存在某个值,而不是某个键哦!

package com.zhangfushuai;

import java.util.*;

public class Demo {
public static void main(String[] args) {
    Hashtable hashtable = new Hashtable();
    hashtable.put("11","z");
    hashtable.put("11","h"); //重复添加会覆盖
    hashtable.put("12","h");

    hashtable.put("13","x");
    hashtable.put("13","x");

    String s = hashtable.toString();
    System.out.println(s); //打印Hashtable集合

    boolean contains1 = hashtable.contains("z");
    System.out.println(contains1);//查询哈希表中,键相同,值不相同的node的时候,会返回false

    boolean contains2 = hashtable.contains("h");
    System.out.println(contains2);//查询哈希表中,键不相同,值相同的node的时候,true
    
    boolean x = hashtable.contains("x");
    System.out.println(x);//查询哈希表中,键相同,值相同的node的时候,true
}
}

06、两者计算 hash的方法不同

这个其实就是获取槽位的方式不同,上文已经提到过了

为了得到元素的位置,首先需要根据元素的 KEY计算出一个hash值,然后再用这个hash值来计算得到最终的位置。

①:HashMap有个hash方法重新计算了key的hash值,因为hash冲突变高,所以通过一种方法重算hash值的方法:

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
index = (n - 1) & hash

注意这里计算hash值的时候,先调用hashCode方法计算出来一个hash值,再将hash与右移16位后相异或,即取摸之后,从而得到新的hash值。


②:Hashtable通过计算key的hashCode()来得到hash值

int index = (hash & 0x7FFFFFFF) % tab.length;

& 0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而& 0x7FFFFFFF后,只有符号位改变,而后面的位都不变。


总结 - 分析利弊

取模时,不需要做除法,只需要做位运算。在计算机中,位运算比除法的效率要高很多。

四、扩展 - HashSet

除了HashMap和Hashtable外,还有一个hash集合HashSet!

有所区别的是HashSet不是key-value结构,而仅仅存储了不重复的元素,相当于简化版的HashMap,只是包含HashMap中的key,而不包含value而已。

通过查看源码也证实了这一点,HashSet内部就是使用HashMap实现,只不过HashSet里面的HashMap所有的value都是同一个Object而已

HashSet底层使用HashMap来保存所有元素,因此HashSet的实现比较简单,相关HashSet的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值

因此HashSet也是非线程安全的,至于HashSet和Hashtable的区别,HashSet就是个简化的HashMap的,和Hashtable和HashMap的区别一样,不再赘述了!

五、ConcurrentHashMap与HashTable的区别

有了以上基础之后,我们开始学习:ConcurrentHashMap与HashTable的区别!

一句话:ConcurrentHashMap融合了hashtable和hashmap二者的优势。

集合诞生的先后顺序:HashTable-----HashMap-----ConcurrentHashMap

一个技术的出现,肯定是为了解决某个方案才诞生的,上文已经了解过,HashTable线程安全,但是效率慢,HashMap线程不安全,但是效率高,那么ConcurrentHashMap正是为了解决这个问题而诞生的。

JDK1.7的ConcurrentHashMap 底层和HashMap一样,仍然采用:分段的数组+链表 实现

JDK1.8的ConcurrentHashMap 底层和HashMap一样,仍然采用:数组+链表/红黑二叉树。

ConcurrentHashMap解决了HashMap的线程不安全的问题,让HashMap线程安全了,HashTable也是线程安全的,所以转换一下思路

ConcurrentHashMap与HashTable的区别!其实就是在问:ConcurrentHashMap与HashTable在实现线程安全的实现方式上有什么区别?

01、实现线程安全的方式

ConcurrentHashMap

在 JDK1.7 的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),相较于Hashtable的锁,更加的细粒度了,ConcurrentHashMap将hash表分为16个桶(默认值),诸如get、put、remove等常用操作只锁住当前需要用到的桶。

每一把锁只锁容器的其中一部分数据,这个时候当多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。试想,原来 只能一个线程进入,现在却能同时16个写线程进入,并发性的提升是显而易见的

ConcurrentHashMap在读取的大多数时候都没有用到锁定,所以读取操作几乎是完全的并发操作,而写操作锁定的粒度又非常细,比起之前又更加快速(这一点在桶更多时表现得更明显些)。只有在求size等操作时才需要锁定整个表。

到了JDK1.8 的时候已经摒弃了 Segment 的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6 以后 对 synchronized 锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在 JDK1.8 中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;


HashTable

Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。

02、两者的对比图


首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问

ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成

Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。

static class Segment<K,V> extends ReentrantLock implements Serializable {
}

一个 ConcurrentHashMap里包含一个 Segment 数组。Segment的结构和 HashMap 类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个 HashEntry 数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 的锁。


JDK1.8 的 ConcurrentHashMap 不再是 Segment 数组 + HashEntry 数组 + 链表,而是 Node 数组 + 链表 / 红黑树。不过,Node 只能用于链表的情况,红黑树的情况需要使用 TreeNode。当冲突链表达到一定长度时,链表会转换成红黑树。

ConcurrentHashMap 取消了 Segment 分段锁,采用 CAS 和 synchronized 来保证并发安全。数据结构跟 HashMap1.8 的结构类似,数组+链表/红黑二叉树。Java 8 在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为 O(N))转换为红黑树(寻址时间复杂度为 O(log(N)))

synchronized 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,效率又提升 N 倍。

✨现在回答一下这个面试问题:

ConcurrentHashMap和HashTable区别?

ConcurrentHashMap:锁定map的一部分

遍历集合时候安全失败,在原有集合上拷贝一份,不会出现并发修改异常

HashTable : 锁定整个map

遍历集合快速失败,会出现并发修改异常

并发修改异常:集合在迭代时候,对集合进行修改,则会出现的异常

不知名小杨
关注 关注
  • 17
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java中ConcurrentHashMap学习
imzoer的专栏
02-28 2万+
ConcurrentHashMap融合了hashtable和hashmap二者的优势。 hashtable是做了同步的,hashmap未考虑同步。所以hashmap在单线程情况下效率较高。hashtable在的多线程情况下,同步操作能保证程序执行的正确性。 但是hashtable每次同步执行的时候都要锁住整个结构。看下图: 图左侧清晰的标注出来,lock每次都要锁住整个结构。 Conc
ConcurrentHashMapHashtable区别
weixin_43375482的博客
04-02 1823
ConcurrentHashMapHashtable区别 主要体现在实现线程安全的方式上不同。 1.底层数据结构: JDK1.7的ConcurrentHashMap 底层采用的是分段的数组和链表实现,JDK1.8之后数据 +链表/红黑二叉数。 Hashtable 底层采用的是数据+链表,数组是HashMap的主体,链表则是为了解决哈希冲突而存在的; 2.实现线程的安全方式: 在JDK1....
Java中,concurrentHashMapHashTable有什么区别
胡晗靓的Java学习博客
06-06 693
Java中,concurrentHashMapHashTable有什么区别
ConcurrentHashMapHashTable什么区别
最新发布
qq_30939943的博客
09-06 918
ConcurrentHashMapHashTable什么区别
CocurrentHashMap和Hashtable区别
热门推荐
kobejayandy的专栏
11-19 1万+
集合类是Java API的核心,但是我觉得要用好它们是一种艺术。我总结了一些个人的经验,譬如使用ArrayList能够提高性能,而不再需要过时的Vector了,等等。JDK 1.5引入了一些好用的并发集合类,它们对于大型的、要求低延迟的电子商务系统来说非常的有用。这篇文章中将会看看ConcurrentHashMapHashtable之间的区别。 这篇文章是HashMap的工作原理以及Has
HashtableConcurrentHashMap区别
pi88_dian88的专栏
01-22 263
相同点: HashtableConcurrentHashMap都是线程安全的,可以在多线程环境中运行; key跟value都不能是null 区别: 两者主要是性能上的差异,Hashtable的所有操作都会锁住整个对象,虽然能够保证线程安全,但是性能较差; ConcurrentHashMap内部使用Segment数组,每个Segment类似于Hashtable,在“写”线程或者部分特殊的“...
ConcurrentHashMapHashtable区别?
一刀的博客
08-31 742
ConcurrentHashMapHashtable区别? ConcurrentHashMapHashtable区别主要体现在实现线程安全的方式上不同。 1 底层数据结构: JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构跟 HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 J...
ConcurrentHashMapHashtable区别
weixin_34166472的博客
10-09 120
Hashtable:synchronized是针对整张Hash表的,即每次锁住整张表让线程独占安全的背后是巨大的浪费 ConcurrentHashMapHashtable主要区别就是围绕着锁的粒度以及如何锁                         左边便是Hashtable的实现方式---锁整个hash表;而右边则是ConcurrentHashMap的实现方式---锁桶(或段)。C...
ConcurrentHashMapHashTable区别
08-17
ConcurrentHashMapHashTable都是用于实现线程安全的哈希表数据结构,但它们在实现上有一些区别。 1. 线程安全性:ConcurrentHashMap使用了分段锁(Segment)的机制,不同的分段可以被不同的线程同时访问,因此在...
ConcurrentHashMapHashtable区别
09-15
ConcurrentHashMapHashtable都是线程安全的Map实现,但它们之间有几个重要的区别。 首先,底层数据结构不同。Hashtable使用数组和链表的组合来存储数据,而ConcurrentHashMap使用了一种叫做分段锁(Segment)的...
ConcurrentHashMapHashtable区别
06-13
ConcurrentHashMapHashtable 都是 Java 中的线程安全的哈希表实现,它们有以下区别: 1. 线程安全方式不同:Hashtable 是通过对整个哈希表进行加锁来实现线程安全的,而 ConcurrentHashMap 则是采用了分段锁...
21-ConcurrentHashMapHashtable区别
09-16
ConcurrentHashMapHashtable是两种线程安全的哈希表,它们之间有几个区别。首先,ConcurrentHashMap在多线程环境下提供了更好的性能和可靠性,而Hashtable则使用了同步方法来实现线程安全,性能相对较低。其次,...
ConcurrentHashMapHashTable区别
qq_38197844的博客
10-11 564
ConcurrentHashMapHashTable区别主要体现在实现线程安全的方式上的不同: 底层数据结构:jdk7之前的ConcurrentHashMap底层采用的是分段的数组+链表实现,jdk8之后采用的是数组+链表/红黑树;HashTable采用的是数组+链表,数组是主体,链表是解决hash冲突存在的。 实现线程安全的方式:1. jdk8以前,ConcurrentHashMap采用分段锁,对整个数组进行了分段分割,每一把锁只锁容器里的一部分数据,多线程访问不同数据段里的数据,就不会存在锁竞争
ConcurrentHashMapHashtable区别
zhugedali_的博客
07-29 753
总的来说,ConcurrentHashMap 在大多数高并发场景下是更优的选择,因为它提供了更好的并发性能和更灵活的线程安全控制,同时对空值的支持也增加了一定的使用便利性。- 由于 ConcurrentHashMap 的分段锁机制,在并发环境下,其读操作几乎可以并发进行,只有在写操作涉及到同一分段时才会阻塞,因此其并发读的性能非常出色。- ConcurrentHashMap 在进行扩容时,不会像 Hashtable 那样一次性完成,而是逐步进行,以减少在扩容过程中的性能开销和锁竞争。
ConcurrentHashMapHashTable区别
ruky36的专栏
07-14 508
由于 `Hashtable` 性能较差且不允许 `null` 值,通常推荐使用 `ConcurrentHashMap` 或 `Collections.synchronizedMap` 包装一个 `HashMap` 来提供线程安全性。- `Hashtable` 不允许键或值为 `null`,如果尝试插入 `null` 值,会抛出 `NullPointerException`。- `Hashtable` 的迭代器不是弱一致的,对 `Hashtable` 的迭代需要进行外部同步。
Hashtable vs ConcurrentHashMap
batman
02-10 650
Hashtable 对比 ConcurrentHashMap HashtableConcurrentHashMap 都是线程安全的 Map 集合 Hashtable 并发度低,整个 Hashtable 对应一把锁,同一时刻,只能有一个线程操作它 ConcurrentHashMap 并发度高,整个 ConcurrentHashMap 对应多把锁,只要线程访问的是不同锁,那么不会冲突 ConcurrentHashMap 1.7** 数据结构:Segment(大数组) + HashEntry(小数组.
ConcurrentHashMapHashtable区别
weixin_44050355的博客
04-05 487
HashtableConcurrentHashMap有什么分别呢?它们都可以用于多线程的环境,但是当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。因为ConcurrentHashMap引入了分割(segmentation),不论它变得多么大,仅仅需要锁定map的某个部分,而其它的线程不需要等到迭代完成才能访问map。简而言之,在迭代的过程中,Concu...
写文章

热门文章

  • ConcurrentHashMap和HashTable区别?(超详细解释) 1805
  • 基于SpringBoot的大学生就业管理系统(内附源码,数据库,讲解等) 1743
  • 基于SpringBoot的图书馆推荐系统(数据收集与处理、推荐算法、部署) 1642
  • 基于springboot的电影院管理系统(内附讲解视频,数据库,源码等) 1515
  • 基于springboot的网上购物商城系统(内附源码,数据库,讲解等) 1402

最新评论

  • 月度员工绩效考核管理系统毕业设计(源码+数据库+讲解等)

    安皮卡: 大佬分享一下源码表情包

  • ConcurrentHashMap和HashTable区别?(超详细解释)

    m0_64812693: 讲的真的太好了

  • 基于Springboot的师生健康信息管理系统(内附讲解、源码、演示视频等)

    爱学习it小白白: 递归、搜索与回溯】综合练习二

  • 基于SpringBoot的大学生就业管理系统(内附源码,数据库,讲解等)

    鲸慕: 我想看看参考文献

  • 基于SpringBoot的大学生就业管理系统(内附源码,数据库,讲解等)

    鲸慕: 有文档没

最新文章

  • 基于springboot的宿舍管理系统(源码+lw+数据库+部署文档+讲解等)
  • 使用JDBC连接数据库(IDEA)
  • 反射是什么?-reflection
2024年16篇

目录

目录

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

玻璃钢生产厂家河南肖像玻璃钢卡通雕塑公司松江区玻璃钢雕塑厂重庆玻璃钢公园雕塑贵阳多彩玻璃钢雕塑销售电话玻璃钢美陈雕塑设计蜘蛛秒收录玻璃钢雕塑保定玻璃钢农耕雕塑长沙动漫玻璃钢雕塑公司商场美陈热气球香港玻璃钢人物雕塑河南景观玻璃钢雕塑厂家如何选择商场美陈玻璃钢动物雕塑壁厚一般多少商场玻璃钢花盆费用岑溪玻璃钢雕塑四平玻璃钢卡通雕塑济南玻璃钢仿真雕塑福建玻璃钢雕塑厂商云南景观雕塑玻璃钢雕塑品牌玻璃钢雕塑销售厂家北京雕塑玻璃钢创意玻璃钢雕塑价位玻璃钢雕塑上色怎么进行商场室内美陈合同辽宁商场主题创意商业美陈报价商场气球美陈布置郑州水果玻璃钢雕塑定制透光玻璃钢雕塑制作酒店玻璃钢雕塑制作绍兴公园玻璃钢雕塑哪家便宜香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化