关于集合

本文深入解析Java集合框架,包括List、Set、Queue、Map的主要实现类及其特性,如ArrayList、LinkedList、HashSet、TreeSet、HashMap、ConcurrentHashMap等。探讨各集合的底层数据结构、性能特征及适用场景。

Collection集合

Collection主要有三个子接口,分别为List(列表)、Set(集)、Queue(队列)。其中,List、Queue中的元素有序可重复,而Set中的元素无序不可重复;

List集合

为顺序可重复存储方式

  • ArrayList :为线程不安全集合,底层采用数组的形式,即Object[];允许存储null。因为采用数组,所以查询,修改的速度快。但不适合进行频繁的增加或删除。

在此需要注意 ArrayList集合不能使用foreach增删改

  • LinkedList: 底层采用双向链表的方式,为线程不安全。为顺序可重复存储方式。允许存储null。链表特性是添加删除速度快。

需要注意:remove()
remove(int index)是针对于角标来进行删除,不需要去遍历整个集合,效率更高;
而remove(Object o)是针对于对象来进行删除,需要遍历整个集合进行equals()方法比对,所以效率较低;

Set集合(均为线程不安全)

Set集合不允许包含相同的元素,如果试图把两个相同元素加入同一个Set集合中,则添加操作失败,add()方法返回false,且新元素不会被加入。

  • HashSet集合: 存储无序(是指内存上无序),底层采用哈希表的形式。在存储的时候,依据哈希值来进行存储。若哈希值相同,则通过equals方法比较两元素是否相同,若不同,则存储在响应哈希值对应的链表中。否则则不进行存储。(equals与HashCode方法要重写)
    一定要注意:删除或添加的时候,都是要先比较HashCode值是否相等,接下来比较值是否相等。

  • ListedHashSet集合: 同HashSet类似,不同的是在添加元素的时候,会同时存在一个双向链表用来记录每一个新添加元素的前一个元素和后一个元素。所以在遍历的时候速度更快,同时输出的顺序同添加的顺序相同。(但是在内存中的顺序依旧时无序的)

  • TreeSet集合: 底层采用红黑树的数据结构,类似于二叉排序树。所以不可添加相同的元素。同时添加的元素还会自然的进行排序。所以不可添加不同类型的元素。若想要添加对象类型的数据,需要指定所依据的排序字段。即需要实现Comparable接口:如下,根据name从小到大排序。
    注意: 相对于前两个set集合,判断两对象是否相同的条件不再是equals,而是compareTo

	@Override
    public int compareTo(Object o) {
        if (o instanceof user){
            User user = (user)o;
            return this.name.compareTo(user.name);
        }else {
            throw new RuntimeException("输入类型不匹配");
        }
    }

特点:查询比List集合快,有序。

Map集合

HashMap集合(线程不安全)

底层为哈希表/红黑树,线程不安全,键值可以为null。

哈希表: 相比上述几种数据结构,在哈希表中进行添加,删除,查找等操作,性能十分之高,不考虑哈希冲突的情况下,仅需一次定位即可完成,时间复杂度为O(1),接下来我们就来看看哈希表是如何实现达到惊艳的常数阶O(1)的。
结构图为(存储过程)
在这里插入图片描述
采用这种结构可以直接根据散列值来获取元素在数组中的下标,从而快速定位到数组中。若散列值对应的数组中的位置存在单链表,则遍历单链表。

哈希冲突

通过哈希函数得出的实际存储地址相同便是哈希冲突。好的哈希函数会尽可能地保证 计算简单和散列地址分布均匀,但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间,再好的哈希函数也不能保证得到的存储地址绝对不发生冲突。

哈希冲突的解决方案有多种:开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),再散列函数法,链地址法,而HashMap即是采用了链地址法,也就是数组+链表的方式,

HashMap实现原理

一: HashMap的主干是一个Node数组(1.7中为Entry数组)。Node是HashMap的基本组成单元,每 一个Node包含一个key-value键值对。

HashMap整体结构图
在这里插入图片描述
简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

二: 根据HashMap的构造函数可以看出,在创建HashMap对象的时候并未创建数组,而是在执行put方法的时候创建数组
查看put方法的源码

public V put(K key, V value) {
        //如果table数组为空数组{},进行数组填充(为table分配实际内存空间),入参为threshold,此时threshold为initialCapacity 默认是1<<4(24=16)
        if (table == EMPTY_TABLE) {
            inflateTable(threshold); //inflateTable这个方法用于为主干数组table在内存中分配存储空间
        }
       //如果key为null,存储位置为table[0]或table[0]的冲突链上
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);//对key的hashcode进一步计算,确保散列均匀
        int i = indexFor(hash, table.length);//获取在table中的实际位置
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        //如果该对应数据已存在,执行覆盖操作。用新value替换旧value,并返回旧value
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;//保证并发访问时,若HashMap内部结构发生变化,快速响应失败
        addEntry(hash, key, value, i);//新增一个entry
        return null;
    }

inflateTable这个方法用于为主干数组table在内存中分配存储空间,
通过roundUpToPowerOf2(toSize)可以确保capacity为大于或等于toSize的最接近toSize的二次幂
比如toSize=13,则capacity=16;to_size=16,capacity=16;to_size=17,capacity=32.

注意:如果key为null,存储位置为table[0]或table[0]的冲突链上

三:Hash函数
这是一个神奇的函数,用了很多的异或,移位等运算,对key的hashcode进一步进行计算以及二进制位的调整等来保证最终获取的存储位置尽量分布均匀

final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();

        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

以上hash函数计算出的值,通过indexFor进一步处理来获取实际的存储位置

/**
     * 返回数组下标
     */
    static int indexFor(int h, int length) {
        return h & (length-1);
    }

四:重写equals方法需同时重写hashCode方法

/**
 * Created by chengxiao on 2016/11/15.
 */
public class MyTest {
    private static class Person{
        int idCard;
        String name;

        public Person(int idCard, String name) {
            this.idCard = idCard;
            this.name = name;
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()){
                return false;
            }
            Person person = (Person) o;
            //两个对象是否等值,通过idCard来确定
            return this.idCard == person.idCard;
        }

    }
    public static void main(String []args){
        HashMap<Person,String> map = new HashMap<Person, String>();
        Person person = new Person(1234,"乔峰");
        //put到hashmap中去
        map.put(person,"天龙八部");
        //get取出,从逻辑上讲应该能输出“天龙八部”
        System.out.println("结果:"+map.get(new Person(1234,"萧峰")));
    }
}

实际输出结果:
结果:null

如果我们已经对HashMap的原理有了一定了解,这个结果就不难理解了。尽管我们在进行get和put操作的时候,使用的key从逻辑上讲是等值的(通过equals比较是相等的),但由于没有重写hashCode方法,所以put操作时,key(hashcode1)–>hash–>indexFor–>最终索引位置 ,而通过key取出value的时候 key(hashcode1)–>hash–>indexFor–>最终索引位置,由于hashcode1不等于hashcode2,导致没有定位到一个数组位置而返回逻辑上错误的值null(也有可能碰巧定位到一个数组位置,但是也会判断其entry的hash值是否相等,上面get方法中有提到。)

所以,在重写equals的方法的时候,必须注意重写hashCode方法,同时还要保证通过equals判断相等的两个对象,调用hashCode方法要返回同样的整数值。而如果equals判断不相等的两个对象,其hashCode可以相同(只不过会发生哈希冲突,应尽量避免)。

五:哈希表转换成为红黑树的两个条件
1:链表的长度大于 8 ,调用treeifyBin方法
在这里插入图片描述
2:开头有判断数组长度是否小于64,小于则进行扩容,否则转红黑树

六:HashMap在JDK7和JDK8中的区别

JDK7中的HashMap

基于链表+数组实现,底层维护一个Entry数组

Entry<K,V>[] table;

根据计算的hashCode将对应的KV键值对存储到该table中,一旦发生hashCode冲突,那么就会将该KV键值对放到对应的已有元素的后面, 此时,形成了一个链表式的存储结构

JDK8中的HashMap
基于位桶+链表/红黑树的方式实现,底层维护一个Node数组

Node<K,V>[] table;
在JDK7中HashMap,当成百上千个节点在hash时发生碰撞,存储一个链表中,那么如果要查找其中一个节点,那就不可避免的花费O(N)的查找时间,这将是多么大的性能损失,这个问题终于在JDK8中得到了解决。

JDK8中,HashMap采用的是位桶+链表/红黑树的方式,当链表的存储的数据个数大于等于8的时候,不再采用链表存储,而采用了红黑树存储结构。这是JDK7与JDK8中HashMap实现的最大区别。

其他异同
共同点

1.容量(capacity):容量为底层数组的长度,JDK7中为Entry数组,JDK8中为Node数组
a. 容量一定为2的次幂
b. 默认初始容量16(容量为低层数组的长度,JDK7中为Entry数组,JDK8中为Node数组)
c.最大容量1<<30,即2的30次方

hashmap的“最大容量“其实是Integer.MAX_VALUE

2.扩容机制:扩容时resize(2 * table.length),扩容到原数组长度的2倍。

3.key为null:若key == null,则hash(key) = 0,则将该键-值 存放到数组table 中的第1个位置,即table [0]

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

不同点
1.发生hash冲突时

JDK7:发生hash冲突时,新元素插入到链表头中,即新元素总是添加到数组中,就元素移动到链表中。
JDK8:发生hash冲突后,会优先判断该节点的数据结构式是红黑树还是链表,如果是红黑树,则在红黑树中插入数据;如果是链表,则将数据插入到链表的尾部并判断链表长度是否大于8,如果大于8要转成红黑树。

2.扩容时
JDK7:在扩容resize()过程中,采用单链表的头插入方式,在将旧数组上的数据 转移到 新数组上时,转移操作 = 按旧链表的正序遍历链表、在新链表的头部依次插入,即在转移数据、扩容后,容易出现链表逆序的情况 。
多线程下resize()容易出现死循环。此时若(多线程)并发执行 put()操作,一旦出现扩容情况,则 容易出现 环形链表,从而在获取数据、遍历链表时 形成死循环(Infinite Loop),即 死锁的状态 。

**JDK8:**由于 JDK 1.8 转移数据操作 = 按旧链表的正序遍历链表、在新链表的尾部依次插入,所以不会出现链表 逆序、倒置的情况,故不容易出现环形链表的情况 ,但jdk1.8仍是线程不安全的,因为没有加同步锁保护。

建议:
1.使用时设置初始值,避免多次扩容的性能消耗
2.使用自定义对象作为key时,需要重写hashCode和equals方法
3.多线程下,使用CurrentHashMap代替HashMap



为什么线程不安全

个人觉得 HashMap 在并发时可能出现的问题主要是两方面,首先如果多个线程同时使用put方法添加元素,而且假设正好存在两个 put 的 key 发生了碰撞(根据 hash 值计算的 bucket 一样),那么根据 HashMap 的实现,这两个 key 会添加到数组的同一个位置,这样最终就会发生其中一个线程的 put 的数据被覆盖。第二就是如果多个线程同时检测到元素个数超过数组大小* loadFactor ,这样就会发生多个线程同时对 Node 数组进行扩容,都在重新计算元素位置以及复制数据,但是最终只有一个线程扩容后的数组会赋给 table,也就是说其他线程的都会丢失,并且各自线程 put 的数据也丢失。

《Java并发编程的艺术》一书中是这样说的:HashMap 在并发执行 put 操作时会引起死循环,导致 CPU 利用率接近100%。因为多线程会导致 HashMap 的 Node 链表形成环形数据结构,一旦形成环形数据结构,Node 的 next 节点永远不为空,就会在获取 Node 时产生死循环。但是这个问题在jdk1.8中已经解决,在jdk1.8链表节点的插入中放弃了头插法从而采用尾插法进而解决死循环。

死循环并不是发生在 put 操作时,而是发生在扩容时。

线程不安全的原因及后果
https://mp.weixin.qq.com/s/EO6H0MViOugdMfT3rvuRYA

LinkedHashMap(线程不安全)

  • 本身是HashMap的实现类。
  • 底层实现跟HashMap相同
  • 与HashMap比起来,多了一个双向链表,用来记录添加元素时前驱与后继。所以在遍历的时候,输出的顺序与添加的顺序相同。
  • 类比LinkedTreeSet

TreeMap(线程不安全)

TreeMap 是一个有序的key-value集合,它是通过红黑树实现的,每个key-value对即作为红黑树的一个节点。能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,不允许key值为空,非同步的。

Hashtable(线程安全但效率低下)

Hashtable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下Hashtable的效率非常低下。因为当一个线程访问Hashtable的同步方法时,其他线程访问Hashtable的同步方法时,可能会进入阻塞或轮询状态。如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低。

ConcurrentHashMap(线程安全)

讲解HashMap与ConcurrentHashMap的博客
https://blog.youkuaiyun.com/weixin_44460333/article/details/86770169
敖丙:ConcurrentHashMap讲解

简述,详细的还是直接看上面的两篇博客
在1.7中ConcurrentHashMap底层是 Segment 数组、HashEntry 组成,形式是数组加链表的形式。线程安全采用的是分段锁的机制。即一段一段加锁。所以若初始容量是16,那么最多同时能够有十六个线程同时访问。
Segment 是ConcurrentHashMap的一个内部类
源代码如下

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

    private static final long serialVersionUID = 2249069246763182397L;

    // 和 HashMap 中的 HashEntry 作用一样,真正存放数据的桶
    transient volatile HashEntry<K,V>[] table;

    transient int count;
        // 记得快速失败(fail—fast)么?
    transient int modCount;
        // 大小
    transient int threshold;
        // 负载因子
    final float loadFactor;

}

其HashEnty是被volatile修饰的,保证的线程的可见性柯禁止指令重排列。
源代码也可以看出来segment是继承自ReentrantLock,采用分段锁原理上要比hashtable
并发更好。
插入数据的时候需要先定位到segment,在执行put操作。put的源码如下

final V put(K key, int hash, V value, boolean onlyIfAbsent) {
          // 将当前 Segment 中的 table 通过 key 的 hashcode 定位到 HashEntry
            HashEntry<K,V> node = tryLock() ? null :
                scanAndLockForPut(key, hash, value);
            V oldValue;
            try {
                HashEntry<K,V>[] tab = table;
                int index = (tab.length - 1) & hash;
                HashEntry<K,V> first = entryAt(tab, index);
                for (HashEntry<K,V> e = first;;) {
                    if (e != null) {
                        K k;
 // 遍历该 HashEntry,如果不为空则判断传入的 key 和当前遍历的 key 是否相等,相等则覆盖旧的 value。
                        if ((k = e.key) == key ||
                            (e.hash == hash && key.equals(k))) {
                            oldValue = e.value;
                            if (!onlyIfAbsent) {
                                e.value = value;
                                ++modCount;
                            }
                            break;
                        }
                        e = e.next;
                    }
                    else {
                 // 不为空则需要新建一个 HashEntry 并加入到 Segment 中,同时会先判断是否需要扩容。
                        if (node != null)
                            node.setNext(first);
                        else
                            node = new HashEntry<K,V>(hash, key, value, first);
                        int c = count + 1;
                        if (c > threshold && tab.length < MAXIMUM_CAPACITY)
                            rehash(node);
                        else
                            setEntryAt(tab, index, node);
                        ++modCount;
                        count = c;
                        oldValue = null;
                        break;
                    }
                }
            } finally {
               //释放锁
                unlock();
            }
            return oldValue;
        }

先进行tryLock尝试获取锁,若返回true则获取成功,否则执行scanAndLockForPut进行自旋获取锁,若尝试次数超过自旋的最大值,则进入阻塞状态。

get操作就很简单了,只需要将 Key 通过 Hash 之后定位到具体的 Segment ,再通过一次 Hash 定位到具体的元素上。

由于 HashEntry中的value属性是用 volatile关键词修饰的,保证了内存可见性,所以每次获取时都是最新值。

但是1.7jdk中,ConcurrentHashMap实现是数组加链表,若是链表长度过长,则会造成性能降低。
所以在1.8中采用的hash表加上链表/红黑树的形式。锁机制也换成了CAS+synchronized 的方式。
在1.8中,将HashEnty改成了Node的方式,但作用不变,同时使用volatile修饰值和next。使之线程间可见。

1.8中put操作的流程:

  1. 通过计算得到hash值
  2. 判断是否需要初始化分配内存
  3. 如当前位置为空,则利用cas锁尝试获取锁进行写入操作,否则就行自旋获取锁
  4. 若当前位置hashcode == MOVED == -1,则需要进行扩容。
  5. 若都不满足以上两中情况,则使用synchronized 锁写入数据
  6. 若链表过长,则将其转换成红黑树

CAS锁
7. 是一种乐观锁,操作流程如下
在这里插入图片描述
在修改的时候我们进行数据的判断,如下sql代码

update a set value = newValue where value = #{oldValue}//oldValue就是我们执行前查询出来的值 

CAS锁缺点:

  1. CAS若是得不到锁,就会进入自旋操作,尝试重新获得锁。在并发请求大且冲突概率高的情况下,大量线程会反复尝试更新一个变量,自旋获得锁,会造成cpu的负载过高。
  2. 会产生ABA问题,所以我们可以采用版本号的方式实现乐观锁。
  3. 不能保证代码块的原子性
    CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。
本系统采用Python编程语言中的Flask框架作为基础架构,实现了一个面向二手商品交易的网络平台。该平台具备完整的前端展示与后端管理功能,适合用作学术研究、课程作业或个人技术能力训练的实际案例。Flask作为一种简洁高效的Web开发框架,能够以模块化方式支持网站功能的快速搭建。在本系统中,Flask承担了核心服务端的角色,主要完成请求响应处理、数据运算及业务流程控制等任务。 开发工具选用PyCharm集成环境。这款由JetBrains推出的Python专用编辑器集成了智能代码提示、错误检测、程序调试与自动化测试等多种辅助功能,显著提升了软件编写与维护的效率。通过该环境,开发者可便捷地进行项目组织与问题排查。 数据存储部分采用MySQL关系型数据库管理系统,用于保存会员资料、产品信息及订单历史等内容。MySQL具备良好的稳定性和处理性能,常被各类网络服务所采用。在Flask体系内,一般会配合SQLAlchemy这一对象关系映射工具使用,使得开发者能够通过Python类对象直接管理数据实体,避免手动编写结构化查询语句。 缓存服务由Redis内存数据库提供支持。Redis是一种支持持久化存储的开放源代码内存键值存储系统,可作为高速缓存、临时数据库或消息代理使用。在本系统中,Redis可能用于暂存高频访问的商品内容、用户登录状态等动态信息,从而加快数据获取速度,降低主数据库的查询负载。 项目归档文件“Python_Flask_ershou-master”预计包含以下关键组成部分: 1. 应用主程序(app.py):包含Flask应用初始化代码及请求路径映射规则。 2. 数据模型定义(models.py):通过SQLAlchemy声明与数据库表对应的类结构。 3. 视图控制器(views.py):包含处理各类网络请求并生成回复的业务函数,涵盖账户管理、商品展示、订单处理等操作。 4. 页面模板目录(templates):存储用于动态生成网页的HTML模板文件。 5. 静态资源目录(static):存放层叠样式表、客户端脚本及图像等固定资源。 6. 依赖清单(requirements.txt):记录项目运行所需的所有第三方Python库及其版本号,便于环境重建。 7. 参数配置(config.py):集中设置数据库连接参数、缓存服务器地址等运行配置。 此外,项目还可能包含自动化测试用例、数据库结构迁移工具以及运行部署相关文档。通过构建此系统,开发者能够系统掌握Flask框架的实际运用,理解用户身份验证、访问控制、数据持久化、界面动态生成等网络应用关键技术,同时熟悉MySQL数据库运维与Redis缓存机制的应用方法。对于入门阶段的学习者而言,该系统可作为综合性的实践训练载体,有效促进Python网络编程技能的提升。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
在当代储能装置监控技术领域,精确测定锂离子电池的电荷存量(即荷电状态,SOC)是一项关键任务,它直接关系到电池运行的安全性、耐久性及整体效能。随着电动车辆产业的迅速扩张,业界对锂离子电池SOC测算的精确度与稳定性提出了更为严格的标准。为此,构建一套能够在多样化运行场景及温度条件下实现高精度SOC测算的技术方案具有显著的实际意义。 本文介绍一种结合Transformer架构与容积卡尔曼滤波(CKF)的混合式SOC测算系统。Transformer架构最初在语言处理领域获得突破性进展,其特有的注意力机制能够有效捕捉时间序列数据中的长期关联特征。在本应用中,该架构用于分析电池工作过程中采集的电压、电流与温度等时序数据,从而识别电池在不同放电区间的动态行为规律。 容积卡尔曼滤波作为一种适用于非线性系统的状态估计算法,在本系统中负责对Transformer提取的特征数据进行递归融合与实时推算,以持续更新电池的SOC值。该方法增强了系统在测量噪声干扰下的稳定性,确保了测算结果在不同环境条件下的可靠性。 本系统在多种标准驾驶循环(如BJDST、DST、FUDS、US06)及不同环境温度(0°C、25°C、45°C)下进行了验证测试,这些条件涵盖了电动车辆在实际使用中可能遇到的主要工况与气候范围。实验表明,该系统在低温、常温及高温环境中,面对差异化的负载变化,均能保持较高的测算准确性。 随附文档中提供了该系统的补充说明、实验数据及技术细节,核心代码与模型文件亦包含于对应目录中,可供进一步研究或工程部署使用。该融合架构不仅在方法层面具有创新性,同时展现了良好的工程适用性与测算精度,对推进电池管理技术的进步具有积极意义。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
代码转载自:https://pan.quark.cn/s/9e296fe8986c 实验题目为“复杂模型机的设计与实现”。 _1. 实验目的与要求:目的:1. 熟练掌握并达成较为复杂的计算机原理。 2. 本实验增加了16条机器指令,全面运用所学的计算机原理知识,借助扩展的机器指令设计并编写程序,然后在CPU中执行所编写的程序。 要求:依照练习一和练习二的要求完成相应的操作,并上机进行调试和运行。 2. 实验方案:……实验报告的标题设定为“广东工业大学计组实验报告复杂模型机的设计与实现六”,主要围绕计算机组成原理中的复杂模型机设计和实现展开。 实验的宗旨在于让学生深入理解和实际操作计算机原理,特别是通过增加16条机器指令,来全面运用所学知识设计程序,并在CPU中运行这些程序。 实验的具体要求包括:1. 掌握复杂的计算机工作原理,这要求学生不仅具备扎实的理论知识,还需要拥有将理论转化为实际操作的能力。 2. 实验中增加了16条机器指令,这涉及到计算机指令集的扩展和设计,可能包含算术运算、逻辑运算、数据传输和控制流程等指令。 3. 学生需要运用扩展的机器指令编写程序,并通过CPU进行运行和调试,这涉及到编程、汇编和CPU执行流程的理解。 4. 依照练习一和练习二的要求完成操作,这表明实验包含分阶段的练习任务,需要逐步完成并验证。 实验方案包括:1. 实验连线:保证硬件连接准确无误,这是任何电子实验的基础,对于计算机实验,这通常涵盖CPU、内存、输入/输出设备等组件的连接。 2. 实验程序:提供了范例程序,包括机器指令程序和微指令程序的微代码。 这部分内容展示了如何利用扩展的机器指令编写程序,以及对应的微指令实现,有助于理解计算机内部的低级操作。 在实验结果和数据处理部分,学生需要:1. 在程...
关于Python中集合的语法,以下是一些常用操作: 1. 创建集合: 使用大括号 `{}` 或者使用 `set()` 函数来创建一个空集合。 例如:`my_set = {1, 2, 3}` 或者 `my_set = set([1, 2, 3])` 2. 添加元素: 使用 `.add()` 方法来向集合中添加一个元素。 例如:`my_set.add(4)` 3. 删除元素: 使用 `.remove()` 方法来删除集合中的指定元素。 例如:`my_set.remove(2)` 4. 集合运算: - 并集:使用 `|` 或者 `.union()` 方法。 例如:`union_set = set1 | set2` 或者 `union_set = set1.union(set2)` - 交集:使用 `&` 或者 `.intersection()` 方法。 例如:`intersection_set = set1 & set2` 或者 `intersection_set = set1.intersection(set2)` - 差集:使用 `-` 或者 `.difference()` 方法。 例如:`difference_set = set1 - set2` 或者 `difference_set = set1.difference(set2)` - 对称差集:使用 `^` 或者 `.symmetric_difference()` 方法。 例如:`symmetric_difference_set = set1 ^ set2` 或者 `symmetric_difference_set = set1.symmetric_difference(set2)` 5. 集合方法: - `.add(element)`:向集合中添加元素。 - `.remove(element)`:从集合中删除指定元素,如果元素不存在会引发 KeyError。 - `.discard(element)`:从集合中删除指定元素,如果元素不存在不会引发错误。 - `.clear()`:清空集合中的所有元素。 - `.copy()`:复制集合。 - `.pop()`:随机删除并返回集合中的一个元素。 - `.update(other_set)`:将另一个集合的元素添加到当前集合中。 - `.union(other_set)`:返回两个集合的并集。 - `.intersection(other_set)`:返回两个集合的交集。 - `.difference(other_set)`:返回两个集合的差集。 - `.symmetric_difference(other_set)`:返回两个集合的对称差集。 这些是关于Python集合的一些基本操作和方法,希望能对你有所帮助!如果你有更多关于集合的问题,请随时提问。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值