我是靠谱客的博主 健忘曲奇,最近开发中收集的这篇文章主要介绍浅谈JAVA类集框架(JAVA集合类)【学习笔记】一、前置二、正篇三、尾文,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

关于Java类集框架的学习笔记

  • 一、前置
    • 1.情景引入
    • 2.讲在前头
    • 3.笔记总结
  • 二、正篇
    • 1.框架结构
    • 2. Collection(译:类集)框架接口部分
      • ①Collection接口
      • ②List接口
      • ③Set接口
      • ④SortSet接口
    • 3. Collection(译:类集)框架相关实现类部分
      • ①ArrayList实现类
      • ②LinkedList实现类
      • ③HashSet实现类
      • ④TreeSet实现类
      • ⑤通过迭代器方法访问类集 Iterator
      • ⑥迭代器部分方法的代码实操
    • 4. Map(译:映射)接口部分
      • ①Map接口
      • ②SortedMap接口
      • ③Map.Entry接口
    • 5. Map(译:映射)相关实现类部分
      • ①HashMap类
      • ②TreeMap类
      • ③比较方法:Comparator接口
  • 三、尾文

发布于2019年08月06日 ⇲禁止转载于copy

笔记属于应用和基础原理层面,为浅谈;不涉及源代码解析。

一、前置

1.情景引入

Java知识中有一个类集框架的概念,每种文献各有各的说法,也有的叫做Java集合类,造成混乱。并且概念中Collection翻译成中文又是类集的意思,Set翻译成中文是集合的意思,混乱的翻译、指代不明的名词概念以及网络上copy的差错,导致初学者从入门到放弃。现在本学习笔记将帮你梳理基础概念、供你参考解决疑惑。

2.讲在前头

我们先统一约定为类集框架概念,类集框架是为了表示和操作集合而规定的一种统一的标准的体系结构。(注意区分类集框架和集合框架,类集框架包含了集合框架和映射Map)。Java类集类又称为容器,有别于数组容器用于存放基本类型的数据,集合类则是用来存放对象的引用,并可对集合进行高效的增删改查及遍历等等操作。常用的集合有List集合、Set集合、Map集合,每个集合都有各自的特点和适用场景。

3.笔记总结

  1. 值得注意的是Set的中文翻译是集合,要仔细区分好集合框架和set集合的差别,不要弄混。
  2. 类集框架包括Collection(翻译为类集),和Map(翻译为映射);为什么包括了Map(映射)还叫类集框架这个名字呢。其实Map(映射)虽然存储的是键值对,好像和Collection(类集)有差别,但是Map(映射)中的键值对用的是对象存储,所以本质上Map(映射)也是一个Collection(类集)。
  3. 为了更好理解类集框架的结构,我们做以下总结:
    类集框架看作是一种容器,容器里存放的是类对象。Map(映射)是一种比较特殊的容器,它里面存放的是一种称为“键值对”的类对象,容器里的“键值对”对象是不可复制的(意思是不能出现两个“相等”的对象实例) ;Collection(类集)是一种存放任意类对象的容器。Collection(类集)下面呢又分为了两种不一样的容器:一种是List(列表),存放的类对象是可以复制的,而且是一个线性表(可与数据结构的线性表概念关联);另一种是Set(集合),存放的类对象是不可复制的,并且对象之间的存放是无序的(可与数学的集合概念相关联)。
    List(列表)实现了ArrayList和LinkedList,ArrayList实现的是动态数组(支持随机访问,插入删除效率低,查询效率高);LinkedList实现的是链接表(插入删除效率高,查询效率慢)。Set(集合)实现了HashSet(散列集合)和TreeSet(树集合),HashSet具备散列的优势,即便是对于庞大的集合,其基本操作也能保持效率的不变性;TreeSet具有排序性,集合内的元素按照特定顺序输出按照树结构存放。Map(映射)下包含HashMap和TreeMap,HashMap同样具有散列的特性不再赘述,TreeMap同样具有树的排序特性不再赘述。
  4. 遍历器类Iterator只能遍历Collection(类集)下的实现类;Map(映射)没有提供遍历器,但是可以将映射中的对象转化为Collection(类集)“视图”,再调用遍历器实现遍历。
  5. Comparator接口用于给出排序比较的统一方法,类实现该接口并将该类的实例对象初始化Set或Map即可。

二、正篇

1.框架结构

提到框架,必包含预先抽象好的接口、相应接口的具体实现类等。可以先看可视化抽象图如下:

①简单版图解:
简单版图解,加载中
②稍详细版图解:
稍详细版图解,加载中

2. Collection(译:类集)框架接口部分

Collection框架下主要的接口如下表,参照框架图解更加清晰

接口解释
Collection接口位于集合框架的顶层,能操作对象组。
List接口扩展Collection接口去处理序列(即对象的列表)。
Set接口扩展Collection接口去处理集合,集合中的元素必须是唯一的,不能出现两个复制对象。
SortedSet接口扩展Set接口去处理排序集合。

备注:除了以上类集接口之外,类集也使用Comparator、Iterator和ListIterator接口,Comparator接口定义了两个对象比较方法,即Iterator和ListIterator接口类集中定义的对象。

序列和集合的区别:记住序列的特点是有先后顺序且允许实列对象重复,而集合的特点则是元素的排列没有先后顺序且必须保证实例对象元素的唯一性(实例的不重复性,不是类类型的不重复性)

以下将会分别讨论主要的接口和接口的实现类
(接口定义统一操作,达到统一规范实现类的作用;实现类各自实现继承的操作,并且实现类可以扩展出属于自己的方法)

①Collection接口

collection接口是构造集合框架的基础,它声明了集合框架类都应具有的核心方法。以下是其核心方法列表

方法解释
boolean add(Object o)将obj加入到调用集中,如果obj被加入到类集中了,则返回true;如果obj已经是类集中的一个成员或类集不能被复制时,则返回false。备注:参数使用的是Object类型,意味着Collection中可以加入的只要是对象即可,而不需要是统一类型的对象。特别提醒的是由于基本类型(int等)不属于对象,固不能将基本类型加入到Collection(类集)中。
boolean addAll(Collection c)将c中所有的元素都加入到调用集中,r如果操作成功(元素被加入了),则返回true,否则返回false。
Void clear()从调用类集中清除所有元素
boolean contains(Object o)如果o是类集中的一个元素,则返回true,否则返回false。
boolean containsAll(Collection c)如果调用类集包含了c中所有元素,则返回true,否则返回false。
boolean eauals(Object o)如果调用类集与o相等,则返回true,否则返回false。备注:此处的object为类集类即可(不要传入类集中的元素),类集类中的元素如果按照equals()方法都返回true则该方法返回true。
Int hashCode()返回调用类集的hash码
boolean isEmpty()判断调用类集是否为空,为空则返回true,否则返回false。
Iterator iterator()返回调用类集的迭代程序。备注:改为迭代器类比较好。
boolean remove(Object o)从调用集中移除一个o的实例,如果被移除了,则返回true,否则返回false。
boolean removeAll(Collection c)从调用集中移除c集中所有的实列,如果类集被改变了(也就是说元素被删除了),则返回true,否则返回false。备注:调用集只会删除掉在c中并且包含在调用集的元素,简单的举个例子:设调用集A为{a,b,d},c集为{e,d},则A.removeAll©结果为{a,b}。接收到我的想法了没?
boolean retainAll(Collection c)删除调用集中除了包含在c中元素之外的全部元素,如果类集被改变了(也就是说元素被删除了),则返回true,否则返回false。备注: 调用集会删除掉除了调用集与c集共有元素部分的元素,也就是说调用集只会留下调用集与c集的公共实例对象元素,简单的举个例子:设调用集A为{h, g, k},c集为{g, d, p},则A.retainAll©结果为{g}。
int size()返回调用类集中元素的个数。
Object[] toArray()返回一个数组,该数组包含了所有存储在调用类集中的元素。数组元素是类集元素的拷贝。
Object[] toArray(Object array[])返回一个数组,该数组仅仅包含了那些类型与数组元素类型匹配的类集元素。数组元素是类集元素的拷贝。如果array的大小与匹配元素的个数相等,他们将被返回到array。如果array的大小比匹配的元素个数小,将分配并返回一个所需大小的新数组。如果array的大小比匹配的元素的个数大,在数组中,在类集元素之外的单元被置为null。注意:如果任意类集元素的类型都不是array的子类型,则引发一个ArrayStoreException异常。

②List接口

定义:List接口扩展了Collection并声明存储一系列元素的类集特性。使用一个基于0的下标,元素可以通过它们在列表中的下标进行插入和访问。并且一个列表允许包含复制元素(也就是两个元素类型和值都相同)。
备注:index都是从0 开始
除了Collection定义的方法之外,List还定义了自己的方法:

接口解释
void add(int index, Object obj)将对象加入调用列表,插入位置有index传递,之前已经存在与列表中的元素,在插入下标以及插入下标之后的所有元素都后移,没有元素被覆写。
boolean allAll(int index, Collection c)将c中所有元素插入列表中,插入位置有index传递,之前已经存在与列表中的元素,在插入下标以及插入下标之后的所有元素都后移,没有元素被覆写。如果调用列表改变了则返回true,否则返回false。
Object get(int index)获得列表中index位置上的对象
int indexOf(Object obj)返回调用列表中obj第一个实例的下标,如果obj不是列表中的元素则返回-1。
int lastIndexOf(Object obj)返回调用列表中obj最后一个实例的下标,如果obj不是列表中的元素则返回-1。
ListIterator listIterator()返回调用列表开始的迭代类对象。备注:ListIterator类是List接口的内部类,同样继承并扩展了Iterator类,具有更多针对于List的新功能。
ListIterator listIterator(int index)返回调用列表指定下标处开始的迭代类对象
Object remove(int index)移除调用列表index下标下的元素并返回元素对象,当元素对象被删除以后,列表相当于被压缩了,所有的下标都减一。
Object set(int index, Object obj)修改调用列表指定下标index下的元素的进行修改,改为obj对象。
List subList(int start ,int end )返回一个列表,该列表包括了调用列表中从start到end-1的元素。返回的列表中的元素也被调用列表中的对象引用。

③Set接口

集合接口定义了一个集合,它扩展了Collection并声明了不允许复制元素的类集特性(也就是不能存在两个类型和值都相同的实例对象)。

因此,如果试图将复制元素加入到集合之中,add()方法将返回false。
此外,Set接口没有定义任何附加的方法。

④SortSet接口

SortSet接口扩展了Set并声明了按升序排列的集合特性。

核心方法如下:

接口解释
Comparator comparator()返回被调用被排序集合的比较方法,如果对该集合使用自然排序,则返回null。
Object first()返回被调用被排序集合的d第1个元素。
SortedSet headSet(Object end)返回一个包含那些小于end的元素的SortedSet,并且那些元素必须是包含在调用排序集合中,返回的排序集合中的元素也被调用的排序集合所引用。
Object end()返回被调用被排序集合的最后1个元素。
SortedSet tailSet(Object start)返回一个包含那些大于或等于start的元素的SortedSet,并且那些元素必须是包含在调用排序集合中,返回的排序集合中的元素也被调用的排序集合所引用。
SortedSet subSet(Object start, Object end)返回一个SortedSet,它包括了从start到end-1的元素。返回类集中的元素也被调用对象所引用。

3. Collection(译:类集)框架相关实现类部分

Collection接口的相关规范第一部分已经介绍了,还需要关注Collection接口的标准实现类。有一些类提供了完整的可以被使用的工具; 而另一些类是抽象的,用来起“提供主框架工具和作为创建具体类集的起点”的作用。

标准的Collection接口实现类总结如下表:

解释
AbstractCollection实现大多数的Collection接口
AbstractList扩展了AbstractCollection并实现大多数List接口
AbstractSequentialList为了被类集使用而扩展AbstractList,该类集是连续而不是用随机的方式访问其元素。
ArrayList通过扩展AbstractList来实现动态数组。
LinkedList通过扩展AbstractSequentialList来实现链接表。
AbstractSet通过扩展AbstractCollection来实现大多数Set接口
HashSet为了使用散列表而扩展AbstractSet。
TreeSet实现了一个存储在树中的一个集合,扩展AbstractSet。

①ArrayList实现类

ArrayList类扩展AbstractList类并执行List接口。(意味着ArrayList实现了具体的方法,并继承了已有的父类方法)

ArrayList支持可随需要增长的动态数组。
在java中标准数组是定长的,在数组被创建后不能被加长或者缩短,这意味着需要事先知道数组可以容纳多少元素,但是事实上至于运行的时候才知道需要多大的数组,或者数组长度需要动态地改变。

特点:ArrayList允许所有元素,包括null,可以根据索引位置对集合进行快速的随机访问。缺点是向指定的索引位置插入对象或者删除对象的速度比较慢。(适合于查询修改频率非常高的场景;不适合于插入删除元素频率非常高的场景,因为开销特别大。)

构造方法(3种)

  • ArrayList()//建立一个空的数组列表
  • ArrayList(Collection c)//建立一个数组列表,该数组列表由类集c中的元素初始化
  • ArrayList(int capacity)//创建一个数组列表,该数组有指定的初始化大小

②LinkedList实现类

LinkedList类扩展AbstractSequentialList类并执行List接口。

LinkedList提供一个链接列表的数据结构。
特点:该类采用链表结构存储对象,其优点是便于向列表中插入和删除对象元素,效率非常高。(适合于插入删除元素频率非常高的场景;不适合于查询频率高的场景,因为效率不高。)
构造方法

  • LinkedList()//建立一个空链接列表
  • LinkedList(Collection c)//建立一个空链接列表,并用类集c中的元素进行初始化。

备注:LinkList本身定义了一些自己的方法
Void addFirst(Object obj)
Void addLast(Object obj)

Object getFirst()
Object getLast()

Object removeFirst()
Object removeLast()

③HashSet实现类

HashSet类扩展AbstractSet并且实现Set接口。创建一个集合,集合使用散列表进行存储,而散列表则通过使用称之为散列法的机制来存储信息。

关于散列(Hash)的数据结构基础概念这里不在做过多的阐述。
采用散列法的HashSet的优点在于,即便是对于一个很大的集合,它允许的一些基本操作,如add()加入、contains()判包含(相当于查询)、remove()移除、size()判大小等方法的运行时间保持不变。

构造方法:

  • HashSet()//创建一个空的集合
  • HashSet(Collection c)//创建一个空的集合,并用类集c中的元素初始化hash集合
  • HashSet(int capacity)//创建一个初始容量为capacity的集合
  • HashSet(int capacity, int fillRatio)//创建一个初始容量为capacity、填充比fillRatio的集合
    (填充比的概念可参考数据结构散列的相关基础知识)
    备注:默认不指定填充比的话,将会置为0.75

散列集合HashSet中的元素是无序的,如果希望集合中存储的元素是有序的,则建议采用TreeSet。

④TreeSet实现类

TreeSet类为使用树来进行存储的set接口提供了一个工具,对象按升序存储。访问和减速的速度很快,适合于存储了大量需要快速检索的排序信息的情况。

构造方法

  • TreeSet()//构建一个空的树集合,该树集合将根据其元素的自然顺序按升序排序。
  • TreeSet(Collection c)//构造一个包含了类集c中的元素的树集合。
  • TreeSet(Comparator comp)//构造一个空的树集合,他按照由comp指定的比较方法进行排序
  • TreeSet(SortedSet ss)//构造一个包含了排序集合ss中元素的集合。

⑤通过迭代器方法访问类集 Iterator

通常开发者希望通过循环输出类集中的元素。解决的最佳方法是使用iterator(),iterator()是一个或者实现Iterator,或者实现ListIterator接口的对象。

Iterator可以完成通过循环输出类集内容,从而获得或删除元素。
ListIterator扩展了Iterator,允许双向遍历列表,并且可以修改单元。

Iterator定义的方法如下:

方法解释
boolean hasNext()如果存在更多的元素,则返回true,否则返回false
Object next()返回下一个元素,如果没有下一个元素,则会引发NoSuchElementException异常
void remove()删除当前元素,如果试图在调用next()方法之后调用remove()方法,则引发IllegalStateException异常。

ListIterator定义的方法如下:

方法解释
void add(Object obj)此处于书籍有出入,更改为:“obj将会插入到在调用了next()之后返回的对象之后,并且在下一次调用next()时将跳过该新插入的元素。”详细请看文章后面的代码实操展示。
boolean hasNext()如果存在下一个元素,则返回true,否则返回false。
boolean hasPrevious()如果存在前一个元素,则返回true,否则返回false。
Object next()返回下一个元素,如果不存在下一个元素,则会引发NoSuchElementException异常。
int nextIndex()返回下一个元素的下标,如果不存在下一个元素怒,则返回列表的大小(即对象的个数)。
Object previous()返回前一个元素,如果不存在下一个元素,则会引发NoSuchElementException异常
int previousIndex()返回前一个元素的下标,如果不存在前一个元素,则返回-1。
void remove()remove操作将会把next()方法访问的元素删除掉(previous()方法是同样的道理),如果在获得遍历类实例对象时没有获得任何元素就调用remove()方法将会引发IllegalStateException。
void set(Object obj)会修改当前对象的内容,也就是上一次调用next()或者previous()返回的对象。

特别注意:

  • next()方法返回的对象是nextIndex()返回下标对应的对象,最开始遍历器类实例配置的nextIndex为0;previous()是previousIndex()返回下标对应的对象,最开始遍历器类实例配置的previousIndex为-1;
  • 约定俗成的当前对象是指上一次调用next()方法(或者previous()方法)返回的对象。

⑥迭代器部分方法的代码实操

测试遍历器的add()方法,可以看到遍历器并不能通过之前获得的遍历器对象查询到,如果强行查询会报错。测试遍历器的add()方法的图片
报错1
这一次在插入元素之后访问前一个元素,可以看到它遍历了两次这个插入的元素,需要特别注意
11
12
为了搞清楚究竟是什么原因导致的以上现象,我们决定用下标索引测试一下:
21
在这里插入图片描述
在插入了新元素之后下标发生了改变,与正常的步骤做对比:
在这里插入图片描述
在这里插入图片描述
测试移除操作(仔细观察索引的变化):
在这里插入图片描述
在这里插入图片描述
测试修改操作:
在这里插入图片描述
在这里插入图片描述

选取了一部分容易混淆的操作,值得多留意一下。

4. Map(译:映射)接口部分

映射,也就是map,是一个存储关键字和值的关联,概念略微抽象,可以说映射是存储“关键字/值”对的对象。

关键字和值都是对象,但是关键字必须是唯一的,但值是可以被复制的。(复制的概念不在复述)。有的映射可以接收null关键字和null值,有的则不能。

接口解释
Map映射唯一关键字到值
Map.Entry描述映射中元素(“关键字/值”对)。是Map的一个内部类
SortedMap扩展Map以便关键字按升序保持

①Map接口

Map接口定义了核心方法如下:

方法解释
void clear()清空映射
boolean containsKey(Object k)如果调用映射中包含了作为关键字的k,则返回true,否则返回false。
boolean containsValue(Object v)如果映射中包含了值v,则返回true,否则返回false。
Set entrySet()返回包含了映射中的项的集合(Set)。该集合包含了类型Map.Entry的对象。这个方法为调用映射提供了一个集合“视图”。
boolean equals(Object obj)如果obj是一个Map并包含相同的输入,则返回true,否则返回false。
Object get(Object k)返回与关键字k相关联的值
int hashCode()返回映射的散列码
boolean isEmpty()如果调用映射是空的,则返回true,否则返回false。
Set keySet()返回一个包含调用映射中关键字的集合,这个方法为调用映射的关键字提供了一个集合“视图”
Object put(Object k,Object v)将一个输入加入调用映射,改写原先与关键字相关联的值。关键字和值分别为k和v。如果关键字已经不存在了,则返回null;否则,返回原先与关键字相关联的值。
void putAll(Map m)将所有来自m的输入加入调用映射
Object remove(Object k)删除关键字等于k的值
int size()返回映射中关键字/值对的个数
Collection values()返回一个包含了映射中的值的类集。这个方法为映射中的值提供了一个类集“视图”映射循环使用两个基本操作:get()和put()。使用put()方法可以将一个指定了关键字和值加的值入映射。为了得到值,可以通过将关键字作为参数来调用get()方法,调用返回该值。

注意:keySet()方法返回的是一个集合,而values()方法返回的是一个类集。是因为关键字不能有复制对象而值对象允许存在复制对象。注意区分好区别,如果值存在复制对象,则不要使用集合接收返回结果。

注意:虽然映射不是类集,但可以获得映射的类集“视图”。entrySet()方法返回一个包含了映射中元素的集合Set,keyet()方法得到关键字的类集“视图”,values()方法得到值的类集“视图”。所谓类集“视图”就是指将映射(Map)集成到类集框架(Collection框架下)的一种手段。

②SortedMap接口

SortedMap接口扩展了Map,他确保了各项按关键字升序排序。

方法解释
Comparator comparator()返回调用排序映射的比较方法。如果调用映射使用的是自然排序的话,则返回null。
Object firstKey()返回调用映射的第1个关键字
Object lastKey()返回调用映射的最后1个关键字
SortedMap headMap(Object end)返回一个排序映射,该映射包含了那些关键字小于end的映射输入(也就是排序映射)。
SortedMap subMap(Object start,Object end)返回一个排序映射,该映射包含了那些关键字小于end同时大于start的映射输入(也就是排序映射)。
SortedMap tailMap(Object start)返回一个映射,该映射包含了那些关键字大于等于start的输入排序映射。

③Map.Entry接口

Map.Entry接口使得可以操作映射的输入。简单说来就是映射中存放的键值对是Map.Entry对象。

有如下一些方法:

接口方法
boolean equals(Object obj)如果obj是一个关键字和值都与调用对象相等的Map.Entry,则返回true
Object getKey()返回该映射项的关键字
Object getValue()返回该映射项的值
int hashCode()返回该映射项的散列值
Object setValue(Object v)用v值修改映射输入中的值

5. Map(译:映射)相关实现类部分

实现类结构如下表:

解释
AbstractMap实现大多数的Map接口
HashMap将AbstractMap扩展到使用散列表
TreeMap将AbstractMap扩展到使用树

①HashMap类

HashMap类使用散列表实现Map接口。允许一些基本操作,如get()和put()的运行时间保持恒定,即便对大型集合也是如此(将键值对对象看作一个对象,映射就相当于一个集合)

散列映射并不保证元素的顺序,因此,元素加入散列映射的顺序并不一定是他们呢被迭代方法读出的顺序。

构造方法:

  • HashMap()//构造一个默认的散列映射。
  • HashMap(Map m)//用m的元素初始化散列映射。
  • HashMap(int capacity)//将散列映射的容量初始化为capacity。
  • HashMap(int capacity, float fillRatio)//初始化散列映射的容量和填充比。

②TreeMap类

TreeMap类通过使用树实现Map接口。TreeMap提供了按排序顺序存储关键字/值对的有效手段,同时允许快速检索。

构造方法:

  • TreeMap()//构造一个空的树映射
  • TreeMap(Comparator comp)//使用比较方法构造一个空的树映射
  • TreeMap(Map m)//采用关键字的自然排序来构造,并用映射m来进行初始化树映射
  • TreeMap(SortedMap sm)//用sm来初始化树映射,并采用与树映射一样的比较方法。

③比较方法:Comparator接口

TreeSet和TreeMap都按排序顺序存储元素。没有指定元素的比较方法,java默认会按照自然顺序进行比较排序。如果开发者想要自己定义元素的比较方法,java提供了Comparator接口来帮助解决。

Comparator接口定义了两个方法:compare()和equals()

Int compare(Object obj1,Object obj2)
当两个对象相等时,该方法返回0;当ob1j大于obj2时,返回一个正值,当ob1j小于obj2时,返回一个负值。

Boolean equals(Object obj)用于测试一个对象是否与调用比较方法相等。

Obj是被用来进行相等测试的对象。如果obj与调用对象都是Comparator的对象,并且使用相同的排序,该方法返回true,否则返回false。重载equals()方法是没有必要的,大多数简单的比较方法都不这样做。

三、尾文

Vector, Stack,HashTable等属于旧版本的遗留问题,但是都已经经过了重新的调整以支持累计框架,将会在以后的学习笔记中介绍

较完整的框架图解如下:

在这里插入图片描述

学习笔记参考书籍:
《Java从入门到精通》 作者:国家863中部软件孵化器

本文如有不正确的地方,欢迎指出,欢迎交流。

最后

以上就是健忘曲奇为你收集整理的浅谈JAVA类集框架(JAVA集合类)【学习笔记】一、前置二、正篇三、尾文的全部内容,希望文章能够帮你解决浅谈JAVA类集框架(JAVA集合类)【学习笔记】一、前置二、正篇三、尾文所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部