C++、Java、python中的一些常见容器总结

主要参考:《数据结构与算法/leetcode/lintcode题解》C++参考手册、《疯狂Java》

<数据结构与算法>学习笔记(一)基础知识-基本数据结构

7.常见容器学习

1. C++

参考C++参考手册,C++有一个容器库,其中包含了一些常见的数据结构。一共有三种容器:顺序容器、关联容器和无序关联容器。

顺序容器关联容器无序关联容器容器适配器
按顺序访问快速查找快速查找且无序提供顺序容器的不同接口
array (c++11)setunordered_set(c++11起)stack(LIFO)栈
vectormapunordered_map(c++11起)queue(FIFO)队列
dequemultisetunordered_multiset(c++11起)priority_queue(优先级)
forward_list (c++11起)multimapunordered_multimap(c++11起)
list

适配器是标准库中的一个通用概念,它是一中机制,使某种事物的行为看起来像另一种事物。一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型。

适配器要求满足容器
stack必须提供函数:back()、push_back()、pop_back()vector、deque(默认)和list
queueback()、front()、push_back()、pop_front()deque和list
priority_queue迭代器必须满足遗留随机访问迭代器的要求,front()、push_back()、pop_back()vector和deque

1.顺序容器

顺序容器在C++参考手册上都有较详细的介绍,使用难度都不算大,所以不用特意做什么说明。

下面是所有的顺序容器的常用操作的示例

#include<array>
#include<iostream>
#include<string>
#include<algorithm>
#include<iterator>
#include<vector>
#include<deque>
#include <forward_list>
#include <list>
class p {
private: 
	std::string name;
	int id;
public:
	p(std::string n, int i) :name(n), id(i){}
	std::string getName() { return name; }
	int getId() { return id; }
	std::string toString() { std::string str = name;str.append(",id:"); str.append(std::to_string(id)); return str; }
};

int main() {
	//----array----
	std::cout << "array" << std::endl;
	std::array<int, 3>a1{ {1,2,3} };
	std::array<int, 3>a2 = { 1,2,3 };
	std::array<std::string, 2>a3 = { "a","b" };
	std::sort(a1.begin(), a1.end());
	std::reverse_copy(a2.begin(), a2.end(), std::ostream_iterator<int>(std::cout, " "));
	std::cout << "\n";
	std::cout << a1.size() << std::endl;
	std::array<int, 10>test;
	test.fill(1);
	a3.fill("t");
	for (const auto& s : a3)
		std::cout << s << " ";
	//---vector---
	std::cout << "\nvector" << std::endl;
	//构造与复制
	std::vector<int> v1{ {1,2,3} };
	std::vector<int> v2(v1);
	std::vector<char>v3 = { 'a','b','c' };
	std::vector<int>v4(10, 0);//10个0
	std::vector<int>v5(v4.begin(), v4.end());
	std::vector<int>v6 = v5;
	std::cout << v1.front() << "---" << v1.back() << std::endl;//访问头部和尾部元素
	std::cout << v1[1] << std::endl;
	std::vector<p>ps;
	//添加元素与删除元素
	ps.emplace_back("Tom", 1);//将参数传递给元素类型的构造函数
	ps.push_back(p("Jerry", 2));
	ps.emplace(ps.begin(), "Jackey", 3);//第一个参数为容器的迭代器
	for (auto & c : ps)
		std::cout << c.toString() << std::endl;
	for (const auto& c : v2)
		std::cout << c << " ";
	std::cout << std::endl;
	v2.pop_back();//删除尾部元素
	for (const auto& c : v2)
		std::cout << c << " ";
	std::cout << std::endl;
	v2.insert(v2.begin(), 3);
	v2.insert(v2.begin(), 2,1);
	v2.insert(v2.begin(), v1.begin(), v1.end());//插入元素
	for (const auto& c : v2)
		std::cout << c << " ";
	std::cout << std::endl;
	v2.erase(v2.begin());			//删除元素
	v2.erase(v2.begin(), v2.begin() + 3);
	for (const auto& c : v2)
		std::cout << c << " ";
	std::cout << std::endl;
	v2.swap(v1);	//交换容器
	for (const auto& c : v1)
		std::cout << c << " ";
	std::cout << std::endl;
	for (const auto& c : v2)
		std::cout << c << " ";
	std::cout << std::endl;
	std::cout << v1.size() << "--" << v1.max_size() << "--" << v1.capacity() << std::endl; 
	//---deque---双端列表
	std::cout << "deque" << std::endl;
	//构造与复制
	std::deque<int>d1{ 1,2,3 };
	std::deque<int>d2 = { 1,2,3,4 };
	std::deque<int>d3{ {1,2,3,4,5} };
	std::deque<int>d4(d1);
	std::deque<int>d5(10, 2);
	std::deque<int>d6(d1.begin(), d1.begin());
	std::deque<int>d7;
	//添加与删除元素
	d7.assign(10, 1);
	for (int i : d7)
		std::cout << i << " ";
	std::cout << "/n" << d1.front() << " --" << d1.back() << std::endl;
	std::cout << d7.size() << "---" << d7.max_size() << std::endl;
	std::deque<p>d8;
	d8.emplace(d8.begin(), std::string("Flandre"), 1);
	d8.emplace_back("Remilia", 2);
	d8.emplace_front("Sakuya", 3);
	d8.push_back(p("Patchouli",4));//尾部添加
	d8.push_front(p("Meirin", 5));//头部
	for (auto c : d8)
		std::cout << c.toString() << std::endl;
	d2.insert(d2.begin(), 11);
	d2.insert(d2.begin(), d1.begin(), d1.end());
	d2.insert(d2.begin(), 3, 9);
	for (int i : d2)
		std::cout << i << " ";
	d2.erase(d2.begin());
	d2.erase(d2.begin(), d2.begin() + 4);
	d2.pop_back();
	d2.pop_front();
	for (int i : d2)
		std::cout << i << " ";
	d2.swap(d1);
	//forward_list 单链表
	std::cout << "\nforward_list" << std::endl;
	//构造与复制
	std::forward_list<int>f1{ 1,2,3 };
	std::forward_list<int>f2{ {1,2,3,4} };
	std::forward_list<int>f3 = { 1,2,3,4,5,6 };
	std::forward_list<int>f4(f1);
	std::forward_list<int>f6(f1.begin(), f1.end());
	std::forward_list<int>f7;
	f7.assign(5, 1);
	for (int i : f7)
		std::cout << i << " ";
	std::cout << std::endl;
	//添加与删除元素
	std::forward_list<p>f8;
	f8.emplace_front("Marisa", 2);
	f8.emplace_after(f8.begin(), "Reimu", 1);//如果容器为空时使用emplace_after会出错
	f8.push_front(p("Alice", 3));
	for (auto c : f8)
		std::cout << c.toString() << std::endl;
	f2.insert_after(f2.begin(), 11);
	f2.insert_after(f2.begin(), f1.begin(), f1.end());
	f2.insert_after(f2.begin(), 4, 9);
	std::cout << std::endl;
	for (auto c : f2)
		std::cout << c << " ";
	f2.pop_front();
	//auto it = f2.begin();
	//f2.erase_after(f2.begin(),++it );
	f2.erase_after(f2.begin());
	std::cout << std::endl;
	for (auto c : f2)
		std::cout << c << " ";
	//----list-----双链表
	std::cout << "\nlist" << std::endl;
	//构造与复制
	std::list<int>l1{ {1,2,3,4} };
	std::list<int>l2{ 1,2,3,4,5 };
	std::list < int > l3(l1);
	std::list<int>l4 = { 1,2,3,4,5,6,7 };
	std::list<int>l6(l1);
	std::list<int>l7(l6.begin(), l6.end());
	std::list<int>l8;
	l8.assign(6, 0);
	//添加与删除元素
	std::list<p>l9;
	l9.emplace(l9.begin(), "Kaguya", 1);
	l9.emplace_back("Fujiwara no Mokou", 2);
	l9.emplace_front("Kamishirasawa Keine", 3);
	l9.push_back(p("Yagokoro eirin", 4));
	l9.push_front(p("Reisen", 5));
	for (auto c : l9)
		std::cout << c.toString() << std::endl;
	l1.insert(l1.begin(), 8);
	l1.insert(l1.begin(), l2.begin(), l2.end());
	l1.sort();//排序
	l1.unique();//删除连续的重复元素
	l1.reverse();//反序
	l1.remove(1);//删除等于1的元素
	l1.remove_if([](int n) {return n > 5; });//删除大于5的元素
	for (auto c : l1)
		std::cout << c << " ";
	std::cout << std::endl;
	l1.pop_back();
	l1.pop_front();
	l1.erase(l1.begin());
	l1.erase(l1.begin(), --l1.end());
	for (auto c : l1)
		std::cout << c << " ";
	
}

输出

array
3 2 1
3
t t
vector
1---3
2
Jackey,id:3
Tom,id:1
Jerry,id:2
1 2 3
1 2
1 2 3 1 1 3 1 2
1 3 1 2
1 3 1 2
1 2 3
4--1073741823--8
deque
1 1 1 1 1 1 1 1 1 1 /n1 --3
10---1073741823
Meirin,id:5
Sakuya,id:3
Flandre,id:1
Remilia,id:2
Patchouli,id:4
9 9 9 1 2 3 11 1 2 3 4 11 1 2 3
forward_list
1 1 1 1 1
Alice,id:3
Marisa,id:2
Reimu,id:1

1 9 9 9 9 1 2 3 11 2 3 4
9 9 9 1 2 3 11 2 3 4
list
Reisen,id:5
Kamishirasawa Keine,id:3
Kaguya,id:1
Fujiwara no Mokou,id:2
Yagokoro eirin,id:4
5 4 3 2
3
C:\Users\xhh\Source\Repos\c++study\queue_S\Debug\queue_S.exe (进程 47068)已退出,代码为 0。
按任意键关闭此窗口. . .

2.关联容器

set:含有key类型的已排序集,用比较函数进行排序。搜素、移除和插入拥有对数复杂度。set通常以红黑树来实现。

set按照键排序,所以不会出现相等的元素。

map:有序键值对容器,元素的键是唯一的,用比较函数排序键。map通常也用红黑树来实现。

mutlisetmultimap是set和map对应的键值不唯一的,相等的键的排列顺序按照插入顺序,且不会改变。

#include<iostream>
#include<string>
#include<algorithm>
#include<iterator>
#include <set>
#include <map>
class p {
private:
	std::string name;
	int id;
public:
	p(std::string n, int i) :name(n), id(i) {}
	std::string getName() { return name; }
	int getId() { return id; }
	std::string const  toString() const { std::string str = name; str.append(",id:"); str.append(std::to_string(id)); return str; }
	constexpr bool operator<(const p& r)const { return id < r.id ; };//注意,非常重要的一步:提供内建运算符operator<重载,
	//这样提供了这个类的比较大小的途径,只有这样这种类的关联容器才能正常使用
};

int main()
{
	//---set---
	std::cout << "---set----" << std::endl;
	//构造和复制
	std::set<int>s1{ 1,2,3 };
	std::set<int>s2{ {1,2,3,4,5,6,7} };
	std::set<int>s3 = { 1,2,3 };
	//std::set<int>s4(s1);
	//std::set<int>s5(s1.begin(), s1.end());
	std::set<int>s6;
	//没有assign函数
	//插入和删除元素
	std::set<p>s7;
	s7.emplace("Shameimaru Aya", 1);
	s7.emplace_hint(s7.begin(), "Cirno", 2);
	s7.insert(s7.end(), p("Youmu", 3));
	for (auto c : s7)
		std::cout << c.toString() << std::endl;
	s1.insert(s1.begin(), 9);
	s1.insert(s2.begin(), s2.end());
	for (auto c : s1)
		std::cout << c << " ";
	std::cout << std::endl;
	s1.erase(s1.begin(),++s1.begin());
	s1.erase(3);
	s1.erase(s1.begin());
	for (auto c : s1)
		std::cout << c << " ";
	std::cout << std::endl;
	std::cout << s2.count(2) << std::endl;//返回匹配特定键的元素数量
	std::cout << *s2.find(2) << std::endl;//寻找
	std::cout << *s2.equal_range(3).second << std::endl;//返回匹配特定键的元素范围,
	std::cout << *s2.lower_bound(4) << std::endl;//返回指向首个不小于给定键的元素的迭代器
	std::cout << *s2.upper_bound(5) << std::endl;//返回指向首个大于给定键的元素的迭代器
	//---map-----
	std::cout << "\nmap" << std::endl;
	//构造和复制
	std::map<int, int>m1{{1,2},{3,4}};
	std::map<char, int>m2{ {std::make_pair('c',1),std::make_pair('d',2)} };
	std::map<int, int>m3(m1);
	std::map<int, int>m4 = { {1,2},{3,4},{5,6},{7,8} };
	std::map<p, int>m5;
	m5.emplace(std::make_pair(p("Yuyuko",1),2));
	m5.emplace(std::make_pair(p("Yakumo Yukari", 2), 3));
	m5.emplace_hint(m5.begin(), std::make_pair(p("Suika", 4),5));
	m5.try_emplace(p("Tenshi", 5), 1);
	m5[p("Ran", 6)] = 4;
	for (auto c : m5)
		std::cout << c.first.toString() << "-->value-->" << c.second << std::endl;
	std::string str = "Explicit is better than implicit.";
	//可以将map用于统计一段字符串中的字符
	std::map <char, int>m6;
	for(auto c:str)
	{
		if (m6.find(c) != m6.end())//寻找指定键值的元素,没有则返回容器的尾部迭代器end()
			m6[c]++;
		else
			m6[c] = 1;
	}
	for (auto& c : m6)
		std::cout << c.first << ":" << c.second << "  ";
	std::cout << std::endl;
	m6.erase(m6.begin());//删除迭代器指向的元素
	m6.erase(m6.begin(), ++m6.begin());//删除范围内的元素
	m6.erase('i');//删除指定键值的元素
	for (auto& c : m6)
		std::cout << c.first << ":" << c.second << "  ";
	std::cout << std::endl;

	return 0;
}

输出

---set----
Shameimaru Aya,id:1
Cirno,id:2
Youmu,id:3
1 2 3 4 5 6 7 9
4 5 6 7 9
1
2
4
4
6

map
Yuyuko,id:1-->value-->2
Yakumo Yukari,id:2-->value-->3
Suika,id:4-->value-->5
Tenshi,id:5-->value-->1
Ran,id:6-->value-->4
 :4  .:1  E:1  a:1  b:1  c:2  e:2  h:1  i:6  l:2  m:1  n:1  p:2  r:1  s:1  t:5  x:1
E:1  a:1  b:1  c:2  e:2  h:1  l:2  m:1  n:1  p:2  r:1  s:1  t:5  x:1

C:\Users\xhh\Source\Repos\c++study\queue_S\Debug\queue_S.exe (进程 61628)已退出,代码为 0。
按任意键关闭此窗口. . .

3. 无序关联容器

unordered_set:键值唯一,搜索、插入和移除具有平均常数时间复杂度。在内部元素不以特别的顺序排列,而是组织进桶中。元素被放进那个同依赖于其值的哈希。这允许对单独元素的快速访问。但不可修改容器元素。注意无序容器比较特殊,如果要用这一容器来装自定义的某个类,要么自定义一个这个类的散列函数然后再容器声明时使用,或者使用注入的std::hash特化。

#include<iostream>
#include<string>
#include<algorithm>
#include<iterator>
#include <unordered_map>
#include <unordered_set>
class p {
private:
	std::string name;
	int id;
public:
	p(std::string n, int i) :name(n), id(i) {}
	const std::string getName() const{ return name; }
	const int getId() const { return id; }
	std::string const  toString() const { std::string str = name; str.append(",id:"); str.append(std::to_string(id)); return str; }
	constexpr bool operator<(const p& r)const { return id < r.id ; };//注意,非常重要的一步:提供内建运算符operator<重载,
	//这样提供了这个类的比较大小的途径,只有这样这种类的关联容器才能正常使用
	constexpr bool operator==(const p& r)const { return id == r.id && name==r.name; }
};

// std::hash 的自定义特化能注入 namespace std
//要使用无序关联容器来装这个类的话,这一步是必要的
namespace std
{
	template<> struct hash<p>
	{
		typedef p argument_type;
		typedef std::size_t result_type;
		result_type operator()(argument_type const& s) const
		{
			result_type const h1(std::hash<std::string>{}(s.getName()));
			result_type const h2(std::hash<int>{}(s.getId()));
			return h1 ^ (h2 << 1); // 或使用 boost::hash_combine (见讨论)
		}
	};
}


int main()
{
	//unordered_set
	std::cout << "unordered_set" << std::endl;
	std::unordered_set<int>us1{{1,2,3,4,5}};
	std::unordered_set<int>us2(us1);
	std::unordered_set<int>us3 = { 1,2,3,4,5,6 };
	//插入和删除
	std::unordered_set<p>us4;
	us4.emplace("Rumia", 1);
	us4.emplace_hint(us4.begin(), p("Daiyousei", 2));
	us4.insert(us4.begin(), p("Lily White", 3));
	for (auto c : us4)
		std::cout << c.toString() << std::endl;
	
	us3.insert(us3.begin(), 7);
	us3.insert(us1.begin(), us1.end());
	for (auto c : us3)
		std::cout << c << " ";
	std::cout << std::endl;
	us3.erase(us3.begin());
	us3.erase(3);
	us3.erase(us3.begin(), ++us3.begin());
	for (auto c : us3)
		std::cout << c << " ";
	std::cout << std::endl;
	std::cout << *us3.find(5) << std::endl;
	//桶接口
	std::cout << us3.bucket_count() << std::endl;//返回桶的个数
	std::cout << us3.bucket(6) << std::endl;//返回装着特定的元素的桶
	for (int i = 0; i < us3.bucket_count(); ++i)
		std::cout << "第" << i << "个桶有" << us3.bucket_size(i) << "个元素" << std::endl;//返回指定桶的元素数量
	std::cout << us3.hash_function()(111) << std::endl;//返回计算键值哈希值的哈希函数

	//unordered_map
	std::cout << "unordered_map" << std::endl;
	//构造与复制
	std::unordered_map<int, int>um1{ {{1,2},{2,3},{3,4}} };
	std::unordered_map<int, int>um2 = { {1,2},{2,4} };
	std::unordered_map<int, int>um3(um1);
	std::unordered_map<char, int>um4 = { std::make_pair('a',1),std::make_pair('b',2) };
	//添加与删除
	std::unordered_map<p, int>um5;
	um5.emplace(p("Suika",1),1);
	um5.emplace_hint(um5.begin(), p("Yuka", 2), 2);
	um5.insert({ p("Shikieiki Yamaxanadu",3),3 });
	for (auto c : um5)
		std::cout << c.first.toString() << ":" << c.second << std::endl;
	um1.insert(um2.begin(),um2.end());
	um1.insert({ 5,6 });
	um1.insert({ 7,2 });
	um1.insert({ 6,6 });
	for (auto c : um1)
		std::cout << c.first << ":" << c.second << std::endl;;
	um1.erase(1);
	um1.erase(um1.begin(), ++um1.begin());
	um1.erase(um1.begin());
	for (auto c : um1)
		std::cout << c.first << ":" << c.second << std::endl;;
	std::cout << um1.bucket_count() << std::endl;//返回桶的个数
	std::cout << um1.bucket(5) << std::endl;//返回装着特定的元素的桶
	for (int i = 0; i < um1.bucket_count(); ++i)
		std::cout << "第" << i << "个桶有" << um1.bucket_size(i) << "个元素" << std::endl;//返回指定桶的元素数量
	std::cout << um1.hash_function()(111) << std::endl;//返回计算键值哈希值的哈希函数
	
	
	return 0;
}

输出

unordered_set
Rumia,id:1
Daiyousei,id:2
Lily White,id:3
1 2 3 4 5 6 7
4 5 6 7
5
8
30个桶有1个元素
第1个桶有1个元素
第2个桶有1个元素
第3个桶有1个元素
第4个桶有0个元素
第5个桶有0个元素
第6个桶有0个元素
第7个桶有0个元素
3605745226
unordered_map
Suika,id:1:1
Yuka,id:2:2
Shikieiki Yamaxanadu,id:3:3
1:2
2:3
3:4
5:6
7:2
6:6
5:6
7:2
6:6
8
00个桶有1个元素
第1个桶有0个元素
第2个桶有1个元素
第3个桶有1个元素
第4个桶有0个元素
第5个桶有0个元素
第6个桶有0个元素
第7个桶有0个元素
3605745226

C:\Users\xhh\Source\Repos\c++study\queue_S\Debug\queue_S.exe (进程 61952)已退出,代码为 0。
按任意键关闭此窗口. . .

4.容器适配器

stack :栈,先进后出。

queue:队列,先进先出

priority_queue:优先队列,提供常数时间的最大元素查找,对数代价的插入与释出。可用用户提供的compare更改顺序。

#include<iostream>
#include<stack>
#include <queue>
#include <vector>
#include <list>
#include <deque>

int main()
{
	std::vector<int>v1{ {1,2,3,4} };
	std::deque<int>d1{ {1,2,3,4,5,6} };
	std::list<int>l1{ {1,2,3,4,5,6,7} };
	std::stack<int, std::vector<int>>s1(v1);
	std::stack<int, std::deque<int>>s2(d1);
	std::stack<int, std::list<int>>s3(l1);
	std::stack<int>s4(d1);
	std::cout << s1.top() << std::endl;
	std::cout << s1.size() << std::endl;
	s1.push(1);//向栈顶插入元素
	for (int i=0,j=s1.size();i<j;++i)
	{
		std::cout << s1.top() << "  ";//访问栈顶元素
		s1.pop();//删除栈顶元素
	}
	//
	std::queue<int, std::deque<int>>q1(d1);
	std::queue<int, std::list<int>>q2(l1);
	std::queue<int>q3(d1);
	q1.push(10);//向队尾插入元素
	q1.emplace(11);
	std::cout << q1.front() << std::endl;//访问第一个元素
	std::cout << q1.back() << std::endl;//访问最后一个元素
	for(int i=0,j=q1.size();i<j;++i)
	{
		std::cout << q1.front() << " ";
		q1.pop();//删除首个元素
	}
	//
	std::priority_queue<int, std::vector<int>>p1(std::less<int>(),v1);
	std::priority_queue<int, std::deque<int>>p2(std::less<int>(),d1);
	std::priority_queue<int>p3(std::less<int>(),v1);//所以优先队列默认是使用容器vector
	std::cout << std::endl;
	p1.push(5);
	p1.push(3);
	std::priority_queue<int>p4(p3);
	std::cout << p1.top() << std::endl;
	std::cout << p1.size() << std::endl;
	for(int i=0,j=p1.size();i<j;++i)
	{
		std::cout << p1.top() << " ";
		p1.pop();
	}
	std::cout << std::endl;
	
	return 0;
}

输出

4
4
1  4  3  2  1  1
11
1 2 3 4 5 6 10 11
5
6
5 4 3 3 2 1

C:\Users\xhh\Source\Repos\c++study\queue_S\Debug\queue_S.exe (进程 71228)已退出,代码为 0。
按任意键关闭此窗口. . .

Heap,堆,通常指二叉堆,二叉堆是一个近似完全二叉树的数据结构。子节点的键值总是小于(或者大于)它的父节点,且每个节点的左右子树又是一个二叉堆(大根堆或者小根堆)。根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或者小根堆。常用作实现优先队列。

特点:

  • 以数组表示,以完全二叉树的方式理解
  • 唯一能同时最优的利用时间和空间的方法
  • 索引从0开始,父节点i的左子节点位置:2*i+1,右子节点位置2*i+2,子节点i的父节点位置floor((i-1)/2)

基本操作:

  • 最大堆调整:将堆的末端子节点作调整,使得子节点永远小于父节点
  • 创建最大堆:将堆所有数据重新排序
  • 堆排序:移除位于第一个数据的根节点,并作最大堆调整的递归运算
#pragma once
#include<algorithm>
#include<functional>
#include<stdexcept>
#include<unordered_map>
#include<utility>
#include<vector>

template<typename T,typename TComparator=std::equal_to<T>,typename PComparator=std::less<double>,typename Hasher=std::hash<T>>
class maxHeap
{
public:
	maxHeap(int m=2,const PComparator &c=PComparator(),const Hasher &hasj=Hasher(),const TComparator &tcomp=TComparator());
	~maxHeap();
	void push(double pri,const T &item);
	T const &top()const;
	void pop();
	bool empty()const;
	void decreaseKey(double newpri,const T &item);

private:
	void trickleUp(int loc);
	void trickDown(int loc);
	std::vector<std::pair<double,T>> store_;
	int m_;
	PComparator c_;
	std::unordered_map<T,size_t,Hasher,TComparator>KeyTOLocation_;
};

template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline maxHeap<T, TComparator, PComparator, Hasher>::maxHeap(int m, const PComparator& c, const Hasher& hash, const TComparator& tcomp):store_(),m_(m),KeyTOLocation_(100,hash,tcomp){}

template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline maxHeap<T, TComparator, PComparator, Hasher>::~maxHeap()
{
}

template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline void maxHeap<T, TComparator, PComparator, Hasher>::push(double pri, const T& item)
{
	std::pair<double,T>temp(pri,item);
	store_.push_back(temp);
	KeyTOLocation_[item]=store_.size();
	trickleUp(store_.size()-1);
}

template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline T const& maxHeap<T, TComparator, PComparator, Hasher>::top() const
{
	if(empty()){
		throw std::logic_error("can't top an empty heap");
	}
	return store_[0].second;
}

template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline void maxHeap<T, TComparator, PComparator, Hasher>::pop()
{
	if(empty())
		throw std::logic_error("can't pop an empty heap");
	store_[0]=store_[store_.size()-1];
	KeyTOLocation_.erase(store_[0].second);
	store_.pop_back();
	if(empty())return;
	trickleUp(0);
}

template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline bool maxHeap<T, TComparator, PComparator, Hasher>::empty() const
{
	return store_.empty();
}

template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline void maxHeap<T, TComparator, PComparator, Hasher>::decreaseKey(double newpri, const T& item)
{
	std::pair<double,T>temp=store_[KeyTOLocation_[item]];
	temp.first=newpri;
	trickleUp(KeyTOLocation_[item]);
}

template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline void maxHeap<T, TComparator, PComparator, Hasher>::trickleUp(int loc)
{
	int parent=(loc-1)/m_;
	while(parent>=0&&c_(store_[loc].first,store_[parent].first)){
		std::pair<double,T>temp=store_[loc];
		store_[loc]=store_[parent];
		store_[parent]=temp;
		double to_swap=KeyTOLocation_[store_[loc].second];
		KeyTOLocation_[store_[loc].second]=KeyTOLocation_[store_[parent].second];
		KeyTOLocation_[store_[parent].second]=to_swap;
		loc=parent;
		parent=(loc-1)/m_;
	}
}
template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline void maxHeap<T, TComparator, PComparator, Hasher>::trickDown(int loc)
{
	if(loc*m_+1>store_.size()-1)return;
	int smallerChild=m_*loc+1;
	for(size_t i=0;i<m_;++i){
		if(m_*loc+i<store_.size()){
			int rChild=m_*loc+i+1;
			if(c_(store_[rChild].first,store_[smallerChild].first)){
				smallerChild=rChild;	
			}
		}
	}
	if(c_(store_[smallerChild].first,store_[loc].first)){
		std::pair<double,T>temp=store_[loc];
		store_[loc]=store_[smallerChild];
		store_[smallerChild]=temp;
		double to_swap=KeyTOLocation_[store_[loc].second];
		KeyTOLocation_[store_[loc].second]=to_swap;
		trickDown(smallerChild);
	}
}

main.cpp

#include"maxHeap.h"
#include<iostream>
int main(){
	maxHeap<int>heap;
	heap.push(0.3,10);
	heap.push(0.1,22);
	heap.push(0.5,123);
	heap.push(0.8,12);
	
	while(!heap.empty()){
		std::cout<<heap.top()<<std::endl;
		heap.pop();
	}
	return 0;
	
}

2. Java

参考:《疯狂Java讲义》、Java官方文档

Java集合类主要有:Set、Queue、List、Map四种体系。和C++中的相关的容器类相对应,用途大概都能想到,但实际用法和C++还是有很大区别的,毕竟是不同的语言。Java的集合类主要由两个接口派生而来:CollectionMap,他们是集合框架的根接口,两个接口又包含一些子接口或实现类。它们的继承树分别如下:

Collection
Set
Queue
List
LinkedList
EnumSet
SortedSet
TreeSet
HashSet
LinkedHashSet
Deque
ArrayDeque
PriorityQueue
ArrayList
Vector
Stack
Map
EnumMap
IdentityHashMap
HashMap
LinkedHashMap
Hashtable
Properties
SortedMap
TreeMap
WeakHashMap

这些集合类定义在Java.Base模块下的Java.util包下。Java.util包的官方简介如下:

Contains the collections framework, some internationalization support classes, a service loader, properties, random number generation, string parsing and scanning classes, base64 encoding and decoding, a bit array, and several miscellaneous utility classes. This package also contains legacy collection classes and legacy date and time classes.

CollectionMap都是泛型接口。当然,Java里面实例化泛型类时可以不用尖括号显示指定类型,这样一般容器中元素的类型都是Object的,这样虽然没有语法错误,但IDEA会给出警告的,因为这样可能会出现一些问题,一般还是用棱形语法指定容器内元素类型比较好。

首先是Collection接口的通用的一些方法:

package algo_study;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;

public class collectionStudy {
    public static void main(String[] args) {
        Collection test=new ArrayList();
        test.add(122);
        test.add(1232);
        test.add(11);
        System.out.println("test集合的元素个数"+test.size());
        test.remove(122);
        test.add('a');
        test.add("adfs");
        test.forEach(obj->System.out.println("->"+obj));//用Lambda表达式遍历集合中的元素
        for(Iterator c = test.iterator();c.hasNext();) {//用迭代器遍历集合中的元素
            Object o = c.next();
            System.out.println(o);
            if (o.equals(11)) {
                c.remove();
            }
        }
        Iterator it=test.iterator();
        it.forEachRemaining(obj->System.out.println("-"+obj));//用迭代器提供的forEachRemaining方法的lambda来遍历
        Object[] oo=test.toArray();
        System.out.println(oo[0]);
        System.out.println(cal(test,ele->ele.equals('a')));
        //用stream来操作集合
        System.out.println(test.stream().filter(ele->ele.toString().length()>1).count());
    }
    public static int cal(Collection c, Predicate p){//利用Predicate来操作集合
        int t=0;
        for(Iterator it=c.iterator();it.hasNext();){
            if(p.test(it.next())){
                t++;
            }
        }
        return t;
    }
}

输出

test集合的元素个数3
->1232
->11
->a
->adfs
1232
11
a
adfs
-1232
-a
-adfs
1232
1
2

主要的是一些遍历结合元素的方法:用foreach结合lambda表达式、用迭代器遍历、用Predicate操作、用Stream操作集合。

1. Set

Set接口不允许添加相同的元素,其它基本与Collect相同。而HashSet是它的典型实现,通常用HashSet来作为Set的实现类。

HashSet的特点:

  • 元素的顺序不与添加顺序相同
  • 不是同步的,多个线程同时访问时需要通过代码保证其同步
  • 集合元素可以是null

HashSet集合存入一个元素时会调用对象的hashCode()方法来得到其hashCode值,然后根据该值决定对象在HashSet中的存储位置。如果有两个值通过equals()方法返回true,但它们的hashCode()返回值不同,还是可以添加成功,被储存在不同位置。所以集合实际上在添加元素时同时做出两个判断:通过equals方法来判断新加入的元素是否和已有的元素相等、计算新加入的元素的hashCode值确定已有元素的hashCode是否存在和它相等的。这时会存在几种情况:如果新加入的元素–equals方法返回true且hashCode相同则添加失败、equals返回true但hashCode不同则添加成功、equals返回false且hashCode不同则添加失败、equals返回false但hashCode相同–>这时HashSet会试图将它们保存在同一个位置,会在这个位置用链式结构来保存多个对象,这样会导致HashSet的新能降低的,所以应尽量避免出现这种情况。如下

package algo_study;

import java.util.*;
import java.util.function.Predicate;

class test{
    public boolean equals(Object obj){
        return false;
    }
    public int hashCode(){
        return s.hashCode();
    }
    test(int i,String s){
        this.id=i;
        this.s=s;
    }
    private int id;
    private String s;
}
public class collectionStudy {
    public static void main(String[] args) {
        test t=new test(1,"test");
        Set<test> s=new HashSet<>();
        s.add(t);
        System.out.println(s.add(new test(2,"test")));//true
        System.out.println(s.add(new test(2,"testt")));//true
    }
}

LinkedHashSet是HashSet的子类,它同时用链表来维护元素的次序,这样,遍历时它会按元素的添加顺序来访问集合的元素,虽然维护插入顺序会降低它的性能,但遍历元素时它具有更好的性能。

TreeSetSortedSet接口的实现类。它确保集合元素处于排序状态。相比较HashSet它提供了一些额外的方法。TreeSet是采用红黑树的数据结构来存储集合元素,我们知道红黑树是需要比较两个元素的大小的,它支持两种排序方法:自然排序定制排序,默认采用自然排序。

自然排序:调用元素的compareTo(Object o)方法来比较元素之间的大小,然后将元素按升序排列。该方法返回一个int类型,相等则返回0,小于则返回负整数,大于则返回正整数。

Java提供了一个Comparable接口,该接口中定义了compareTo(Object o)的方法,相当于定义了一个规范。所以如果要让TreeSet的元素为自定义的类型,简单的做法是让这个自定义的类型实现Comparable接口,如果该类没有实现Comparable接口,添加元素时会引发ClassCaseException异常。Java中常用的一些类自然是实现了该接口的,如:BigDecimalBigIntegerCharacterBooleanStringDateTime等。

定制排序:自然排序时这个类型通过实现Comparable接口实现,如果不想让类实现Comparable接口,也可以在实例化TreeSet对象时提供一个Comparator对象与TreeSet集合关联,可以用Lambda表达式来代替Comparator。

这两种排序选择其中一种就行了。

EnumSet类:枚举类的集合类。其中的元素都是指定的枚举类型的枚举值。EnumSet的集合元素是有序的,以枚举值在枚举类中的定义顺序来决定集合元素的顺序。EnumSet内部通过位向量的形式来存储,它占用内存小,运行效率高,尤其在进行批量操作时。

EnumSet不允许插入null元素。

注意:上面介绍的Set的三个实现类HashSet、TreeSet、EnumSet都是线程不安全的,为此,可以通过Collections工具类中的synchronizedSortedSet方法来包装该Set集合。

SortedSet s=Collections.synchronizedSortedSet(new TreeSet(...));

上面介绍的几个实现类的实例如下:

package algo_study;

import java.util.*;
import java.util.function.Predicate;

class test {//implements Comparable
    public boolean equals(Object obj){
        return true;
    }
    public boolean equals(test t){
        return id==t.id && s.equals(t.s);
    }
    public int hashCode(){
        return s.hashCode();
    }
    test(int i,String s){
        this.id=i;
        this.s=s;
    }
    public String toString(){
        return "id:"+this.id+" name:"+this.s;
    }
    private int id;
    private String s;
    public int getId(){return id;}
    /*
    @Override
    public int compareTo(Object o) {
        test t=(test)o;
        if(id<t.id)
            return -1;
        if(id>t.id)
            return 1;
        return 0;
    }
    */
}
enum Week{
    Monday,Tuesday,Wednesday,Thursday,Firday,Saturday,Sunday
}//一个枚举类
public class collectionStudy {
    public static void main(String[] args) {
        test t=new test(1,"test");
        //HashSet
        Set<test> s=new HashSet<>();
        s.add(t);
        System.out.println(s.add(new test(2,"test")));
        System.out.println(s.add(new test(2,"testt")));
        System.out.println(s);
        s.forEach(o->System.out.println(o));
        //LinkedHashSet
        Set<test> s1=new LinkedHashSet<>();
        s1.add(t);
        s1.add(new test(3,"testt"));
        s1.add(new test(2,"testtt"));
        s1.add(new test(3,"tttt"));
        System.out.println(s1);
        //TreeSet
        TreeSet<test> s2=new TreeSet<>((o1,o2)->
        {
            test m1=(test)o1;
            test m2=(test)o2;
            return m1.getId()<m2.getId()?-1:m1.getId()>m2.getId()?1:0;
        });//Lambda表达式
        s2.add(t);
        System.out.println(s2.add(new test(2,"ttest")));
        System.out.println(s2.add(new test(0,"tests")));
        System.out.println(s2.add(new test(4,"ttt")));
        System.out.println(s2);
        System.out.println(s2.first());
        System.out.println(s2.last());
        System.out.println(s2.lower(t));
        System.out.println(s2.higher(t));
        System.out.println(s2.comparator());
        System.out.println(s2.subSet(t,s2.last()));//返回子集
        System.out.println(s2.headSet(t));//返回子集包含t之前的元素
        System.out.println(s2.tailSet(t));//返回子集包含t之后的元素
        //EnumSet
        EnumSet<Week> ew=EnumSet.allOf(Week.class);
        System.out.println(ew);
        EnumSet<Week>ew1=EnumSet.noneOf(Week.class);
        ew1.add(Week.Firday);
        ew1.add(Week.Monday);
        System.out.println(ew1);
        EnumSet<Week>ew2=EnumSet.of(Week.Monday,Week.Firday);
        System.out.println(ew2);
        EnumSet<Week>ew3=EnumSet.range(Week.Thursday,Week.Saturday);
        System.out.println(ew3);
        EnumSet<Week>ew4=EnumSet.complementOf(ew3);
        System.out.println(ew4);
    }
}
false
true
[id:2 name:testt, id:1 name:test]
id:2 name:testt
id:1 name:test
[id:1 name:test, id:3 name:testt, id:2 name:testtt, id:3 name:tttt]
true
true
true
[id:0 name:tests, id:1 name:test, id:2 name:ttest, id:4 name:ttt]
id:0 name:tests
id:4 name:ttt
id:0 name:tests
id:2 name:ttest
algo_study.collectionStudy$$Lambda$2/1747585824@3d075dc0
[id:1 name:test, id:2 name:ttest]
[id:0 name:tests]
[id:1 name:test, id:2 name:ttest, id:4 name:ttt]
[Monday, Tuesday, Wednesday, Thursday, Firday, Saturday, Sunday]
[Monday, Firday]
[Monday, Firday]
[Thursday, Firday, Saturday]
[Monday, Tuesday, Wednesday, Sunday]

2. List集合

List集合:有序、可重复集合,可通过索引访问指定位置的元素。

ArrayListVectorList接口的两个典型实现。ArrayList更常用一些。二者的显著区别是:ArrayList是线程不安全的,而Vector是线程安全的。所以Vector的新能更低一些。然而即使需要一个线程安全的List集合,也不推荐用Vector而是用Collections工具类将一个ArrayList变成线程安全的。

Vector提供了一个Stack子类,用于模拟栈:peek()显示第一个元素、pop()弹出第一个元素、push()将一个元素进栈。(线程安全的)。同样由于性能较差,不推荐用,取而代之的是ArrayDeque

package algo_study;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;

public class listStudy {
    public static void main(String[] args) {
        //List
        List l=new ArrayList();
        l.add("元素1");
        l.add("元素2");
        l.add("element 3");
        System.out.println(l);
        l.add(0,"element4");
        System.out.println(l);
        for(int i=0;i<l.size();++i){
            System.out.println(l.get(i));//通过索引访问元素,Java中并不能重载操作符,所以不能像C++中利用中括号
        }
        System.out.println(l.indexOf("element 3"));
        l.remove(2);
        System.out.println(l);
        System.out.println(l.subList(1,l.size()));
        l.set(0,"ele 0");
        System.out.println(l);
        l.sort((e1,e2)->{
            return e1.toString().length()-e2.toString().length() ;
        });//排序
        System.out.println(l);
        l.replaceAll(ele->ele.toString().length());
        System.out.println(l);
        //ArrayList
        ArrayList<String>a1=new ArrayList<>();
        a1.add("tetst");
        a1.add("ele1");
        a1.add("ele2");
        a1.add(1,"ele3");
        System.out.println(a1);
        a1.remove(1);
        System.out.println(a1);
        a1.set(1,"test");
        System.out.println(a1);
        ListIterator it=a1.listIterator();
        while(it.hasNext()){//用迭代器遍历
            System.out.println(it.next());
        }
        while(it.hasPrevious()){
            System.out.println(it.previous());//反向遍历
        }
        //固定长度的List
        List<String>l1=Arrays.asList("test","ele");
        System.out.println(l1);//l1不允许添加删除元素
        
    }
}

输出

[元素1, 元素2, element 3]
[element4, 元素1, 元素2, element 3]
element4
元素1
元素2
element 3
3
[element4, 元素1, element 3]
[元素1, element 3]
[ele 0, 元素1, element 3]
[元素1, ele 0, element 3]
[3, 5, 9]
[tetst, ele3, ele1, ele2]
[tetst, ele1, ele2]
[tetst, test, ele2]
tetst
test
ele2
ele2
test
tetst
[test, ele]

3.Queue集合

Queue接口模拟队列,它有一个PriorityQueue实现类,它还有一个Deque接口,代表一个双端队列,可同时从两端添加删除元素。Deque的实现类即可以当成队列使用也可以当成栈来使用。两个实现类:ArrayDequeLinkedList

PriorityQueue会按元素的大小将元素排序。

Deque方法对比:

QueueDequeStackDeque
add、offeraddLast、offerLastpushaddFirst、offerFirst
remove、pollremoveFirst、pollFirstpopremoveFirst、pollFirst
element、peekgetFirst、peekFirstpeekgetFirst、peekFirst

ArrayDeque内部以数组的形式来保存元素,所以随机访问集合时有比较好的性能。而LinkedList内部以链表的形式来保存集合中的元素,所以随机访问集合元素时性能差,但插入删除元素时性能很好。另外Vector也是以数组形式存储元素的,但它实现线程同步同时实现机制不好导致各方面性能都不好。

实际使用时自然是根据实际需求灵活选择那个容器。如果不知道选择那个时就选ArrayList就行了。

package algo_study;

import java.util.ArrayDeque;
import java.util.LinkedList;
import java.util.PriorityQueue;

public class QueueLearn {
    public static void main(String[] args) {
        //PriorityQueue
        PriorityQueue p1=new PriorityQueue();
        p1.offer(1);
        p1.offer(4);
        p1.offer(-2);
        p1.offer(4);
        System.out.println(p1);
        System.out.println(p1.poll());
        //ArrayDeque
        //模拟栈,后进先出
        ArrayDeque st=new ArrayDeque();
        st.push(1);//或者addFirst,入栈
        st.push(2);
        st.push(4);
        st.push(6);
        System.out.println(st);
        System.out.println(st.peek());//显示栈顶元素
        while(st.size()!=0){
            System.out.println(st.pop());//出栈
        }
        //模拟队列
        ArrayDeque que=new ArrayDeque();
        que.add(123);//入队
        que.offer(112);//入队
        que.add(23);
        que.add(122);
        System.out.println(que);
        System.out.println(que.element());//显示队首元素
        System.out.println(que.peek());//显示队首元素
        System.out.println(que.remove());//出队
        while(que.size()!=0){
            System.out.println(que.poll());//出队
        }
        //LinkedList
        //栈,和ArrayList的方法名相同,还是很不错的
        LinkedList sta=new LinkedList();
        sta.push(11);
        sta.push(22);
        sta.push(2);
        sta.push(4);
        sta.push(6);
        System.out.println(sta);
        System.out.println(sta.peek());//显示栈顶元素
        while(st.size()!=0){
            System.out.println(sta.pop());//出栈
        }
        //队列,
        ArrayDeque quel=new ArrayDeque();
        quel.add(123);//入队
        quel.offer(112);//入队
        quel.add(23);
        quel.add(122);
        System.out.println(quel);
        System.out.println(quel.element());//显示队首元素
        System.out.println(quel.peek());//显示队首元素
        System.out.println(quel.remove());//出队
        while(que.size()!=0){
            System.out.println(quel.poll());//出队
        }
    }
}

输出

[-2, 4, 1, 4]
-2
[6, 4, 2, 1]
6
6
4
2
1
[123, 112, 23, 122]
123
123
123
112
23
122
[6, 4, 2, 22, 11]
6
[123, 112, 23, 122]
123
123
123

4. Map

Map接口下有:HashMapLinkedHashMapSortedMap(接口)、TreeSetEnumSet等子接口和实现类,正好是和Set对应的,这样正好方便我们去理解这些子接口和实现类。

HashMap和Hashtable都是HashMap接口的典型实现类,它们的区别类似于ArrayList和Vector。

Hashtable类有个子类为Properities,可以用来读写属性文件,这个类挺有用的。

WeakHashMap与HashMap用法相似,区别在于WeakHashMap对象的key只保留对实际对象的弱引用。

IdentityHashMap与HashMap也相似,但它处理两个key相等时仅当两个key严格相等时才认为它们相等。

package algo_study;

import java.io.*;
import java.util.*;

class mapTest{
    private int id;
    private String name;
    public int getId(){return id;}
    public String getName(){return name;}
    @Override//对于HashMap来说必须要重载equals和hashCode这两个方法,顺便重载toString
    public boolean  equals(Object o){
        mapTest m=(mapTest)o;
        return id==m.getId() && name.equals(m.getName());
    }
    @Override
    public int hashCode() {
        return this.toString().hashCode();
    }
    @Override
    public String toString(){
        return "id->"+id+"&& name->"+name;
    }
    public mapTest(int i,String s){
        this.id=i;
        this.name=s;
    }
}

public class mapLearn {
    public static void main(String[] args) throws IOException {
        //HashMap
        HashMap hm=new HashMap();
        hm.put(new mapTest(10,"Tom"),1212);
        hm.put(new mapTest(11,"Jack"),21212);
        mapTest m=new mapTest(111,"Joker");
        hm.put(m,111);
        hm.put(m,12112);
        hm.put(m,123);//放入重复的key会覆盖原有的Value。
        System.out.println(hm);
        System.out.println(hm.put(m,11111));//如果覆盖原有值时会返回被覆盖的值
        System.out.println(hm.containsKey(m));
        System.out.println(hm.containsValue(1212));
        for(Object k:hm.keySet()){
            System.out.println(k+"---->"+hm.get(k));//遍历
        }
        hm.forEach((k,v)->System.out.println(k+"->"+v));//用forEach时有两个参数,key和value
        HashMap<mapTest, Integer>mi=new HashMap<>();
        hm.forEach((k,v)->mi.put((mapTest) k,(int)v));
        System.out.println(mi);
        //Properities类,这个类是Hashtable的子类,用于读写属性文件,非常有用的类
        //
        Properties properties=new Properties();
        properties.setProperty("name","XieHuiHui");
        properties.setProperty("QQ","0000000000");
        properties.setProperty("phone number","00000000000000");
        properties.store(new FileOutputStream("test.ini"),"coment line");//写入属性文件
        Properties pro2=new Properties();
        File f=new File("test.ini");
        System.out.println(f.exists());
        pro2.load(new FileInputStream("test.ini"));//读取属性文件
        System.out.println(pro2);
        System.out.println(pro2.get("QQ"));
        //LinkedHashMap
        LinkedHashMap<mapTest,Integer>lmi=new LinkedHashMap<>();
        lmi.put(m,111);
        lmi.put(new mapTest(11,"sss"),11);
        lmi.put(new mapTest(12313,"sdfa"),1231);
        System.out.println(lmi);
        //TreeMap
        TreeMap<mapTest,Integer>tmi=new TreeMap<>((o1,o2)->{
           mapTest t1=(mapTest) o1;
           mapTest t2=(mapTest)o2;
           return o1.getId()-o2.getId();
        });//定制排序
        tmi.put(m,111);
        tmi.put(new mapTest(1,"2w324"),12);
        tmi.put(new mapTest(123,"sadfs"),2);
        System.out.println(tmi);
        //identityHashMap
        IdentityHashMap id=new IdentityHashMap();
        id.put(m,111);
        id.put(new mapTest(1,"aaa"),12);
        id.put(new mapTest(123,"sadfs"),2);
        id.put(new mapTest(1,"aaa"),12);
        System.out.println(id);
        //EnumMap
        EnumMap<Week,String> en=new EnumMap(Week.class);
        en.put(Week.Monday,"周一");
        en.put(Week.Tuesday,"周二");
        System.out.println(en);
    }
}
{id->111&& name->Joker=123, id->11&& name->Jack=21212, id->10&& name->Tom=1212}
123
true
true
id->111&& name->Joker---->11111
id->11&& name->Jack---->21212
id->10&& name->Tom---->1212
id->111&& name->Joker->11111
id->11&& name->Jack->21212
id->10&& name->Tom->1212
{id->111&& name->Joker=11111, id->11&& name->Jack=21212, id->10&& name->Tom=1212}
true
{phone number=00000000000000, name=XieHuiHui, QQ=0000000000}
0000000000
{id->111&& name->Joker=111, id->11&& name->sss=11, id->12313&& name->sdfa=1231}
{id->1&& name->2w324=12, id->111&& name->Joker=111, id->123&& name->sadfs=2}
{id->111&& name->Joker=111, id->1&& name->aaa=12, id->123&& name->sadfs=2, id->1&& name->aaa=12}
{Monday=周一, Tuesday=周二}

5. Collections工具类

  1. 改变List元素次序的方法:
    1. void reverse(List list)
    2. void shuffle(List list): 对list集合元素进行随机排序(模拟“洗牌”行为)
    3. void sort(List list):根据元素的自然排序对集合元素进行升序
    4. void sort(List list,Comparator c):根据指定的Comparator排序
    5. void swap(List list ,int i,int j):将i处元素和j处元素进行交换
    6. void rotate(List list,int distance):distance为正时,将结合后distance个元素整体移动到前面,否则将前distance个元素整体移动到后面
  2. 查找、替换操作
    1. int binarySearch(List,list,Object key):用二分法搜索指定的List集合,返回指定对象的索引,必须确保List中元素处于有序状态
    2. Object max(Collection coll):根据元素自然排序返回集合中的最大元素
    3. Object max(Collection co,Comparator c):~
    4. Object min(Collection co)
    5. Object min(Collection co,Comparator c)
    6. void fill(List l,Object o):用指定元素O替换集合中的所有元素
    7. int frequency(Collection c,Object O):返回O的出现次数
    8. int indexOfSubList(List source,List target):返回子List对象在父List中首次出现的位置,没有则返回-1
    9. boolean replaceAll(List list,Object oldVal,Object newVal):用一个新值替换所有的旧值
  3. 同步控制:synchronizedXXX()方法
package algo_study;

import java.util.*;

public class collectionsLearn {
    public static void main(String[] args) {
        //改变List中元素次序的操作
        List l=new ArrayList();
        l.add(11);
        l.add(22);
        l.add(1);
        l.add(23);
        System.out.println(l);
        Collections.reverse(l);
        System.out.println(l);
        Collections.shuffle(l);
        System.out.println(l);
        Collections.sort(l);
        System.out.println(l);
        Collections.sort(l,(o1,o2)->{
            int i1=(int)o1;
            int i2=(int)o2;
            return i2-i1;
        });
        System.out.println(l);
        Collections.swap(l,0,1);
        System.out.println(l);
        Collections.rotate(l,2);
        System.out.println(l);
        //查找、替换
        l.add(2);
        l.add(4);
        Collections.sort(l);
        System.out.println(l);
        System.out.println(Collections.binarySearch(l,4));//二分法搜索
        System.out.println(Collections.max(l));
        System.out.println(Collections.min(l));
        List l2=new ArrayList(l);
        Collections.fill(l2,2);
        System.out.println(l2);
        l.add(1);
        System.out.println(Collections.frequency(l,1));
        System.out.println(Collections.indexOfSubList(l,l2));
        System.out.println(Collections.replaceAll(l,1,2));
        System.out.println(l);
        //同步控制,下面创建的几个对象都是线程安全的
        List l3=Collections.synchronizedList(new ArrayList<>());
        Set s=Collections.synchronizedSet(new HashSet<>());
        Map m=Collections.synchronizedMap(new HashMap<>());
    }
}
[11, 22, 1, 23]
[23, 1, 22, 11]
[11, 22, 23, 1]
[1, 11, 22, 23]
[23, 22, 11, 1]
[22, 23, 11, 1]
[11, 1, 22, 23]
[1, 2, 4, 11, 22, 23]
2
23
1
[2, 2, 2, 2, 2, 2]
2
-1
true
[2, 2, 4, 11, 22, 23, 2]

3. python

前面可以看出Java和C++的容器类不管时用途还是用法都是比较类似的,也都有很多细节需要注意。而python的容器就显得和不一样。

python内置的容器主要有三个listtupledict,这三个list最常用,我反正能用list的基本都用list,list本身很随意,可以任意添加元素,元素也可以是不同类型的,元素也可以是list、tuple、dict。

要说对应的化,list可勉强对应vector,tuple为不可变集合,dict可对应map。

  1. list

通过help(list)可以注意到list类自身有以下常见的方法:

  • append
  • clear
  • copy
  • count
  • extend
  • index
  • insert
  • pop
  • remove
  • reverse
  • sort
  1. tuple
  2. dict

dict中的函数有

  • clear
  • copy
  • get
  • items
  • keys
  • pop
  • popitem
  • setdefault
  • update
  • values

python中通过heapq的lib来实现priority queue。

队列和栈都可以通过list来实现,既然list通用性这么强,效率自然是有所牺牲的。为了提升数据的处理效率,一些高效的数据结构放在了collections中。如deque类。

collections

官方文档中的描述为:

This module implements specialized container datatypes providing alternatives to Python's general purpose built-in containers, dict,list, set, and tuple.

主要有以下的一些包装类

	* namedtuple   factory function for creating tuple subclasses with named fields
    * deque        list-like container with fast appends and pops on either end
    * ChainMap     dict-like class for creating a single view of multiple mappings
    * Counter      dict subclass for counting hashable objects
    * OrderedDict  dict subclass that remembers the order entries were added
    * defaultdict  dict subclass that calls a factory function to supply missing values
    * UserDict     wrapper around dictionary objects for easier dict subclassing
    * UserList     wrapper around list objects for easier list subclassing
    * UserString   wrapper around string objects for easier string subclassing

具体的一些用法自己摸索完全没问题的。这个类的意义就是提供一些更高效的容器,当然可能用起来不如内置的容器方便,但可以确定的是当我们更注重运行效率而不是自己的开发效率时最好首先考虑使用collections类中的元素,这时再不用的话其它时候更用不到了。

参与评论 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页

打赏作者

xhh22900

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值