ArrayList源码全面解析

news2024/9/24 13:20:33

在这里插入图片描述

一、概述

ArrayList 是 java 集合框架中比较常用的数据结构,继承自 AbstractList,实现了 List 接口。底层采用数组来实现。ArrayList 实现了java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。

1.1、底层数据结构

底层采用数组进行数据存储,相当于动态数组。

1.2、特点
  • 动态大小:ArrayList的大小是动态的,可以在运行时添加或删除元素。这意味着您不需要预先确定列表的大小。

  • 数组实现:ArrayList基于数组实现,这使得它具有高效的内部数据结构,可以快速随机访问列表中的元素。但是,如果你需要从列表的中间位置插入或删除元素,这可能会涉及到数组元素的移动,所以可能相对较慢。

  • 可修改:ArrayList是可修改的。这意味着您可以在列表中更改、添加或删除元素。

  • 线程不安全的:ArrayList不是线程安全的。这意味着如果在多个线程同时修改ArrayList时,可能会导致数据的不一致性。如果需要线程安全,可以使用同步块或者考虑使用线程安全的集合类,如CopyOnWriteArrayList。

  • 易于使用:ArrayList提供了许多有用的方法,如add(), remove(), get()等,可以方便地操作列表。同时,它也支持迭代器,可以进行遍历操作。

  • 性能:由于ArrayList内部使用数组进行实现,所以在知道索引的情况下进行读取或写入操作的速度非常快,时间复杂度为O(1)。但是,如果需要从中间位置进行插入或删除操作,那么需要移动数组中的元素,所以时间复杂度为O(n)。

1.3、ArrayList类图

在这里插入图片描述

由源码可得知:
在这里插入图片描述

ArrayList继承了抽象类AbstractList,并实现了ListRandomAccessCloneableSerializable等接口。

在这里插入图片描述

在这里插入图片描述

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

抽象类AbstractList继承了抽象类AbstractCollection,并实现了List接口抽象类AbstractCollection实现了Collection接口Collection接口继承了Iterable接口

  • 实现RandomAccess接口:表明ArrayList支持快速(通常是常量时间)的随机访问。

  • 实现了Cloneable接口,表明它支持克隆。可以调用clone()进行浅拷贝。

  • 实现了Serializable接口,表明它支持序列化。

二、源码解析

2.1、属性解析
    /**
     * 默认的容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 定义了一个空的数组,用于在用户初始化代码的时候传入容量为0时使用
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 定义了一个空数组,用于在默认构造器中,赋值给顶级数组 elementData
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 底层数组,真正存储元素的地方
     */
    transient Object[] elementData;  

    /**
     * 表示集合中ArrayList含有元素的个数
     */
    private int size;
    
    
    /**
     * 标记数组的最大容量
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
    
    /**
     * 记录对 List 操作的次数
     */
    protected transient int modCount = 0;


2.2、构造方法解析

ArrayList默认提供了三种构造器,分别是:

  • 无参构造器
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

解析:

当我们使用无参构造器时,将DEFAULTCAPACITY_EMPTY_ELEMENTDATA变量赋值给elementData,即把elementData初始化为一个空数组。

注意:在这里没有对数组容量进行分配大小;具体给数组分配容量大小是第一次增加元素时才分配的。

  • 指定初始容量的构造器
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

解析:

当传入的容量大小大于0,elementData 赋值为一个容量指定容量为initialCapacity的数组。

当传入的容量等于0,elementData 赋值为 EMPTY_ELEMENTDATA。

其他情况下,抛出异常!

注意:

对比无参构造,与此时的initialCapacity传入0,两种情况有什么不同呢,又有什么作用呢?

// 无参构造
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

// initialCapacity传入0时的赋值
this.elementData = EMPTY_ELEMENTDATA;

这两个变量的作用:用于区分是通过哪个构造函数来进行初始化的。

  • 提供一个Collection集合的构造器
 public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

解析:

将传入的集合转化为数组,赋值给elementData,并计算集合的大小size;

若 size != 0,接着判断保证此刻数组elementData数组的类型和Object[]类型相同,若不同,则拷贝一个Object[]类型的数组赋值给elementData。

若size == 0,将 elementData 赋值为 EMPTY_ELEMENTDATA。

注:若传入的c为null,将会报空指针异常。

2.3、常用方法解析
add方法
// add方法
public boolean add(E e) {
        // 判断数组用不用扩容
        ensureCapacityInternal(size + 1);  
        elementData[size++] = e;
        return true;
}

// ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// calculateCapacity方法
 private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
}

// ensureExplicitCapacity方法
 private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
}

// grow方法
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
}

解析:

当我们调用add方法,首先调用ensureCapacityInternal()方法,传入当前元素的个数+1;紧接着调用calculateCapacity方法,进行容量的计算。

 private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
}

在calculateCapacity 方法中,有个判断逻辑:elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个也正是我们使用默认构造器创建的。若为默认的构造器创建的话,把minCapacity(也就是我们的size+1)和DEFAULT_CAPACITY(默认容量是10)比较,取较大者返回,否则的话,就返回minCapacity。

该方法的作用:通过无参构造创建的 ArrayList 在第一次 add 时进行初始容量的设置。因为只要不相等,返回的还是minCapacity,与传入的相同;

因此通过无参构造创建的ArrayList,第一次add的时候,创始容量的大小为DEFAULT_CAPACITY(默认容量是10)。

接下来调用ensureExplicitCapacity 方法:

 private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
}

进行判断:如果minCapacity大于底层数组的长度,则需要调用grow(minCapacity)方法进行扩容,否则的话,回到add()方法中,ensureCapacityInternal(size + 1)什么也不做。

过程解析

  • 当我们要 add 进第1个元素到 ArrayList 时,elementData.length 为0 (因为还是一个空的 list),因为执行了 ensureCapacityInternal() 方法 ,所以 minCapacity 此时为10。此时,minCapacity - elementData.length > 0 成立,所以会进入 grow(minCapacity) 方法。
  • 当add第2个元素时,minCapacity 为2,此时elementData.length(容量)在添加第一个元素后扩容成 10 了。此时,minCapacity - elementData.length > 0 不成立,所以不会进入 (执行)grow(minCapacity) 方法。
  • 添加第3、4···到第10个元素时,依然不会执行grow方法,数组容量都为10。

直到添加第11个元素,minCapacity(为11)比elementData.length(为10)要大;进入grow方法进行扩容。

接下来进入grow方法:

// grow方法
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
}

解析:

int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);

oldCapacity为旧容量, newCapacity是扩容后的容量,即oldCapacity + (oldCapacity >> 1),大概是oldCapacity的1.5倍。

oldCapacity为偶数就是1.5倍;否则是1.5倍左右,因为如果是奇数的话会丢掉小数。

接下来进行判断:

第一个判断:如果新容量小于我们设置的minCapacity的话,就把minCapacity赋值给newCapacity。

第二个判断:判断newCapacity是否大于MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8),(MAX_ARRAY_SIZE代表了所能设置的数组的最大容量,如果超过这个值,可能导致内存溢出的错误(OutOfMemoryError),当然,如果超过的话,会将数组的容量设置为INTEGER.MAX_VALUE,否则新容量大小则为 MAX_ARRAY_SIZE 即为 Integer.MAX_VALUE - 8)。

过程解析

  • 当add第1个元素时,oldCapacity 为0,经比较后第一个if判断成立,newCapacity 赋值为minCapacity(minCapacity = 10)。但是第二个if判断不会成立,即newCapacity 不比 MAX_ARRAY_SIZE大,则不会进入 hugeCapacity 方法。数组容量为10,add方法中 return true,size增为1。
  • 当add第11个元素进入grow方法时,newCapacity为15,比minCapacity(为11)大,第一个if判断不成立。新容量没有大于数组最大size,不会进入hugeCapacity方法。数组容量扩为15,add方法中return true,size增为11。
  • 以此类推… …

总结:

  • 如果 ArrayList 是通过无参构造创建的,那么初始化时并不会初始化数组的大小,只是把数组标记为通过无参构造初始化的;然后在第一次加入数据时,初始化数组大小为10。

  • 如果超过了最大容量则需要扩容,扩容后的数组大小大概为原数组的1.5倍;扩容需要判断是否超过允许的最大的长度,并做相关的处理。

addAll方法
public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
}

解析:

addAll方法与add方法基本相似

首先将传入的Collection转化为数组,然后将其容量numNew 加上ArrayList的大小,进行判断容量够不够,最后进行数组的拷贝,把传入的所有元素添加到底层数组elementData中,然后把ArrayList中元素个数的size重新计算。

最后如果传入的参数的元素不为空,则返回true,表示把元素添加了进去,如果为空的话,说明没有添加元素,则返回false。

获取ArrayList的大小
// 获取元素大小
public int size() {
    return size;
}

直接返回size即可,因为属性size代表了ArrayList的大小。

获取元素的位置

获取元素第一次出现的索引位置使用indexOf方法;获取元素最后一次出现的索引位置使用lastIndexOf方法。

indexOf方法

// 获取元素第一次出现的索引位置
public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

解析:

当参数是null的时候,循环遍历elementData 判断其位置,直接返回;

当参数不为null的时候,循环遍历elementData 判断其位置,直接返回;

如果没有找到则返回-1。

lastIndexOf方法

// 获取元素最后一次出现的索引位置
public int lastIndexOf(Object o) {
    if (o == null) {
        for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

解析:

与indexOf方法类似,只不过遍历elementData是从后向前遍历的。

判断是否包含某个元素contains
// 判断是否包含某个元素
public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

解析:

直接调用上面的indexof()方法即可,若该方法返回大于0,则存在该元素;否则不存在。

给某个位置设置元素
// 给某个位置设置元素
public E set(int index, E element) {
    // 检查索引位置是否越界
    rangeCheck(index);

    // 查看底层elementData数组的某个元素
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

解析:

首先检查,传入的索引位置是否越界,越界抛出IndexOutOfBoundsException异常,然后获取elementData数组该位置的元素,接着将新元素放入该位置,最后返回该位置的旧值。

在指定位置的地方添加新的元素
public void add(int index, E element) {
    // 范围检查
    rangeCheckForAdd(index);

    // 增加容量
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

解析:

首先检查一下参数索引位置是否符合,其次通过ensureCapacityInternal(size + 1)判断是否需要增加容量,如果需要则扩容,否则不需要则什么也不用做。

然后进行数组数组elementData元素的移动,最后把要添加的元素添加到指定的位置,然后进行元素个数size的累加。

移除指定位置的元素
// 移除指定位置的元素
public E remove(int index) {
    // 范围检查
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

解析:

private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

首先,rangeCheck方法对传入的索引参数范围检查,接着获取旧值赋值给变量oldValue;

在移动元素的时候,仍然选择进行数组的拷贝。首先计算需要移动的元素个数(size - index - 1);如果移动的元素的个数大于0,下面进行数组的拷贝(其实就是起到了移动元素的功能);否则的话直接在elementData[–size]处设置为空就可以了。记住最后需要返回移除了的值。

移除指定值的元素
// 移除指定值的元素
public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

// fastRemove方法
 private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

解析:

该方法对传入的参数进行是否为null的判断,两种情况下,对elementData数组进行循环遍历判断,是否与传入的值相等,相等的话调用fastRemove方法 进行元素的移除操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1263633.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从 Rust 程序员的早期使用印象看 Go

Go vs Rust 我在过去的几周开始使用 Go。这是我第一次在一个较大且严肃的项目中使用 Go。之前我对 Go 有过很多了解&#xff0c;并且在研究 Rust 的特性时&#xff0c;曾经使用例子和玩具程序。然而&#xff0c;真正的编程经验是完全不同的。 我认为写写我对它的印象会很有趣。…

Java(九)(多线程,线程安全,实现线程的方法,线程同步,线程池,并发和并行,线程的六种状态)

目录 多线程 线程 实现线程的方法 方法一:继承Thread父类 方法二:实现Runnable接口 方法三:Callable接口和FutureTask类来实现 Thread方法 线程安全 线程同步 同步代码块 同步方法 Lock锁 线程池 线程池对象的创建方式一: 线程池处理Runnable任务 线程池处理Cal…

办公软件定制开发在企业发展中的优势|app小程序搭建

办公软件定制开发在企业发展中的优势|app小程序搭建 如今&#xff0c;办公软件已经成为企业日常工作的必需品。很多企业为了提高工作效率和满足自身业务需要&#xff0c;选择定制开发办公软件。下面将介绍定制开发办公软件在企业发展中的优势。 1定制开发办公软件可以满足企业…

uni-app 微信小程序 pdf预览

<div click"getPDF">查看体检报告</div>getPDF() {uni.downloadFile({url: ${this.$baseURL}/file/download?id${this.pdfFileId},//编写在线的pdf地址success: function(res) {const filePath res.tempFilePath;uni.openDocument({filePath: filePath…

每日一练【移动零】

一、题目描述 283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 二、题目解析 可以…

Appium自动化如果出现报错怎么办?这么做确实解决问题

解决通过appium的inspector功能无法启动app的原因 在打开appium-desktop程序&#xff0c;点击inspector功能&#xff0c;填写app的配置信息&#xff0c;启动服务提示如下&#xff1a; 报错信息&#xff1a; An unknown server-side error occurred while processing the com…

数据结构(超详细讲解!!)第二十五节 树与森林

1.树的存储结构 和线性表一样&#xff0c;树可以用顺序和链式两种存储结构。 树的顺序存储结构适合树中结点比较“满”的情况。根据树的非线性结构特点&#xff0c;常用链式存储方式来表示树。树常用的存储方法有&#xff1a;双亲表示法、孩子表示法和孩子兄弟表…

Toast UI Editor上传图片到Flask

Toast UI Editor国内文档几乎搜不到&#xff0c;国外文档也写得不是特别项目&#xff0c;没有太多举例的demo。一开始选择使用这个就是因为UI好看。不过看看源码把思路滤清了。 他会给把图片转成Base64&#xff0c;到时候发表单直接丢过去就行了&#xff0c;blob这个参数能拿到…

05 Nacos实战:集成Nacos实现分布式配置中心实现配置动态刷新

上一节介绍了Nacos注册中心的功能,这节介绍下木谷博客中如何使用Nacos作为分布式配置中心。 在第二节搭建项目并运行中讲到创建mugu_nacos_config这个数据库,其中已经包含了木谷博客所需的全部配置,在nacos中也可以查看到,如下: 引入Nacos作为配置中心很简单,步骤如下:…

vue3中的customRef创建一个自定义的 ref对象

customRef 创建一个自定义的 ref&#xff0c;并对其依赖项跟踪和更新触发进行显式控制 小案例: 自定义 ref 实现 debounce <template><div style"font-size: 14px;"><input v-model"text" placeholder"搜索关键字"/><…

flask 上传文件

from flask import Flask, request, render_template,redirect, url_for from werkzeug.utils import secure_filename import os from flask import send_from_directory # send_from_directory可以从目录加载文件app Flask(__name__)#UPLOAD_FOLDER media # 注意&#xff…

【TiDB】TiDB离线方式部署

目录 1 下载TiDB离线组件包 2 安装TiUP 3 合并离线包 4 TIDB 软件和硬件环境建议配置 5 TiDB环境与系统配置检查 6 生成集群初始化配置文件模板 7 执行部署命令 1 检查就能存在的潜在风险 2 手动修复风险 3 部署 TiDB 集群 8 查看TIUP管理的集群情况 9 检查部署的…

[⑥ADRV902x]: 软件系统初始化流程学习

前言 本篇博客主要记录ADRV902x参考软件中对ADRV902x系统的初始化流程&#xff0c;使用API函数来实现transceiver的配置&#xff0c;校准和控制等。官方将整个系统初始化称之为multichip synchronization initialization (MCS) sequence&#xff0c;主要分成PreMcsInit&#x…

数字电源为什么一般用DSP控制,而不能用普通的单片机?

数字电源为什么一般用DSP控制&#xff0c;而不能用普通的单片机&#xff1f; 首先你要清楚&#xff0c;数字电源需要一个芯片具备什么功能&#xff1f; 1 能发PWM波 &#xff0c;并且具备保护关断功能&#xff1b; 电源对PWM发波 要求很高&#xff0c;精度要ns级甚至ps级的&…

易宝OA系统ExecuteSqlForSingle接口SQL注入漏洞复现 [附POC]

文章目录 易宝OA系统ExecuteSqlForSingle接口SQL注入漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 易宝OA系统ExecuteSqlForSingle接口SQL注入漏洞复现 [附POC] 0x01 前言 免责声明&#xff1a;请勿利用文章…

2023极客大挑战-AGRT战队wp

目录 RE Shiftjmp 点击就送的逆向题 幸运数字 ​编辑 砍树 小黄鸭 flower-or-tea mySelf 是男人就来扎针 听说cpp很难&#xff1f; Easymath 寻找初音未来 Rainbow 浪漫至死不渝 ezandroid Pwn nc_pwntools password ret2text write1 ret2libc ezpwn wr…

MicroPython STM32F4 RTC功能使用介绍

MicroPython STM32F4 RTC功能使用介绍 &#x1f516;STM32和ESP32 RTC功能差不多&#xff0c;相关篇《MicroPython ESP32 RTC功能使用介绍》&#x1f4cc;固件刷可参考前面一篇《STM32刷Micropython固件参考指南》&#x1f33f; 相关篇《Micropython STM32F4入门点灯》&#x1…

v-model(双向数据绑定)自动收集数据

v-model 是 Vue 中一个常用的指令&#xff0c;用于实现表单元素与数据的双向绑定。 它的实现原理主要基于以下两个方面&#xff1a; 语法实现&#xff1a; v-model 实际上是 Vue 对 :value 和 input 两个属性的语法糖。当我们在组件中使用 v-model 指令时&#xff0c;Vue 会根…

只需十分钟快速入门Python,快速了解基础内容学习。零基础小白入门适用。

文章目录 简介特点搭建开发环境版本hello world注释文件类型变量常量数据类型运算符和表达式控制语句数组相关函数相关字符串相关文件处理对象和类连接mysql关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源…

数据在内存中的存储练习题

数据在内存中的存储练习题 文章目录 数据在内存中的存储练习题1. 练习一2.练习二3. 练习三4. 练习四5. 练习五6. 练习六7. 总结 1. 练习一 #include <stdio.h>int main() {char a -1;signed b -1;unsigned char c -1;printf("a %d b %d c %d", a, b, c)…