java基础之Map集合

2023-11-15


java Map集合又很多种,很多人java开发几年,却不知道这几种常见的Map有什么区别 。大部分人开发存放key-value键值对数据类型集合容器, 第一想到HashMap集合, 其他Map到没有怎么使用过,甚者可能连HashMap的实现原理也都不清楚。
发现问题查找博客,发现大部分的博客知识点错误百出 简直误人子弟,当然 我早些年写的博客 甚至现在可能写的博客知识也有这种情况,如果有读者指出错误,我很乐意和读者探讨。
本篇文章Map基于openj9.1.8

HashMap

数据结构

JDK1.8之前HashMap由数组+链表组成 ,JDK1.8以在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转 化为红黑树,以减少搜索时间

链表长度不超过8
在这里插入图片描述
链表长度超出8 且HashMap数组长度超出64(默认是64)
在这里插入图片描述

HashMap数据存放过程

HashMap put的流程 map.put(key,value);

  1. 先根据key 找到key的Node hash 值 int hashCode = hash(key); (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
  2. 根据hash和hashMap数组长度计算对应的数组下标 (n - 1) & hash n为数组长度
    所以 当hashMap数组扩容后 key的数组下标值会变动
  3. 该数组索引对应的桶有没有数据,没数据 new Node 放入数组下标内。 到这里跳到第6步。
  4. 数组索引对应的桶有数据,判断数据存放类型 如果是自平衡的排序二叉树 new TreeNode insert数据到红黑树。到这里跳到第6步。
  5. 数据索引对应的桶 数据类型为Node ,遍历链表节点 如果与key hash值相等 覆盖该节点 ,否则 在链表最后面添加 新节点 Node ,链表长度+1 判断修改后的链表长度 如果超出8 链表改为红黑树。到这里跳到第6步。
  6. 判断hashMap 散列表的装填因子 ,如果装填因子超出0.75 (默认值0.75),给数组扩容 把HashMap集合数据重新组装存放到新的数组内。

在这里插入图片描述

HashMap 线程安全问题

与大多数集合类一样,此类不同步。可以使用 Collections.synchronizedMap 方法构造同步的HashMap。Map m = Collections.synchronizedMap(new HashMap(...)); ,为什么说HashMap是线程不安全的呢?

线程不安全的情况,多个线程给map put数据

  1. 两个线程(线程A 和线程B)key 计算的数组索引下标相同 都是2
  2. . 线程A 优先获取时间调度 判断出 数组索引2 的位置没有数据 new 出一个Node 名叫nodeA ,
  3. 紧接着线程B 获取了资源, 线程A挂起 。 判断数组索引2位置没有数据也 new 一个Node 叫nodeB, 线程B准备存放数据时 ,线程A 获取资源 把nodeA 放入 索引2 内, 结束put工作 ,接着线程B获取资源 把nodeB放入数组内 。这样线程A存放的数据被覆盖get线程A的key 返回为null。

图文表示:

线程A 找到下标2的位置,看没有值 创建一个nodeA
在这里插入图片描述
线程A被挂起,线程B 查询 下标2 没有数据,创建节点 NodeB
在这里插入图片描述
线程A获取资源,直接把nodeA放入桶内
在这里插入图片描述
线程B ,一直认为索引2的桶没有数据 ,忽略已有的链表数据 直接把nodeB节点覆盖放入桶内
在这里插入图片描述

多线程不安全案例

案例测试代码

public class HashMapTest {

    static HashMap map = new HashMap();
    public static void main(String[] args) {

        new Thread(() -> {
            System.out.println("线程1 执行");
            for (int i = 0; i < 8000; i++) {
                map.put(i,i);
            }
            System.out.println("线程1---------"+map.get(500));
        }).start();

        new Thread(() -> {
            System.out.println("线程2 执行");
            for (int i = 8000; i < 16000; i++) {
                map.put(i,i);
            }
            System.out.println("线程2---------"+map.get(1500));
        }).start();

    }


}

多次执行结果

线程1 执行
线程2 执行
线程2---------null
线程1---------null



线程1 执行
线程2 执行
线程1---------500
线程2---------1500



线程1 执行
线程2 执行
线程1---------null
线程2---------1500


线程1 执行
线程2 执行
线程2---------1500
线程1---------null

线程1 执行
线程2 执行
线程1---------500
线程2---------null

Collections.synchronizedMap(new HashMap(…));保证Map安全

public class HashMapTest {

    public static void main(String[] args) {

        Map map = Collections.synchronizedMap(new HashMap());
        new Thread(() -> {
            System.out.println("线程1 执行");
            for (int i = 0; i < 80000; i++) {
                map.put(i,i);
            }
            System.out.println("线程1---------"+ map.get(500));
        }).start();

        new Thread(() -> {
            System.out.println("线程2 执行");
            for (int i = 80000; i < 160000; i++) {
                map.put(i,i);
            }
            System.out.println("线程2---------"+ map.get(80020));
        }).start();

    }


}

HashTable

和hashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射。

数据结构

在这里插入图片描述

数据存放过程

和hashMap 差不多相同 但是没有红黑树 ,具体的看代码。

线程安全问题

HashTable 内的方法是同步的 synchronized ,当多个线程同时put 或remove 时
多线程案例,把创建的HashTable对象 加锁,缺点:没加锁之前多个线程 put key的数组下标索引不一样时,同时往数组添加数据没有影响 ,整个对象加锁后 要等上个线程释放锁后才能操作put

hashTable锁加的只加


public class HashTableTest {

    static  Hashtable<Integer,Integer> map = new Hashtable();

    public static void main(String[] args) {



        new Thread(() -> {
            System.out.println("线程1 执行");
            for (int i = 0; i < 8000; i++) {
                map.put(i,i);
            }
            System.out.println("线程1---------"+map.get(500));
        }).start();

        new Thread(() -> {
            System.out.println("线程2 执行");
            for (int i = 8000; i < 16000; i++) {
                map.put(i,i);
            }
            System.out.println("线程2---------"+map.get(1500));
        }).start();


    }


}

执行结果
线程相对安全的

线程1 执行
线程2 执行
线程2---------1500
线程1---------500

ConcurrentHashMap

jdk1.6版本:

1.默认情况下会有16个区段 Segment数组 Segment[16]

2.每次每个区段Segment中会保存若干个散列桶,每次散列桶长度扩容成2^n次方的长度。 多个散列桶相连就构成了散列表。

3.存入元素: key带入到hashcode方法众获得hashcode值,然后把hashcode值带入到散列算法中获取segment的下标(区段编号),再根据key带入到定义好的函数中获取Segment对象中散列桶的下标。

如果此位置有元素就构成链表(JDK1.8及以后会形成红黑树),如果没有元素就存入

3.存取的线程安全问题: 如果多个线程操作同一个Segment,则某个线程给此Segment加锁,另一个线程只能阻塞。

同时解决了HashTable的问题,HashTable只能由一个线程操作。 ConcurrentHashMap可以让一个线程操作第一个Segment,另一个线程操作另一个Segment。

4.小矩形块表示散列桶

绿色的Segment表示ConcurrentHashMap集合众 Segment[16]数组里的一个对象。

5.并发问题:

两个线程给不同的区段Segment中添加元素,这种情况可以并发。 所以ConcurrentHashMap可以保证线程安全(多个线程操作同一个Segment,则某个线程给此Segment加锁,另一个线程只能阻塞)并且在一定程度上提交线程并发执行效率。

两个线程给同一个区段Segment中添加元素,这种情况不可以并发, 这样JDK1.8进行了改进:

没有区段了,和HashMap一致了,数组+链表+红黑树 +乐观锁 + synchronized

8及以后

省略 下次补上

WeakHashMap

与大多数集合类一样,此类不同步。可以使用 Collections.synchronizedMap 方法构造同步的 WeakHashMap。

WeakHashMap.Entry 和 HashMap.Node 的不同点在于,WeakHashMap.Entry 继承了WeakReference。
弱引用的生存期特别短。https://blog.csdn.net/ChineseSoftware/article/details/119212399的时候,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。
想象一下如下场景:

  1. 调用两次 size():第一次为 10,第二次就为 8 了。
  2. 两次调用 isEmpty():第一次返回 false,第二次返回 true。
  3. 两次调用 containsKey():第一次返回 true,第二次返回 false。
  4. 两次调用 get():第一次返回一个 value,第二次返回 null。
    在如今的并发泛滥的大环境下,大家应该都用过缓存,缓存都是放在内存中的,而内存几乎是计算机中最宝贵也是最稀缺的资源,所以需要谨慎的使用,不然很容易就出现 https://blog.csdn.net/ChineseSoftware/article/details/119212481。缓存的主要作用是为了更快的处理业务、降低服务器的压力,那么就要保证缓存命中率,这里假设整个缓存是一个 key-value 结构的(以键值对缓存为例),HashMap 作为强引用对象在没有主动将 key 删除时是不会被 JVM 回收的,这样 HashMap 中的对象就会越积越多直到 OOM 错误;那么如何做到既让缓存的命中率高又不占用那么多的内存,这里就可以采用 WeakHashMap,当然不会有 HashMap 100% 的命中率(假设内存足够),但是在保证程序正常的前提下更好的实现了缓存这套解决方案。

WeakHashMap使用了软引用结构,它的对象在垃圾回收时会被删除
注:垃圾回收是优先级非常低的线程,不能被显示调用,当内存不足的时候会启用
下面是 WeakHashMap 的实现原理拆分:

public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
    ... ...
    // 用于存储需要清理的引用对象
    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
    ... ...
    // 内部Entry继承自WeakReference,从而有弱引用特性
    private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        ... ...
    }
    ... ...
    // 用于移除内部不用的Entry来释放内存
    private void expungeStaleEntries() { ... ... }
    ... ...
}

WeakHashMap 原理说明
1.每次GC清理对象后,引用对象被放置到 ReferenceQueue 之中
2.每次访问 WeakHashMap 都会调用 expungeStaleEntries 遍历删除 ReferenceQueue 中引用对象

1、缓存中使用
由于 WeakHashMap 是弱引用,因此适合在缓存中使用,当内存不足GC的时候,会清理不用的引用达到释放内存的目的

2、不要使用基础类型作为WeakHashMap的key

我大概理解的是,基础类型的一定范围不会被回收
原文:objectMap.put方法执行的时候i会被封装为Integer类型的,Integer保留了-128到127的缓存。但是对于int来说范围大很多,因此那些Key <= 127的Entry将不会进行自动回收,但是那些大于127的将会被回收,因此最后的尺寸总是会稳定在128左右

key 设置从0 开始 ,一直循环发现 0至128 的key value 没有被清理

    public static void main(String[] args) {
        WeakHashMap map = new WeakHashMap();
        map.put(0,0);
        for (int i = 0; map.size() == 0; i++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(i< 2000){
                map.put(i,i);
            }
            System.out.println("i 值为:  "+i);
            System.out.println("map size 为:  "+map.size());
            if(map.size() == 0){
                System.out.println("map size 为  " + 0);
                break;
            }
        }
    }

int 类型改为大于 128 gc自动清除

public class WeakHashMapTest {
    public static void main(String[] args) {
        WeakHashMap map = new WeakHashMap();
        map.put(3000,3000);
        for (int i = 129; map.size() != 0; i++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(i< 2000){
                map.put(i,i);
            }
            System.out.println("i 值为:  "+i);
            System.out.println("map size 为:  "+map.size());
            if(map.size() == 0){
                System.out.println("map size 为  " + 0);
                break;
            }
        }
    }
}

在检测到适当的可达性更改后,垃圾收集器会将注册的引用对象附加到该队列中。

byte[] key = new byte[1024*10];
WeakReference<byte[]> reference = new WeakReference<>(key,queue);

//当垃圾回收之后实际上会将reference对象放进引用队列中。
//而key就是也就是byte数组对象回收掉。通常引用队列就是作为一个通知的信号,表明这个对象被回收掉了。

验证代码 这里设置堆的大小为-Xmx20m (20m)

@Test
public void test() throws InterruptedException {
ReferenceQueue queue = new ReferenceQueue<>();

new Thread(()->{
    HashMap<Object,Object> map = new HashMap<>();
    for(int i=0;i<100;i++){
        WeakReference<byte[]> reference = new WeakReference<>(new byte[1024*100],queue);
        map.put(reference,"a");
    }
    System.out.println(map.size());
}).start();

new Thread(()->{
    int cnt = 0;
    WeakReference<byte[]> k;
    try {
    	//ReferenceQueue.remove是阻塞的。poll()方法是不阻塞的。
        while((k = (WeakReference) queue.remove()) != null) {
            System.out.println("byte对象地址" + k.get());
            System.out.println("WeakReference的地址" + k);
        }
    }catch (Exception e){

    }
}).start();
Thread.sleep(2000);

}

WeakReference的地址java.lang.ref.WeakReference@6930026c
byte对象地址null
WeakReference的地址java.lang.ref.WeakReference@561e343
byte对象地址null

map.size()=100

实际上只有map数组中key对象中的byte数组被回收掉了。

IdentityHashMap

IdentityHashMap数据结构

在这里插入图片描述

IdentityHashMap数据存放过程

在这里插入图片描述
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java基础之Map集合 的相关文章

  • 在 JTable 中移动行

    我使用 MVC 模式 并且有一个如下所示的 JTable List
  • 热重载在docker中运行的java程序

    我开发了一个java程序 应该在docker中运行 然而 我在调试docker中运行的java程序时遇到了很多痛苦 我在网上搜索 一些教程提出了像 spring dev tools 这样的工具 因为我的java程序是基于spring boo
  • 如何在 JFace 的 TableViewer 中创建复选框?

    我创建了一个包含两列的 tableViewer 我想将其中一列设为复选框 为此 我创建了一个 CheckBoxCellEditor 但我不知道为什么它不起作用 名为 tableName 的列显示其值正常 色谱柱规格如下 String COL
  • Java程序中的数组奇怪的行为[重复]

    这个问题在这里已经有答案了 我遇到了这个 Java 程序及其以意想不到的方式运行 以下程序计算 int 数组中元素对之间的差异 import java util public class SetTest public static void
  • CXF Swagger2功能添加安全定义

    我想使用 org apache cxf jaxrs swagger Swagger2Feature 将安全定义添加到我的其余服务中 但是我看不到任何相关方法或任何有关如何执行此操作的资源 下面是我想使用 swagger2feature 生成
  • java.io.IOException: %1 不是有效的 Win32 应用程序

    我正在尝试对 XML 文档进行数字签名 为此我有两个选择 有一个由爱沙尼亚认证中心为程序员创建的库 还有一个由银行制作的运行 Java 代码的脚本 如果使用官方 认证中心 库 那么一切都会像魅力一样进行一些调整 但是当涉及到银行脚本时 它会
  • 如何在 Java 中禁用 System.out 以提高速度

    我正在用 Java 编写一个模拟重力的程序 其中有一堆日志语句 到 System out 我的程序运行速度非常慢 我认为日志记录可能是部分原因 有什么方法可以禁用 System out 以便我的程序在打印时不会变慢 或者我是否必须手动检查并
  • 一种使用 Java Robot API 和 Selenium WebDriver by Java 进行文件上传的解决方案

    我看到很多人在使用 Selenium WebDriver 的测试环境中上传文件时遇到问题 我使用 selenium WebDriver 和 java 也遇到了同样的问题 我终于找到了解决方案 所以我将其发布在这里希望对其他人有所帮助 当我需
  • jdbc4.MySQLSyntaxErrorException:数据库中不存在表

    我正在使用 SpringBoot 开发一个网络应用程序 这是我的application properties文件来指定访问数据库的凭据 spring datasource driverClassName com mysql jdbc Dri
  • Prim 的迷宫生成算法:获取相邻单元格

    我基于 Prim 算法编写了一个迷宫生成器程序 该算法是 Prim 算法的随机版本 从充满墙壁的网格开始 选择一个单元格 将其标记为迷宫的一部分 将单元格的墙壁添加到墙壁列表中 While there are walls in the li
  • 无法理解 Java 地图条目集

    我正在看一个 java 刽子手游戏 https github com leleah EvilHangman blob master EvilHangman java https github com leleah EvilHangman b
  • Clip 在 Java 中播放 WAV 文件时出现严重延迟

    我编写了一段代码来读取 WAV 文件 大小约为 80 mb 并播放该文件 问题是声音播放效果很差 极度滞后 你能告诉我有什么问题吗 这是我的代码 我称之为doPlayJframe 构造函数内的函数 private void doPlay f
  • 序列化对象以进行单元测试

    假设在单元测试中我需要一个对象 其中所有 50 个字段都设置了一些值 我不想手动设置所有这些字段 因为这需要时间而且很烦人 不知何故 我需要获得一个实例 其中所有字段都由一些非空值初始化 我有一个想法 如果我要调试一些代码 在某个时候我会得
  • Spring Data 与 Spring Data JPA 与 JdbcTemplate

    我有信心Spring Data and Spring Data JPA指的是相同的 但后来我在 youtube 上观看了一个关于他正在使用JdbcTemplate在那篇教程中 所以我在那里感到困惑 我想澄清一下两者之间有什么区别Spring
  • 检查 protobuf 消息 - 如何按名称获取字段值?

    我似乎无法找到一种方法来验证 protobuf 消息中字段的值 而无需显式调用其 getter 我看到周围的例子使用Descriptors FieldDescriptor实例到达消息映射内部 但它们要么基于迭代器 要么由字段号驱动 一旦我有
  • 使用 AWS Java SDK 为现有 S3 对象设置 Expires 标头

    我正在更新 Amazon S3 存储桶中的现有对象以设置一些元数据 我想设置 HTTPExpires每个对象的标头以更好地处理 HTTP 1 0 客户端 我们正在使用AWS Java SDK http aws amazon com sdkf
  • org.jdesktop.application 包不存在

    几天以来我一直在构建一个 Java 桌面应用程序 一切都很顺利 但是今天 当我打开Netbeans并编译文件时 出现以下编译错误 Compiling 9 source files to C Documents and Settings Ad
  • Springs 元素“beans”不能具有字符 [children],因为该类型的内容类型是仅元素

    我在 stackoverflow 中搜索了一些页面来解决这个问题 确实遵循了一些正确的答案 但不起作用 我是春天的新人 对不起 这是我的调度程序 servlet
  • 查看Jasper报告执行的SQL

    运行 Jasper 报表 其中 SQL 嵌入到报表文件 jrxml 中 时 是否可以看到执行的 SQL 理想情况下 我还想查看替换每个 P 占位符的值 Cheers Don JasperReports 使用 Jakarta Commons
  • java迭代器内部是如何工作的? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有一个员工列表 List

随机推荐

  • 基础css-flex布局基础属性

    1 flex布局 弹性布局 伸缩布局 设置当前盒子为弹性盒子 display flex 设置主轴方向的对齐方式 justify content justify content center 设置侧轴方向的对齐方式 align items a
  • replace(),IndexOf(),substring() ,lastIndexOf() ,split() ,pollFirst() ,pollFirst()

    replace pattern replacement 使用replacement替换pattern 如果pattern是字符串只替换第一个匹配项 如果pattern是正则表达式则替换每次匹配都要调用的回调函数 实例 String a 1
  • win10 系统开启自带热点,手机无法连接(连接超时)

    win10开始自带热点 手机成功连接 颇费周折 所以在此记录一下 也给其他人一个参考 今天想在win10上安装个WIFI软件 好让手机连接 结果无意间发现win10自带了热点功能 于是赶紧打开 手机的WIFI列表也显示出来了 本以为就这样愉
  • 国际MES供应商与产品大全

    最近花了一点时间 将国际上知名的MES厂商和其产品整理了一下 如下 ABB ABB 苏黎世 瑞士 是一个240亿美元的自动化和电力技术的全球业务的供应商 它的制造软件利用其控制及自动化产品通用解决方案的一部分 其系统800xa控制体系结构
  • Android 自定义播放暂停按钮图片

    Android 自定义播放暂停按钮图片 在开发Android应用程序时 我们经常需要使用播放暂停按钮来控制媒体播放器的状态 虽然Android提供了默认的播放暂停按钮 但有时候我们希望根据设计需求自定义这些按钮的外观 在本文中 我将详细介绍
  • 嵌入式毕设分享 自动晾衣架设计与实现(源码+硬件+论文)

    文章目录 0 前言 1 主要功能 2 硬件设计 原理图 3 核心软件设计 4 实现效果 5 最后 0 前言 这两年开始毕业设计和毕业答辩的要求和难度不断提升 传统的毕设题目缺少创新和亮点 往往达不到毕业答辩的要求 这两年不断有学弟学妹告诉学
  • 解决tmux启动「can't create socket」的问题

    Tmux是终端重度用户的好帮手 你再也不用担心以下问题 任务运行一半要下班 发现忘记使用nohup 网络不稳定 终端会掉线 有人找我开会 切换有线 无线会掉线 我入职以后 一直在开发机里面使用tmux和screen 在我发现tmux之前 最
  • Java的循环

    目录 1 while循环 2 do while循环 3 for 循环 1 while循环 while循环结构 语法结构 初始值 while 条件 循环操作代码块 迭代代码 执行规律 1 首先执行一次初始值 2 然后判断条件 如果条件为tru
  • ESP32的WIFI的STA模式&调控ESP32蓝牙和WIFI发射功率

    以下相关API接口的定义可进入l乐鑫官方查看 Wi Fi 库 ESP32 ESP IDF 编程指南 v4 4 文档 STA模式配置过程 include
  • springboot 获取当前日期_Spring Boot获取时间

    运行环境新建测试类 package com wusiyao websockets service import org springframework stereotype Service import java text SimpleDa
  • 【Linux后端服务器开发】常用开发工具

    目录 一 apt yum 二 gcc g 三 make makefile 四 vi vim 五 gdb 一 apt yum apt 和 yum 都是在Linux环境下的软件包管理器 负责软件的查找 安装 更新与卸载 apt 是Ubuntu系
  • 小程序经典案例

    1 上拉触底事件 data colorList isloding false getColors this setData isloding true 需要展示 loading 效果 wx showLoading title 数据加载中 w
  • Java学习笔记 03

    相关文章 Java学习笔记 01 概论 Java学习笔记 02 快速之旅 Java环境配置及HelloWorld程序 引言 写这篇文章 主要是为了以后能快速复习Java的基础语法 同时 帮助有C 等语言基础的同学快速入门Java 目录 一
  • SmartFusion从FPGA到ARM系列教程

    前言 本系列教程 将会以Microsemi SmartFusion一代芯片A2F200M3F为例 简单介绍片上ARM Cortex M3 硬核 MCU 基本外设的使用 及其与FPGA逻辑模块进行交互的示例 在学习片上硬核ARM Cortex
  • 网上阅卷系统php源码,又开源了,网上阅卷系统自动识别功能代码

    想让自己轻松点就要让计算机多为你做点 前几天一个朋友找到我让我做一个网上阅卷系统 就是实现这么几个功能 高速扫描仪扫描试卷后得到一张一张的图片 软件的功能就是处理图片 计算成绩 再详细点就是自动识别考生涂的学号 自动识别考生的选择题答案并记
  • css设计引言,HTML5与CSS3设计模式 引言(3)

    引言 3 2 代码清单2 浮动下沉首字示例 HTML pclass hanging indent spanclass hanging dropcap H span anging Dropcap p CSS hanging indent pa
  • 2022-渗透测试-git提权(Linux)

    目录 1 什么是提权 2 git提权命令 3 git的使用 1 什么是提权 提权就是通过各种办法和漏洞 提高自己在服务器中的权限 以便控制全局 利用漏洞的最终目的是获取被测系统的最高权限 即Windows中管理员账户的权限 或Linux中r
  • C++自定义connect超时时间——非阻塞套接字法

    一 代码 include
  • 深圳大学数据库系统实验 Leasing Luxury Database system 基于PHP,MySQL,Web三件套

    本实验要求搭建一个手袋租聘的数据库系统 并实现以下要求 创建一个数据库 可以记录客户数据 手袋数据 租聘数据 设计者数据 用户可以提供自己的邮箱地址 邮寄地址 信用卡号码 来注册租聘网站 数据库要展示所有课租聘的手袋 已被租聘的手袋用户不能
  • java基础之Map集合

    Map集合 HashMap 数据结构 HashMap数据存放过程 HashMap 线程安全问题 多线程不安全案例 Collections synchronizedMap new HashMap 保证Map安全 HashTable 数据结构