map/multimap容器类型

2024-04-04 13:18
文章标签 类型 容器 map multimap

本文主要是介绍map/multimap容器类型,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

map和multimap将key/value pair当做元素,进行管理。它们可根据key的排序准则自动将元素排序。multimap允许重复元素,map不允许。

一、map/multimap

在使用map和multimap之前,先必须包含头文件<map>
#include <map>
在其中,map和multimap被定义为命名空间std内的class template:

namespace std {template<class Key, class T,<class Compare = less<Key>,<class Allocator = allocator<pair<const Key,T> > >class map;template<class Key, class T,<class Compare = less<Key>,<class Allocator = allocator<pair<const Key,T> > >class multimap;
}
第一个template参数被当做元素的key,第二个template参数被当做元素的value。
第三个参数可有可无,用它来定义排序准则。缺省准则less-这是一个仿函数,以opertor<对元素进行比较,以便完成排序。
第四个template参数可有可无,用它来定义内存模型。缺省的内存模型是allocator,由C++标准程序库提供。

map<int> c;
map<int> c(op);
map<int> c1(c2);
map<int> c(beg,end,op);
c.size();
c.empty();
c.max_size();
c.count(key);
c.find(key);
c.lower_bound(key);
c.upper_bound(key);
c.equal_range(key);
c1.swap(c2)或全局函数swap(c1,c2);
c.begin();
c.end();
c.rbegin();
c.rend();
c.insert(elem);
c.insert(pos, elem);
c.insert(beg,end);
c.erase(elem);//返回移除元素个数
c.erase(pos);//无返回值
c.erase(beg,end);//无返回值
c.clear();
c[key];//指向键值为key的元素,如果不存在,就安插该元素。

①为了避免隐式型别转换,可以用value_type传递正确型别。
std::map<std::string, float> coll;
coll.insert(std::map<std::string, float>::value_type("otto", 22.3));
②应用pair<>
std::map<std::string, float> coll;
coll.insert(std::pair<std::string, float>("otto", 22.3));
coll.insert(std::pair<const std::string, float>("otto", 22.3));
③应用make_pair()
std::map<std::string, float> coll;
coll.insert(std::make_pair("otto", 22.3));
///
std::map<std::string, float> coll;
if (coll.insert(std::make_pair("otto", 22.3)).second) {std::cout<<"OK, could insert otto/22.3"<<std::endl;
} else {std::cout<<"Oops, could not insert otto/22.3"<<"(lkey otto already exists)"<<std::endl;
}

StringFloatMap coll;
StringFloatMap::iterator pos = coll.find(key);
if (pos != coll.end()) {coll.erase(pos);
}
///
typedef std::map<float, std::string, std::greater<float> > coll;

typedef std::map<std::string, float, std::greater<std::string> > StringFloatMap;

///

//下面是移除"迭代器所指元素"的正确做法:

typedef std::map<std::string, float> StringFloatMap;
StringFloatMap coll;
StringFloatMap::iterator pos;
//...
//remove all elements having a certain value
for (pos = coll.begin(); pos != coll.end(); ++ pos) {if (pos->second == value) {coll.erase(pos ++);} else {++ pos;}
}
二、应用实例

#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;	
void printAll(const map<string, float> &smap)
{map<string, float>::const_iterator iter;for (iter = smap.begin(); iter != smap.end(); ++ iter) {cout<<"stock:"<<iter->first<<"\t"<<"price:"<<iter->second<<endl;}
}
void printVect(vector<pair<string, float> >& svect) 
{vector<pair<string, float> >::iterator iter;cout<<"Sort By Value :\n";for (iter = svect.begin(); iter != svect.end(); ++ iter) {cout<<"stock:"<<iter->first<<"\t"<<"price:"<<iter->second<<endl;}		
}
int SortByValue(pair<string, float>& x, pair<string, float>& y) 
{return x.second < y.second;
}
void MapSortByValue(map<string, float> &smap, vector<pair<string, float> >& svect)
{map<string, float>::iterator pos;for (pos =  smap.begin(); pos != smap.end(); ++ pos) {//svect.push_back(pair<string, float>(pos->first, pos->second));svect.push_back(make_pair(pos->first, pos->second));}sort(svect.begin(), svect.end(), SortByValue);
}
int main(int argc, char **argv)
{//create map/ associate array//keys are string//value are floattypedef map<string, float> StringFloatMap;//create empty containerStringFloatMap coll;//insert some elementscoll["BASF"] = 368.50;coll["VW"] = 413.50;coll["Daimler"] = 819.00;coll["BMW"] = 834.00;coll["Siemens"] = 842.20;cout<<"Print All Elements first\n";printAll(coll);StringFloatMap::iterator pos;for (pos = coll.begin(); pos != coll.end(); ++ pos) {pos->second  *= 2;}cout<<"Print All Elements second\n";printAll(coll);//rename key from "VW" to  "Volkswagen"//only provided by exchanging elementcoll["Volkswagen"] = coll["VW"];coll.erase("VW");cout<<"Print All Elements third\n";printAll(coll);//sort by valuevector<pair<string, float> > svect; MapSortByValue(coll, svect);  printVect(svect);return 0;
}
运行结果:

三、仿函数应用

#include <iostream>
#include <map>
#include <string>
#include <algorithm>using namespace std;//function object to compare string
//allow you to set the comparison criterion at runtime
//allow you to compare case insensitiveclass RuntimeStringCmp {public://constants for the comparison criterionenum cmp_mode {normal, nocase};private://actual comparison valueconst cmp_mode mode;//auxiliary function to compare case insensitivestatic bool nocase_compare(char c1, char c2) {return toupper(c1) < toupper(c2);}public://constructor: initializes the comparsison criterionRuntimeStringCmp(cmp_mode m=normal) : mode(m){}//the comparison bool operator() (const string &s1, const string &s2) const {if (mode == normal) {return s1 < s2;} else {return lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), nocase_compare);}}};//container type:
//map with
//string keys
//string values
//the special comparison object typetypedef map<string, string, RuntimeStringCmp> StringStringMap;//funciton that fills and prints such containers
void fillAndPrint(StringStringMap& coll);int main(int argc, char **argv)
{//create a container with the default comparison criterionStringStringMap coll;fillAndPrint(coll);//create an object for case-insensitive comparisonRuntimeStringCmp ignorecase(RuntimeStringCmp::nocase);//create a container with the case-insensitive//comparisons critersionStringStringMap coll2(ignorecase);fillAndPrint(coll2);return 0;
}void fillAndPrint(StringStringMap& coll)
{//fill insert elements in random ordercoll["Deutschland"] = "Germany";coll["deutsch"] = "German";coll["Haken"] = "snag";coll["arbeiten"] = "work";coll["Hund"] = "dog";coll["gehen"] = "go";coll["Unternehmen"] = "enterprise";coll["unternehmen"] = "undertake";coll["gehen"] = "walk";coll["Bestatter"] = "undertaker";//print elementsStringStringMap::iterator pos;for (pos = coll.begin(); pos != coll.end(); ++ pos) {cout<<pos->first.c_str()<<"\t"<<pos->second.c_str()<<"\n";}cout<<endl;}

=======================

// STLTest2.cpp : 定义控制台应用程序的入口点。
//


#include "stdafx.h"
#include <stdlib.h>    // Need random(), srandom()
#include <time.h>      // Need time()
#include <string>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <deque>
#include <list>
#include <vector>
#include <functional> // Need ptr_fun()
#include <map>
#include <stack>
#include <set>

using namespace std;
typedef struct tagEmployeeInfo
{
    int      nID;
    string   strName;
 bool operator < (tagEmployeeInfo const& _A) const
    {
        //这个函数指定排序策略,按nID排序,如果nID相等的话,按strName排序
        if(nID < _A.nID)  return true;
        if(nID == _A.nID) return strName.compare(_A.strName) < 0;
        return false;
    }

}EmployeeInfo, *PEmployeeInfo;  //职员信息

//仿函数
class ESort
{
    public:
    bool operator() (EmployeeInfo const &_A, EmployeeInfo const &_B) const
    {
            if(_A.nID < _B.nID) return true;
            if(_A.nID == _B.nID) return _A.strName.compare(_B.strName) < 0;
            return false;
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
 int nSize;
 //用职员信息映射分数
 map<EmployeeInfo, int>mapEmployee;
 map<EmployeeInfo, int>::iterator iter;
 EmployeeInfo employeeInfo;
 employeeInfo.nID = 1;
 employeeInfo.strName = "jiayp";
 mapEmployee.insert(pair<EmployeeInfo, int>(employeeInfo, 90));
 employeeInfo.nID = 2;
 employeeInfo.strName = "cdh";
 mapEmployee.insert(pair<EmployeeInfo, int>(employeeInfo, 80)); 
 for (iter=mapEmployee.begin(); iter!=mapEmployee.end(); iter++)
 {
     cout<<iter->first.nID<<endl<<iter->first.strName<<endl<<iter->second<<endl;
 }

 //利用仿函数
 //map<EmployeeInfo, int,ESort>mapEmployee;
 employeeInfo.nID = 1;
 employeeInfo.strName = "jiayp";
    mapEmployee.insert(pair<EmployeeInfo, int>(employeeInfo, 90));
 employeeInfo.nID = 2;
 employeeInfo.strName = "cdh";
 mapEmployee.insert(pair<EmployeeInfo, int>(employeeInfo, 80)); 
 for (iter=mapEmployee.begin(); iter!=mapEmployee.end(); iter++)
 {
     cout<<iter->first.nID<<endl<<iter->first.strName<<endl<<iter->second<<endl;
 }
 int k;
 cin>>k;
 return 0;
}

=======================

C++ STL中Map的按Key排序和按Value排序

     map是用来存放<key, value>键值对的数据结构,可以很方便快速的根据key查到相应的value。假如存储学生和其成绩(假定不存在重名,当然可以对重名加以区分),我们用map来进行存储就是个不错的选择。 我们这样定义,map<string, int>,其中学生姓名用string类型,作为Key;该学生的成绩用int类型,作为value。这样一来,我们可以根据学生姓名快速的查找到他的成绩。

        但是,我们除了希望能够查询某个学生的成绩,或许还想看看整体的情况。我们想把所有同学和他相应的成绩都输出来,并且按照我们想要的顺序进行输出:比如按照学生姓名的顺序进行输出,或者按照学生成绩的高低进行输出。换句话说,我们希望能够对map进行按Key排序或按Value排序,然后按序输出其键值对的内容。

一、C++ STL中Map的按Key排序

       其实,为了实现快速查找,map内部本身就是按序存储的(比如红黑树)。在我们插入<key, value>键值对时,就会按照key的大小顺序进行存储。这也是作为key的类型必须能够进行<运算比较的原因。现在我们用string类型作为key,因此,我们的存储就是按学生姓名的字典排序储存的。

【参考代码】

#include<map>
#include<string>
#include<iostream>
using namespace std;typedef pair<string, int> PAIR;ostream& operator<<(ostream& out, const PAIR& p) {return out << p.first << "\t" << p.second;
}int main() {map<string, int> name_score_map;name_score_map["LiMin"] = 90; name_score_map["ZiLinMi"] = 79; name_score_map["BoB"] = 92; name_score_map.insert(make_pair("Bing",99));name_score_map.insert(make_pair("Albert",86));for (map<string, int>::iterator iter = name_score_map.begin();iter != name_score_map.end();++iter) {cout << *iter << endl;}return 0;}

【运行结果】

 

大家都知道map是stl里面的一个模板类,现在我们来看下map的定义:

template < class Key, class T, class Compare = less<Key>,class Allocator = allocator<pair<const Key,T> > > class map;

它有四个参数,其中我们比较熟悉的有两个: Key 和 Value。第四个是 Allocator,用来定义存储分配模型的,此处我们不作介绍。

现在我们重点看下第三个参数: class Compare = less<Key> 

这也是一个class类型的,而且提供了默认值 less<Key>。 less是stl里面的一个函数对象,那么什么是函数对象呢?

所谓的函数对象:即调用操作符的类,其对象常称为函数对象(function object),它们是行为类似函数的对象。表现出一个函数的特征,就是通过“对象名+(参数列表)”的方式使用一个 类,其实质是对operator()操作符的重载。

现在我们来看一下less的实现:

template <class T> struct less : binary_function <T,T,bool> {bool operator() (const T& x, const T& y) const{return x<y;}
};

它是一个带模板的struct,里面仅仅对()运算符进行了重载,实现很简单,但用起来很方便,这就是函数对象的优点所在。stl中还为四则运算等常见运算定义了这样的函数对象,与less相对的还有greater:
template <class T> struct greater : binary_function <T,T,bool> {bool operator() (const T& x, const T& y) const{return x>y;}
};

map这里指定less作为其默认比较函数(对象),所以我们通常如果不自己指定Compare,map中键值对就会按照Key的less顺序进行组织存储,因此我们就看到了上面代码输出结果是按照学生姓名的字典顺序输出的,即string的less序列。

我们可以在定义map的时候,指定它的第三个参数Compare,比如我们把默认的less指定为greater:

【参考代码】

#include<map>
#include<string>
#include<iostream>
using namespace std;typedef pair<string, int> PAIR;ostream& operator<<(ostream& out, const PAIR& p) {return out << p.first << "\t" << p.second;
}int main() {map<string, int, greater<string> > name_score_map;name_score_map["LiMin"] = 90; name_score_map["ZiLinMi"] = 79; name_score_map["BoB"] = 92; name_score_map.insert(make_pair("Bing",99));name_score_map.insert(make_pair("Albert",86));for (map<string, int>::iterator iter = name_score_map.begin();iter != name_score_map.end();++iter) {cout << *iter << endl;}return 0;
}

【运行结果】

 

现在知道如何为map指定Compare类了,如果我们想自己写一个compare的类,让map按照我们想要的顺序来存储,比如,按照学生姓名的长短排序进行存储,那该怎么做呢?

其实很简单,只要我们自己写一个函数对象,实现想要的逻辑,定义map的时候把Compare指定为我们自己编写的这个就ok啦。

struct CmpByKeyLength {bool operator()(const string& k1, const string& k2) {return k1.length() < k2.length();}
};

是不是很简单!这里我们不用把它定义为模板,直接指定它的参数为string类型就可以了。

【参考代码】

int main() {map<string, int, CmpByKeyLength> name_score_map;name_score_map["LiMin"] = 90; name_score_map["ZiLinMi"] = 79; name_score_map["BoB"] = 92; name_score_map.insert(make_pair("Bing",99));name_score_map.insert(make_pair("Albert",86));for (map<string, int>::iterator iter = name_score_map.begin();iter != name_score_map.end();++iter) {cout << *iter << endl;}return 0;
}

【运行结果】

 

二、C++ STL中Map的按Value排序

        在第一部分中,我们借助map提供的参数接口,为它指定相应Compare类,就可以实现对map按Key排序,是在创建map并不断的向其中添加元素的过程中就会完成排序。

现在我们想要从map中得到学生按成绩的从低到高的次序输出,该如何实现呢?换句话说,该如何实现Map的按Value排序呢?

        第一反应是利用stl中提供的sort算法实现,这个想法是好的,不幸的是,sort算法有个限制,利用sort算法只能对序列容器进行排序,就是线性的(如vector,list,deque)。map也是一个集合容器,它里面存储的元素是pair,但是它不是线性存储的(前面提过,像红黑树),所以利用sort不能直接和map结合进行排序。

       虽然不能直接用sort对map进行排序,那么我们可不可以迂回一下,把map中的元素放到序列容器(如vector)中,然后再对这些元素进行排序呢?这个想法看似是可行的。要对序列容器中的元素进行排序,也有个必要条件:就是容器中的元素必须是可比较的,也就是实现了<操作的。那么我们现在就来看下map中的元素满足这个条件么?

       我们知道map中的元素类型为pair,具体定义如下:

template <class T1, class T2> struct pair
{typedef T1 first_type;typedef T2 second_type;T1 first;T2 second;pair() : first(T1()), second(T2()) {}pair(const T1& x, const T2& y) : first(x), second(y) {}template <class U, class V>pair (const pair<U,V> &p) : first(p.first), second(p.second) { }
}

pair也是一个模板类,这样就实现了良好的通用性。它仅有两个数据成员first 和 second,即 key 和 value,而且

在 <utility>头文件中,还为pair重载了 < 运算符, 具体实现如下: 

template<class _T1, class _T2>inline booloperator<(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y){ return __x.first < __y.first|| (!(__y.first < __x.first) && __x.second < __y.second); }

重点看下其实现:
__x.first < __y.first || (!(__y.first < __x.first) && __x.second < __y.second)

这个less在两种情况下返回true,第一种情况:__x.first < __y.first  这个好理解,就是比较key,如果__x的key 小于 __y的key 则返回true。

第二种情况有点费解:  !(__y.first < __x.first) && __x.second < __y.second

当然由于||运算具有短路作用,即当前面的条件不满足是,才进行第二种情况的判断 。第一种情况__x.first < __y.first 不成立,即__x.first >= __y.first 成立,在这个条件下,我们来分析下  !(__y.first < __x.first)  && __x.second < __y.second

 !(__y.first < __x.first) ,看清出,这里是y的key不小于x的key ,结合前提条件,__x.first < __y.first 不成立,即x的key不小于y的key 

即:  !(__y.first < __x.first)  &&   !(__x.first < __y.first )   等价于   __x.first == __y.first ,也就是说,第二种情况是在key相等的情况下,比较两者的value(second)。

这里比较令人费解的地方就是,为什么不直接写 __x.first == __y.first 呢? 这么写看似费解,但其实也不无道理:前面讲过,作为map的key必须实现<操作符的重载,但是并不保证==符也被重载了,如果key没有提供==,那么 ,__x.first == __y.first 这样写就错了。由此可见,stl中的代码是相当严谨的,值得我们好好研读。

 现在我们知道了pair类重载了<符,但是它并不是按照value进行比较的,而是先对key进行比较,key相等时候才对value进行比较。显然不能满足我们按value进行排序的要求。

而且,既然pair已经重载了<符,而且我们不能修改其实现,又不能在外部重复实现重载<符。

typedef pair<string, int> PAIR;
bool operator< (const PAIR& lhs, const PAIR& rhs) {return lhs.second < rhs.second;
}

如果pair类本身没有重载<符,那么我们按照上面的代码重载<符,是可以实现对pair的按value比较的。现在这样做不行了,甚至会出错(编译器不同,严格的就报错)。

那么我们如何实现对pair按value进行比较呢? 第一种:是最原始的方法,写一个比较函数;  第二种:刚才用到了,写一个函数对象。这两种方式实现起来都比较简单。

typedef pair<string, int> PAIR;bool cmp_by_value(const PAIR& lhs, const PAIR& rhs) {return lhs.second < rhs.second;
}struct CmpByValue {bool operator()(const PAIR& lhs, const PAIR& rhs) {return lhs.second < rhs.second;}
};

接下来,我们看下sort算法,是不是也像map一样,可以让我们自己指定元素间如何进行比较呢?
template <class RandomAccessIterator>void sort ( RandomAccessIterator first, RandomAccessIterator last );template <class RandomAccessIterator, class Compare>void sort ( RandomAccessIterator first, RandomAccessIterator last, Compare comp );
我们看到,令人兴奋的是,sort算法和map一样,也可以让我们指定元素间如何进行比较,即指定Compare。需要注意的是,map是在定义时指定的,所以传参的时候直接传入函数对象的类名,就像指定key和value时指定的类型名一样;sort算法是在调用时指定的,需要传入一个对象,当然这个也简单,类名()就会调用构造函数生成对象。

这里也可以传入一个函数指针,就是把上面说的第一种方法的函数名传过来。(应该是存在函数指针到函数对象的转换,或者两者调用形式上是一致的,具体确切原因还不明白,希望知道的朋友给讲下,先谢谢了。)

【参考代码】

int main() {map<string, int> name_score_map;name_score_map["LiMin"] = 90;name_score_map["ZiLinMi"] = 79;name_score_map["BoB"] = 92;name_score_map.insert(make_pair("Bing",99));name_score_map.insert(make_pair("Albert",86));//把map中元素转存到vector中 vector<PAIR> name_score_vec(name_score_map.begin(), name_score_map.end());sort(name_score_vec.begin(), name_score_vec.end(), CmpByValue());// sort(name_score_vec.begin(), name_score_vec.end(), cmp_by_value);for (int i = 0; i != name_score_vec.size(); ++i) {cout << name_score_vec[i] << endl;}return 0;
}

【运行结果】








这篇关于map/multimap容器类型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/875876

相关文章

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

容器编排平台Kubernetes简介

目录 什么是K8s 为什么需要K8s 什么是容器(Contianer) K8s能做什么? K8s的架构原理  控制平面(Control plane)         kube-apiserver         etcd         kube-scheduler         kube-controller-manager         cloud-controlle

Mysql BLOB类型介绍

BLOB类型的字段用于存储二进制数据 在MySQL中,BLOB类型,包括:TinyBlob、Blob、MediumBlob、LongBlob,这几个类型之间的唯一区别是在存储的大小不同。 TinyBlob 最大 255 Blob 最大 65K MediumBlob 最大 16M LongBlob 最大 4G

Collection List Set Map的区别和联系

Collection List Set Map的区别和联系 这些都代表了Java中的集合,这里主要从其元素是否有序,是否可重复来进行区别记忆,以便恰当地使用,当然还存在同步方面的差异,见上一篇相关文章。 有序否 允许元素重复否 Collection 否 是 List 是 是 Set AbstractSet 否