Java集合框架-Set和Map

news/2024/10/22 14:43:07/

1. Set集合的使用

1.1. Set集合类型

  • Set:无序,唯一
  • HashSet
    • 采用Hashtable哈希表存储结构
    • 优点:添加、查询、删除速度快
    • 缺点:无序
  • LinkedHashSet
    • 采用哈希表存储结构,同时使用链表维护次序
    • 有序(添加顺序)
      在这里插入图片描述在这里插入图片描述
  • TreeSet
    • 采用二叉树(红黑树)的存储结构
    • 优点:有序,查询速度比List快(按照内容查询)
    • 缺点:查询速度没有HashSet快
      在这里插入图片描述

1.2. 使用各种Set集合类存储课程名称

java">public class TestSet1 {public static void main(String[] args) {// 创建一个集合set对象// Set<String> set = new HashSet<String>();// Set<String> set = new LinkedHashSet<String>();Set<String> set = new TreeSet<String>();set.add(new String("语文"));set.add("数学");System.out.println(set.size()); // 2System.out.println(set); // [wyb, xz]// 不可以使用for循环遍历setfor (int i = 0; i < set.size(); i++) {// set.get(i);}// 支持增强的for循环,支持iteratorIterator<String> it = set.iterator();while (it.hasNext()) {System.out.println(it.next()); // 语文 数学}}
}
  • 总结
    • HashSet哈希表,唯一、无序
    • LinkedHashSet:哈希表+链表,唯一、有序(添加顺序)
    • TreeSet:红黑树,一种二叉平衡树,唯一、有序(自然顺序)
    • List针对Collection增加了一些关于索引位置操作的方法:get(i)、add(i, elem)、remove(i)、set(i, elem)
    • Set是无序的,不可能提供关于索引位置操作的方法,set针对Collection没有增加任何方法
    • List的遍历有三种方式:for循环、for-each循环、Iterator迭代器
    • Set的遍历方式有两种:for-each循环、Iterator迭代器

1.3. 使用各种Set存储自定义学生信息

java">public class TestSet2 {public static void main(String[] args) {// 创建一个集合set对象// Set<Student> set = new TreeSet<Student>();// Set<Student> set = new HashSet<Student>();Set<Student> set = new LinkedHashSet<Student>();Student stu = new Student(1, "wyb", "男", 59);Student stu1 = new Student(1, "xz", "男", 98);Student stu2 = new Student(1, "wyb", "男", 59);set.add(stu);set.add(stu1);set.add(stu2);System.out.println(set.size()); // 3System.out.println(set); }
}

Q1: 为什么HashSet、LinkedHashSet String重复,会保持唯一,而Student有重复,不会保持唯一
A1: HashSet、LinkedHashSet需要Student实现hashCode()和equals()
Q2:为什么TreeSet可以添加String,不能添加Student,会抛出异常java.lang.ClassCastException: class com.wyb.staticcontent.Student cannot be cast to class java.lang.Comparable
A2:TreeSet需要Student实现Comparable接口并指定比较的规则

1.4. 让各种Set可以存储自定义类型的对象Student

java">public class Student implements Comparable<Student> {private int sno;private String name;private int age;private double score;@Overridepublic int compareTo(Student o) {return -(this.sno - o.sno);}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return sno == student.sno && age == student.age && Double.compare(score, student.score) == 0 && Objects.equals(name, student.name);}@Overridepublic int hashCode() {return Objects.hash(sno, name, age, score);}
}

2. Set集合的原理

2.1. 外部比较器Comparator的作用和使用

内部比较器Comparable只有一个,如果希望指定多种比较的规则,可以定义多个外部比较器,定义额外的类实现Comparator接口

2.1.1. 定义外部比较器,按照分数升序排列

java">public class StudentScoreComparator implements Comparator<Student>{@Overridepublic int compare(Student stu1, Student stu2) {if(stu1.getScore() > stu2.getScore()) return 1;else if(stu1.getScore() < stu2.getScore()) return -1;else return 0;}
}

2.1.2. 定义外部比较器:按照姓名逆序排序,如姓名相同,按学号逆序排列

java">public class StudentNameDescComparator implements Comparable<Student>{@Overridepublic int compare(Student stu1, Student stu2) {int n = stu1.getName().compareTo(stu2.getName());if(n != 0) return n;else return -(stu1.getSno() - stu2.getSno());}
}

2.1.3. 使用外部比较器实现TreeSet对学生排序

java">public class Test3 {public static void main(String[] args) {// 创建一个set集合对象// Comparator comp = new StudentScoreComparator();// Comparator comp = new StudentNameDescComparator();Comparator comp = new Comparator<Student>() {@Overridepublic int compare(Student stu1, Student stu2) {return -(stu1.getSno() - stu2.getSno());}};// 没有指定比较器,使用内部比较器// Set<Student> set = new TreeSet<Student>();// 指定了外部比较器,就使用外部比较器Set<Student> set = new TreeSet<Student>(comp);Student stu1 = new Student(1, "wyb", 27, 56);Student stu2 = new Student(2, "xz", 33, 98);Student stu3 = new Student(1, "wyb", 23, 56);set.add(stu1);set.add(stu2);set.add(stu3);System.out.println(set.size()); // 2for (Student student : set) {System.out.println(student);}}
}

内部比较器只能定义一个,一般将使用频率最高的比较规则定义为内部比较器的规则,外部比较器可以定义多个

  • 注意
    • 对于外部比较器,如果使用次数较少,可以通过匿名内部类来实现
    • 需要比较的场合才需要实现内部比较器或外部比较器,比如排序、TreeSet中数据的存储和查询,在HashSet、LinkedHashSet、ArrayList中存储元素,不需要实现内部比较器或外部比较器

2.2. 哈希表的原理

2.2.1. 引入哈希表

在无序数组中按照内容查找,效率低下,时间复杂度是O(n)
在这里插入图片描述
在有序数组中按照内容查找,使用折半查找,时间复杂度是O(log2n)
在这里插入图片描述
在二叉平衡树中按照内容查找,时间复杂度是O(log2n)
在这里插入图片描述
在数组中按照索引查找,不进行比较和计数,直接计算得到,效率最高,时间复杂度O(1)
在这里插入图片描述
哈希表:按照内容查找,不进行比较,通过计算得到地址,实现类似数组按照索引查询的高效率O(1)
理想方法是:不需要比较,根据给定值直接定位记录的存储位置,这样,需要在记录的存储位置与该记录的关键字之间建立一种确定的对应关系,使每个记录的关键字与一个存储位置相对应

2.2.2. 哈希表的结构和特点

哈希表也叫散列表
特点:快
结构:顺序表+链表
主结构:顺序表
每个顺序表的节点在单独引出一个链表
在这里插入图片描述

2.2.3. 哈希表是如何添加数据的

  1. 计算哈希码(调用hashCode()),结果是一个int值,整数的哈希码取自身即可
  2. 计算在哈希表中的存储位置:y = k(x) = x % 11,x:哈希码;k(x):在哈希表中的存储位置
  3. 存入哈希表
    • 情况1:一次添加成功
    • 情况2:多次添加成功(出现了冲突,调用equals()和对应链表的元素进行比较,比到最后,结果都为false,创建新节点,存储数据,并加入链表末尾)
    • 情况3:不添加(出现了冲突,调用equals()和对应链表的元素进行比较,经过一次或多次比较后,结果是true,表明重复,不添加)
      结论1: 哈希表添加数据快(3步即可,不考虑冲突)
      结论2: 唯一、无序
      在这里插入图片描述

2.2.4. 哈希表是如何查询数据的

和添加数据的过程是相同的

  • 情况1: 一次找到 2、86、76
  • 情况2: 多次找到 67、56、78
  • 情况3: 找不到 100、200

结论1: 哈希表查询速度快

2.2.5. hashCode和equals到底有什么神奇的作用

  • hashCode():计算哈希码,是一个整数,根据哈希码可以计算出数据在哈希表中的存储位置
  • equals():添加时出现了冲突,需要通过equals进行比较,判断是否相同;查询时也需要使用equals进行比较,判断是否相同

2.2.6. 各种类型数据的哈希码如何获取hashCode()

  1. int:取自身,看Integer的源码
  2. double:3.14、3.15、3.145、6.567、9.89,取整不可以,看Double源码
  3. String:将各个字符的编码值相加不可以
  4. Student:先各个属性的哈希码,进行某些相加相乘的运算

2.2.7. 如何减少冲突

  1. 哈希表的长度和表中的记录数的比例–装填因子
    如果Hash表的空间远远大于最后实际存储的记录个数,就会造成很大的空间浪费,如果选小了,容易造成冲突。
    在实际情况下,需要根据最终记录存储个数和关键字的分布特点,确定Hash表的大小。如果事先不知道最终需要存储的记录个数,则需要动态维护Hash表的容量,此时可能需要重新计算Hash地址
    装填因子 = 表中的记录数 / 哈希表的长度
  • 装填因子越小,表明表中还有很多的空单元,则添加发生的冲突的可能型越小
  • 装填因子越大,表明发生冲突的可能性越大,在查找时耗费的时间越多。
  • 一般装填因子在0.5左右时,Hash性能达到最优
  1. 哈希函数的选择:直接定址法、平方取中法、折叠法、除数取余法(y = x % 11)
  2. 处理冲突的方法:链地址法、开放地址法、再散列法、建立一个公共溢出区

3. Map集合的使用

3.1. Map集合类型

  • Map:特点:存储的键值对映射关系,根据key可以找到value
  • HashMap:采用Hashtable哈希表存储结构
    • 优点:添加、查询、删除速度快
    • 缺点:key无序
  • LinkedHashMap
    • 采用哈希表存储结构,同时使用链表维护次序
    • key有序(添加顺序)
      在这里插入图片描述
  • TreeMap:采用二叉树(红黑树)的存储结构
    • 优点:key有序,查询速度比List快(按照内容查询)
    • 缺点:查询速度没有HashSet快
      在这里插入图片描述

3.2. 使用Map存储国家名称-国家名称映射

java">public class TestMap1 {public static void main(String[] args) {// 创建一个Map集合对象// Map<String, String> map = new HashMap<String, String>();// Map<String, String> map = new LinkedHashMap<String, String>();Map<String, String> map = new TreeMap<String, String>();map.put("cn", "china");map.put("us", "America");System.out.println(map.size()); // 2System.out.println(map); // {cn=china, us=America}// Set得到所有的keySystem.out.println(map.keySet()); // [cn, us]// Collection得到所有的valueSystem.out.println(map.values()); // [china, America]System.out.println(map.get("cn")); // china// Map遍历// 1. 先得到所有的key(Set),然后根据key找到valueSet<String> keyset = map.keySet();for (String key : keyset) {System.out.println(key + "--->" + map.get(key)); // cn--->china  us--->America}// 2. 先得到所有的key-value组成的Set,然后输出每个key-valueSet<Map.Entry<String, String>> entrySet = map.entrySet();Iterator<Map.Entry<String, String>> it = entrySet.iterator();while (it.hasNext()) {// 取出一个EntryMap.Entry<String,String> entry = it.next();System.out.print(entry + "\t");System.out.println(entry.getKey() + "--->" + entry.getValue()); // cn=china	cn--->china  us=America	us--->America}}
}

3.3. 使用各种Map存储学号-学生映射

java">public class TestMap2 {public static void main(String[] args) {// 创建一个Map对象用户存储key-valueMap<Integer, Student> map = new HashMap<Integer, Student>();// Map<Integer, Student> map = new TreeMap<Integer, Student>();Student stu = new Student(1, "wyb", 23, 56);Student stu1 = new Student(1, "wyb", 23, 56);Student stu2 = new Student(3, "xz", 23, 56);map.put(stu.getSno(), stu);map.put(stu1.getSno(), stu1);map.put(stu2.getSno(), stu2);// map.remove(2);// map.clear();// map.replace(1, new Student(1, "bjyx", 5, 100));System.out.println(map.containsKey(1));; // trueSystem.out.println(map.containsValue(stu1)); // trueSystem.out.println(map.isEmpty());; // falseStudent s = map.get(1);if(s == null) System.out.println("该学生不存在");else System.out.println(s);System.out.println(map.size()); // 2System.out.println(map.toString()); // {1=com.wyb.setmap.Student@471fec69, 3=com.wyb.setmap.Student@4093cde9}Set<Entry<Integer, Student>> entrySet = map.entrySet();for (Entry<Integer, Student> entry : entrySet) {Student student = entry.getValue();System.out.println(student);}}
}
  • 方法
    • clear():从此映射中移除所有映射关系(可选操作),返回boolean
    • containsKey(Object key):如果此映射包含指定键的映射关系,则返回true
    • containsValue(Object value):如果此映射将一个或多个键映射到指定值,则返回true
    • entrySet():返回此映射中包含的映射关系的Set视图
    • equals(Object obj):比较指定的对象与此映射是否相等
    • get(Object key):返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回null
    • hashCode():返回此映射的哈希码值
    • isEmpty():如果此映射没有键-值映射关系,则返回true
    • keySet(): 返回此映射中包含的键的Set视图
    • put(K key, V value):将指定键与此映射中的指定键关联(可选操作)
    • putAll(Map<? extends K, ? extends V> m):从指定映射中将所有映射关系复制到此映射中。(可选操作)
    • remove(Object key):如果存在一个键的映射,则将其从此映射中移除。(可选操作)
    • size():返回此映射中的键-值映射关系数。
    • values(): 返回此映射中包含的值的Collection视图。

4. Map和Set集合源码

4.1. 理解HashMap的源码

  • JSK1.7及其之前,HashMap底层就是一个table数组 + 链表实现的哈希表结构
    在这里插入图片描述
  • 链表的每个节点就是一个Entry,其中包括:键Key、值Value、键的哈希码hash、执行下一个节点的引用next四部分
java">static class Entry<K, V> implements Map.Entry<K, V> {final K key; V value;Entry<K, V> next;int hash;
}
  • JDK1.7中HashMap的主要成员变量及其含义
java">public class HashMap<K, V> implements Map<K, V> {// 哈希表主数组的默认长度static final int DEFAULT_INITIAL_CAPACITY = 16;// 默认负载因子static final float DEFAULT_LOAD_FACTOR = 0.75f;// 主数组的引用transient Entry<K, V>[] table;// 界限值 阈值int threshold;// 装填因子float float loadFactor;public HashMap() {this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);}public HashMap(int initialCapacity, float loadFactor) {this.loadFactor = loadFactor;threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); // 16 * 0.75 = 12table = new Entry[capacity];...}
}
  • 调用put方法添加键值对。哈希表三步添加数据原理的具体实现;是计算key的哈希码,和value无关。特别注意:
    1. 第一步计算哈希码时,不仅调用了key的hashcode()方法,还进行了更复杂的处理,目的是尽量保证不同的key得到不同的哈希码
    2. 第二部根据哈希码计算存储位置时,使用了位运算提高效率
    3. 第三步添加Entry时添加到链表的第一个位置,而不是链表末尾
    4. 第三步添加Entry时,发现了相同的key已经存在,就使用新的value替代旧的value,并且返回旧的value
java">public class HashMap {public V put(K key, V value) {if(key == null) return putForNullKey(value);// 1. 计算key的hash值int hash = hash(key);// 2. 将哈希码带入函数,计算出存储位置,y = x % 16int i = indexFor(hash, table.length);// 如果已经存在链表,判断是否存在该key,需要用到equals()for(Entry<K, V> e = table[i]; e != null; e = e.next) {Object k;// 如果找到了,使用心得value替换旧的value,返回旧valueif(e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;return oldValue;}}// 添加一个节点addEntry(hash, key, value, i);return null;}final int hash(Object key) {int h = 0;h ^= k.hashCode();h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);}static int indexFor(int h, int length) {// 作用就相当于y = x % 16,采用了位运算,效率更高return h & (length - 1);}
}
  • 调用get方法,根据key获取value
    • 哈希表三步查询数据原理的具体实现
    • 其实是根据key找Entry,再从Entry中找value
java">public V get(Object key) {// 根据key找到Entry(Entry中有key和value)Entry<K,V> entry = getEntry(key);// 如果entry==null,返回null,否则返回valuereturn (entry == null) ? null : entry.getValue();
}
  • 添加元素时,如达到了阈值需扩容,每次扩容为原来主数组容量的2倍
java">void addEntry(int hash, K key, V value, int bucketIndex) {// 如果达到了门槛值就扩容,容量为原来容量的2倍   16-32if((size >= threshold) && (null != table[bucketIndex]))resize(2 * size);hash = (null != key) ? hash(key) : 0;bucketIndex = indexFor(hash, table.length);}// 添加节点createEntry(hash, key, value, bucketIndex);
}
  • 在JDK1.8中有了一些变化,当链表的存储数据个数>=8时,不再采用链表存储,而采用红黑树存储,
  • 这么做主要是查询的时间复杂度上,链表为O(n),而红黑树一直是O(logn)。如果冲突多,并且超过8,采用红黑树来提高效率
    在这里插入图片描述

4.2. 理解TreeMap的源码

  • 基本特征:二叉树、二叉查找树、二叉平衡树、红黑树
    在这里插入图片描述
  • 每个节点的结构
    在这里插入图片描述
java">static final class Entry<K,V> implements Map.Entry<K,V> {K key;V value;Entry<K,V> left;Entry<K,V> right;Entry<K,V> parent;boolean color = BLACK;
}
  • TreeMap主要的成员变量及其含义
java">public class TreeMap<K, V> implemments NavigableMap<K, V> {// 外部比较器private final Comparator<? super K> comparator;// 红黑树根节点的引用public transient Entry<K, V> root = null;// 红黑树中节点的个数public transient int size = 0;public TreeMap() {// 没有指定外部比较器comparator = null;}public TreeMap(Comparator<? super K> comparator) {// 指定了外部比较器this.comparator = comparator;}
}
  • 添加原理
    • 从根节点开始比较
    • 添加过程就是构造二叉平衡树的过程,会自动平衡
    • 平衡离不开比较:外部比较器优先,然后是内部比较器。如果两个比较器都没有,就抛出异常
java">public V pub(K key, V value) {Entry<K, V> t = root;// 如果添加第一个节点if(t == null) {// 即使是添加第一个节点,也要使用比较器compare(key, key);// 创建根节点root = new Entry<>(key, value, null);// 此时只有一个节点size = 1;return null;}// 如果添加非第一个节点int cmp;Entry<K, V> parent;Comparator<? super K> cpr = comparator;// 如果外部比较器存在,就使用外部比较器if(cpr != null) {do {parent = t;cmp = cpr.compare(key, t.key);if(cmp < 0) t = t.left; // 在左子树中查找else if(cmp > 0) t = t.right; // 在右子树中查找else return t.setValue(value); // 找到了对应的key,使用新的value覆盖旧的value}while (t != null);}else {// 如果外部比较器没有,就使用内部比较器...}// 找到了要添加的位置,创建一个新的节点,加入到树中Entry<K, V> e = new Entry<>(key, value, parent);if(cmp < 0) parent.left = e;else parent.right = e;size++;return null;
}
  • 查询基本原理同添加
java">public V get(Object key) {// 根据key找EntryEntry<K, V> p = getEntry(key);// 如果Entry存在,返回valuereturn (p == null ? null : p.value); 
}
final Entry<K, V> getEntry(Object key) {// 如果外部比较器存在,就使用外部比较器if(comparator != null) return getEntryUsingComparator(key);if(key == null) throw new NullPointerException();@SuppressWarnings("unchecked")// 如果外部比较器不存在,就使用内部比较器Comparable<? super K> k = (Comparable<? super K>) key;Entry<K, V> p = root;while (p != null) {int cmp = k.compareTo(p.key);if(cmp < 0) p = p.left;else if(cmp > 0) p = p.right;else return p; // 如果找到了,就返回Entry}// 如果没有找到,就返回nullreturn null;
}

4.3. 理解HashSet的源码

  • HashSet的底层使用的是HashMap,所以底层结构也是哈希表
  • HashSet的元素到HashMap中做key,value统一的是new Object()
java">public class HashSet<E> implements Set<E> {private transient HashMap<E, Object> map;private static final Object PRESENT = new Object();public HashSet() {map = new HashMap<>();}public boolean add(E e) {return map.put(e, PRESENT) == null;}public int size() {return map.size();}public Iterator<E> iterator() {return map.keySet().iterator();}
}

4.4. 理解TreeSet的源码

  • TreeSet的底层使用的是TreeMap,所以底层结构也是红黑树
  • TreeSet的元素e是作为TreeMap的key存在,value统一为new Object()
java">public class TreeSet<E> implements NavigableSet<E> {// 底层的TreeMap引用private transient NavigableMap<E, Object> m;private static final Object PRESENT = new Object();public TreeSet() {// 创建TreeSet对象就是创建一个TreeMap对象this(new TreeMap<E, Object>());}TreeSet(NavigableMap<E, Object> m) {this.m = m;}public boolean add(E e) {return m.put(e, PRESENT) == null;}public int size() {return m.size();}
}

http://www.ppmy.cn/news/1505348.html

相关文章

一款功能强大且免费的Windows系统优化工具

TweakPower是一款功能强大的Windows系统优化工具&#xff0c;旨在帮助用户提升电脑性能、清理垃圾文件、备份数据以及修复系统问题。该软件提供了多种实用功能&#xff0c;包括内存管理、垃圾清理、数据备份、数据擦除、硬盘维护和性能调度调整等。 TweakPower的主要界面或仪表…

7.25 Git 及分支管理

一.分布式版本控制系统 1.git Git 是一种分布式版本控制系统&#xff0c;用于跟踪和管理代码的变更。它是由 Linus Torvalds 创建的&#xff0c;最初被设计用于 Linux 内核的开发。Git 允许开发 人员跟踪和管理代码的版本&#xff0c;并且可以在不同的开发人员之间进行协作…

Apache Kylin分布式的分析数据仓库

Apache Kylin 是一个分布式的分析数据仓库&#xff0c;用于大数据上的超快在线分析处理 (OLAP)。它能够在大规模数据集上提供亚秒级的查询响应时间&#xff0c;并支持标准的 ANSI SQL 查询接口。Kylin 最初由 eBay 开发&#xff0c;后来捐赠给 Apache 软件基金会&#xff0c;现…

NASA:气溶胶研究处 (ARB) 48 英寸激光雷达数据

Aerosol Research Branch (ARB) 48 inch Lidar Data 气溶胶研究处 (ARB) 48 英寸激光雷达数据 简介 ARB_48_IN_LIDAR 数据集包含从位于 NASA 兰利研究中心的 48 英寸激光雷达系统收集的数据。每个粒度包含一年的数据。每个粒度的数据天数不同。每个测量值由四个参数组成&…

程序员面试中的“八股文”:是敲门砖还是绊脚石?

在当今竞争激烈的IT行业&#xff0c;程序员面试已经不仅仅是对技术能力的考核&#xff0c;更是对综合素质的全面评估。“八股文”作为面试中的“标配”&#xff0c;其存在引发了广泛的争议。有人认为它是程序员必备的知识储备&#xff0c;关键时刻能发挥重要作用&#xff1b;也…

22 - grace数据处理 - 补充 - 泄露误差改正 - Slepian局部谱分析法(二) - Slepian谱分析程序包初始化

22 - grace数据处理 - 补充 - 泄露误差改正 - Slepian局部谱分析法 - Slepian谱分析程序包初始化 0 引言1 slepian程序包配置过程1.1 获取环境配置安装包1.2 执行demo测试是否配置成功2 结语0 引言 上篇提到进行slepian谱分析可以使用美国普林斯顿大学Frederik Simons教授提供的…

关于加载水印PDF、图片以及压缩包格式文件【把博客当工作记录】

写这篇文章的目的是让大家都可以学到东西,核心代码中列出了处理思维和调用方法,业务代码已经过滤掉了,希望大家不要做crud程序员!!要思考。。。该博客不懂时可联系下方。 1、流程图如下 2、策略描述 实现方式: 设计模式:父策略调动子策略 业务理念:在不影响原有业务…

【电商API接口项目实战分享】项目实战案例一:电商平台零售数据分析

本文以真实案例&#xff0c;带领大家一起学习如何搭建电商零售的用户画像。 “项目介绍” 此次项目数据来自Kaggle&#xff0c;包含了2010年12月1日至2011年12月9日在英国注册的非实体网上零售发生的所有交易。 字段如下: Invoice: 订单编号&#xff0c;每笔交易有6个整数。 …