C++【哈希表封装unordered_map/set】—含有源代码

news2024/11/27 11:44:06

文章目录

    • (1)修改原哈希表
    • (2)迭代器
    • (3)最后一步
    • (4)关于key是自定义类型的额外补充(面试题)
    • (5)源代码

(1)修改原哈希表

和红黑树封装一样的逻辑,unordered_map/set公用一张哈希表,所以改的时候两个容器都要一块取考虑,因为需要通过泛型去设计,要有复用思想。

这里一部分就简单说一下,比如第二个参数是K就是K,为什么要传第一个模板参数,它要和map复用,因为map也需要用到key,第一个模板参数拿到K的类型,因为查找,删除等接口的参数是K,第二个模板参数决定了树的节点存什么,是K还是KV。
在这里插入图片描述

我们一步一步加并修改原来哈希表的内容,这里就不叫V,自己写的时候这叫T,把节点结构体里面的pair类型改成T,构造函数里面用data初始化_data。以及后面涉及到pair<K,V>&kv的全都改成T&data。unordered_map就通过第二个模板参数控制node里面的data,同理unordered_set也一样。

之前是kv现在要改为_data,把kv.first改为_data,但是这里的第二个_data并不是我们想要的,因为这里的data到底是什么并不知道,上一层加了一层泛型。这需要用仿函数解决Key。原来哈希表里面涉及到hash(cur->kv.first) 需要成hash(kot(cur->_data)),表示通过指针获取节点的_data数据,我们并不知道这是什么类型需要再用仿函数Key创建的对象kot调用()来获取具体的数据,最后通过hash()来获取键值所对应的存储位置。

(2)迭代器

在这里插入图片描述
如上图,对于++it怎么走,大致思路是这样的,如果node不等于空继续往下走,如果为空,就需要找下一个桶,
这里写迭代器的时候因为++需要用到哈希表,就需要前置声明,这里存在互相引用的问题,迭代器需要哈希表,哈希表需要迭代器,就要把哈希表定义在前面,其他的一样的老套路,只不过里面多了一个哈希表指针,构造的时候需要初始化,另外另一个构造函数一会实现,主要这里先把迭代器++实现出来,因为它的迭代器只支持单向,没有减减。
实现迭代器加加解析
如果node的下一个指针不为空,就继续往下走更新_node,如果为空,可能走到尾部了,就需要找下一个桶,下一个不为空的桶在哪我们不知道,先算自己在哪,先把_data里面的key取出来,再用哈希的仿函数获得整数,模上表的大小找到映射位置,算出来hashi加加一下,就去找不为空的桶,如果hashi小于数据个数,就进如循环,再判断如果下一个位置不为空,就把表的第一个节点指针给_node,否则++hashi,就过走完了整个表,没有找到下一个位置,就用空取替带_node。最后返回this指针。如下图代码:

Self& operator++()
			{
				if (_node->_next != nullptr)
				{
					_node = _node->_next;
				}
				else
				{
					Key kot;
					Hash hash;
					size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();
					++hashi;
					while (hashi < _ht->_tables.size())
					{
						if (_ht->_tables[hashi])
						{
							_node = _ht->_tables[hashi];
							break;
						}
						else
						{
							++hashi;
						}
						if (hashi == _ht->_tables.size())
						{
							_node = nullptr;
						}

					}
				}
				return *this;
			}
		

我们实现begin和end,begin:要返回第一个桶的第一个数据,这个begin不是直接构建的,需要遍历去找第一个不为空的桶,找到之后就构建迭代器,但是除了传找到的这个节点,还得传哈希表,我们直接可以用this指针,就是指向哈希表对象本身。
end:就直接拿空就构造就可以
同理const_begin和const_end同理

            iterator begin()
			{
				Node* cur = nullptr;
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					cur = _tables[i];
					if (cur)
					{
						break;
					}
				}
				return iterator(cur, this);
			}

上面需要一些细节,迭代器需要访问_table的私有,就让我们这个哈希表这个类提供一个get.size和get.table,也可以写一个友元函数,因为是模板还得需要加上模板参数,如图:
在这里插入图片描述

我们继续在unordered_map增加迭代器参数,同理unordered_set也一样,在哈希表类里里面曾加一个普通迭代器和const迭代器,如图下图:
注意这里的unordered_set两个普通迭代器底层用的都是const迭代器,unordered_map普通迭代器用的普通迭代器,因为它需要修改它的Value,它的K不能修改是通过paie里面的constkey达到的,而unordered_set只有K,K是不是能修改的。

在这里插入图片描述
但是直接用会报错,因为unordered_set调用begin的时候,普通对象调用的普通begin,返回的是普通迭代器,但这里是const迭代器,这里就不具体演示了,和红黑树封装一样的逻辑。具体原因是我们迭代器里没有写对应的构造,需要一个支持普通到const迭代器的构造,我们在里面单独加一个普通迭代器Iterator,构造里它的参数前面加一个const,具体就不说了可以看红黑树封装。其实就是权限的缩小。这里还有一个细节,如果调用const_iterator会有问题,因为begin是const,返回时,迭代器里的this指针就是const的,这时候要要把迭代器构造的哈希表对象指针改成const的。因为迭代器里面不会修改这里的哈希表,需要取遍历它,传普通传const都可以接收,因为权限可以缩小。
在这里插入图片描述

(3)最后一步

在这里插入图片描述

我们在unordered_map里面提供一个[],它要去访问第二个V值也就是pair的second,如果它没有就插入,如果有就返回key所在节点的迭代器,因为里面调用的是insert,这样就不仅能查找对应的value值,并有修改功能并可以插入新的键值对,insert返回值得个是个pair类型。而且并把有关的insert返回值,如果返回成功就返回新插入节点的构造的迭代器加一个true即make_pair(iterator(newnode, this), true),同样unordered_set的insert也一样改为pair<iterator,bool>,里面是是一个迭代器和布尔值。
总之,insert返回值是个pair类型,find返回的是一个迭代器,erase返回bool值,unordered_set除了不提供[],其他都一样。
另外,我们的key还可以支持自定义类型,但是需要要满足支持转换成取模的仿函数,和等于比较和等于比较,调用unordered_map/set需要显示传HashDate仿函数如图:
在这里插入图片描述
如下图我们进行的测试和结果
在这里插入图片描述

(4)关于key是自定义类型的额外补充(面试题)

1、一个类型要做unordered哈希系列的key:要满足支持转换成取模的仿函数和等于比较,如上。
2、一个类型要做map系列的key:要满足支持<比较。
在STL源码库中,map默认的第三个参数为less<_Key>,就是将key按照从小到大的顺序排列,其内部实现很简单,就是直接比较两个key的类型返回。但是如果是自定义类型的时候,直接用默认的less比较的话会报错,因为编译器不知道怎么处理,即不知道如何比较他们的大小,它能应对内置类型比如int等,比如复合类型string。如何解决?2种方法:
第一种方法:继续使用第三个模板参数,因为第三个模板参数内部是直接比较key的类型大小,我们可以在自定义的类型里实现重载<运算符,让它知道如何比较这两个key,因为默认参数是less,所以只需要重载<运算符就可以了,如果参数是greater,那么就需要重载>运算符。

struct nza {
    nza(int n1 = 0, int n2 = 1)
        :_n1(n1), _n2(n2) {}
    bool operator<(const nza& Data) const{
        return _n1+_n2  < Data._n1 + Data._n2;
    }
    int _n1;
    int _n2;
};

int main() {
    map<nza, int> myMap;
    }

第二种方法就是:自己实现一个仿函数。

struct nza {
    nza(int n1 = 0, int n2 = 1)
        :_n1(n1), _n2(n2) {}
    int _n1;
    int _n2;
};
template<class T>
struct RLcmp {
    bool operator()(const T& Data1, const T& Data2)const {
        return Data1._n1 + Data1._n2< Data1._n1 + Data2._n2;
    }
};

int main() 
{
    map<nza, int, RLcmp<nza> > myMap;
}

(5)源代码

OpenHash.h

#pragma once
#include<utility>
#include<vector>
#include<string>
#include<iostream>
using namespace std;

template<class K>
struct HashFunc
{
	size_t operator()(const K& key)
	{
		return key;
	}

};
template<>
struct HashFunc<string>
{
	size_t operator()(const string& str)
	{
		size_t hash = 0;
		for (auto& e : str)
		{
			hash += e;
			hash *= 131;
		}
		return hash;
	}

};

	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next;
		HashNode(const T& data)
			:_next(nullptr)
			, _data(data)
		{}
	};

	template<class K, class T, class Key, class Hash>
	class HashTable;

	template<class K, class T, class Ref, class Ptr, class Key, class Hash>
	struct _HashIterator
	{
		typedef  HashNode<T> Node;
		typedef HashTable<K, T, Key, Hash> HT;
		typedef _HashIterator<K, T, Ref, Ptr, Key, Hash> Self;
		typedef _HashIterator<K, T, T&, T*, Key, Hash> Iterator;
		Node* _node;
		const HT* _ht;
		_HashIterator(Node* node, const HT* ht)
			:_node(node)
			, _ht(ht)
		{}

		_HashIterator(const Iterator& it)
			:_node(it._node)
			, _ht(it._ht)
		{}
		Ptr operator->()
		{
			return &_node->_data;
		}
		Ref operator*()
		{
			return _node->_data;
		}
		bool operator!=(const Self& s)
		{
			return _node != s._node;
		}
		bool operator==(const Self& s) const
		{
			return _node == s._node;
		}
		Self& operator++()
		{
			if (_node->_next != nullptr)
			{
				_node = _node->_next;
			}
			else
			{
				Key kot;
				Hash hash;
				size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();
				++hashi;
				while (hashi < _ht->_tables.size())
				{
					if (_ht->_tables[hashi])
					{
						_node = _ht->_tables[hashi];
						break;
					}
					else
					{
						++hashi;
					}
					if (hashi == _ht->_tables.size())
					{
						_node = nullptr;
					}

				}
			}
			return *this;
		}

	};

	template<class K, class T, class Key, class Hash>
	class HashTable
	{
		template<class K, class T, class Ref, class Ptr, class Key, class Hash>
		friend struct _HashIterator;

		typedef HashNode<T> Node;
	public:
		typedef _HashIterator<K, T, T&, T*, Key, Hash> iterator;
		typedef _HashIterator<K, T, const T&, const T*, Key, Hash> const_iterator;


		iterator begin()
		{
			Node* cur = nullptr;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				cur = _tables[i];
				if (cur)
				{
					break;
				}
			}
			return iterator(cur, this);
		}
		iterator end()
		{
			return iterator(nullptr, this);

		}
		const_iterator begin()const
		{
			Node* cur = nullptr;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				cur = _tables[i];
				if (cur)
				{
					break;
				}
			}
			return const_iterator(cur, this);
		}
		const_iterator end()const
		{
			return const_iterator(nullptr, this);

		}


		~HashTable()
		{
			for (auto& cur : _tables)
			{
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				cur = nullptr;
			}
		}
		pair<iterator, bool> Insert(const T& data)
		{
			Key kot;
			iterator it = Find(kot(data));
			if (it != end())
			{
				return make_pair(it, false);
			}

			Hash hash;
			if (_n == _tables.size())
			{
				/*size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;*/
				size_t newsize = GetNextPrime(_tables.size());
				vector<Node*> newtables(newsize, nullptr);
				for (auto& cur : _tables)
				{
					while (cur)
					{
						Node* next = cur->_next;;
						size_t hashi = hash(kot(cur->_data)) % newtables.size();
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;
						cur = next;
					}
				}
				_tables.swap(newtables);
			}
			size_t hashi = hash(kot(data)) % _tables.size();
			Node* newnode = new Node(data);
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_n;
			return make_pair(iterator(newnode, this), true);
		}

		iterator Find(const K& key)
		{
			Key kot;
			if (_tables.size() == 0)
				return end();
			Hash hash;
			size_t hashi = hash(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur, this);
				}
				cur = cur->_next;
			}
			return end();
		}


		bool Erase(const K& key)
		{
			Key kot;
			Hash hash;
			size_t hashi = hash(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;

					}
					delete cur;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}

			}
			return false;

		}
		size_t GetNextPrime(size_t prime)
		{

			static const int __stl_num_primes = 28;
			static const unsigned long __stl_prime_list[__stl_num_primes] =
			{
				53, 97, 193, 389, 769,
				1543, 3079, 6151, 12289, 24593,
				49157, 98317, 196613, 393241, 786433,
				1572869, 3145739, 6291469, 12582917, 25165843,
				50331653, 100663319, 201326611, 402653189, 805306457,
				1610612741, 3221225473, 4294967291
			};

			size_t i = 0;
			for (; i < __stl_num_primes; ++i)
			{
				if (__stl_prime_list[i] > prime)
					return __stl_prime_list[i];
			}

			return __stl_prime_list[i];
		}

	private:
		vector<Node*> _tables;
		size_t _n = 0;
	};
	

unordered_Map.h

#pragma once
#include"OpenHash.h"
namespace nza
{
	template<class K, class V,class Hash=HashFunc<K>>
	class unordered_Map
	{
	public:
		struct MapKey
		{
			const K& operator()(const pair<K,V>& kv)
			{
				return kv.first;
			}
		};
	

	public:
		typedef  typename HashTable<K, pair<const K,V>, MapKey,Hash>::iterator iterator;
		typedef  typename HashTable<K, pair<const K,V>, MapKey,Hash>::const_iterator const_iterator;
		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

		const_iterator begin() const
		{
			return _ht.begin();
		}

		const_iterator end() const
		{
			return _ht.end();
		}
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;
		}
		pair<iterator, bool> insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}
		iterator find(const K& key)
		{
			return _ht.Find(key);
		}
		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}

	private:
		HashTable<K,pair<const K, V>, MapKey,Hash> _ht;
	};

	class Date
	{
		friend struct HashDate;
	public:
		Date(int year = 2000, int month = 12, int day = 23)
			: _year(year)
			, _month(month)
			, _day(day)
		{}

		bool operator<(const Date& d)const
		{
			return (_year < d._year) ||
				(_year == d._year && _month < d._month) ||
				(_year == d._year && _month == d._month && _day < d._day);
		}

		bool operator>(const Date& d)const
		{
			return (_year > d._year) ||
				(_year == d._year && _month > d._month) ||
				(_year == d._year && _month == d._month && _day > d._day);
		}

		bool operator==(const Date& d) const
		{
			return _year == d._year
				&& _month == d._month
				&& _day == d._day;
		}

		friend ostream& operator<<(ostream& _cout, const Date& d);
	private:
		int _year;
		int _month;
		int _day;
	};

	ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}

	struct HashDate
	{
		size_t operator()(const Date& d)
		{
			size_t hash = 0;
			hash += d._year;
			hash *= 31;

			hash += d._month;
			hash *= 31;

			hash += d._day;
			hash *= 31;

			return hash;
		}
	};


	void TestM1()
	{
		unordered_Map<int, int> m;
		m.insert(make_pair(66, 66));
		m.insert(make_pair(77, 77));
		m.insert(make_pair(160, 160));

		unordered_Map<int, int>::iterator it = m.begin();
		while (it != m.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;
	}

	void TestM2()
	{
		Date d1(2016, 3, 15);
		Date d2(2016, 3, 15);
		Date d3(2016, 3, 12);
		Date d4(2016, 3, 11);
		Date d5(2023, 3, 12);
		Date d6(2023, 3, 13);

		Date a[] = { d1, d2, d3, d4, d5, d6 };

		unordered_Map<Date, int, HashDate> cm;
		for (auto e : a)
		{
			cm[e]++;
		}

		for (auto& kv : cm)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}
}

unordered_Set.h

#pragma once
#include"OpenHash.h"
namespace nza
{
	template<class K,class Hash=HashFunc<K>>
	class unordered_Set
	{
	public:
		struct SetKey
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename  HashTable< K, K, SetKey,Hash>::const_iterator iterator;
		typedef typename  HashTable< K, K, SetKey,Hash>::const_iterator const_iterator;
		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

		const_iterator begin() const
		{
			return _ht.begin();
		}

		const_iterator end() const
		{
			return _ht.end();
		}
		pair<iterator,bool> insert(const K& key)
		{
			return _ht.Insert(key);
		}
		iterator find(const K& key)
		{
			return _ht.Find(key);
		}
		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}

	private:
		HashTable<K, K, SetKey,Hash> _ht;

	};


	void TestS1()
	{
		int a[] = { 1, 6, 4, 66, 160, 32, 44 };
		unordered_Set<int> s;
		for (auto e : a)
		{
			s.insert(e);
		}

		s.insert(35);
		s.insert(117);


		unordered_Set<int>::iterator it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto e : s)
		{
			cout << e << " ";
		}
		cout << endl;

	}
}

test.cpp


#include"OpenHash.h"
#include"unordered_Set.h"
#include"unordered_Map.h"

int main()
{
	nza::TestM1();
	nza::TestM2();
	nza::TestS1();

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/627073.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【链表Part01】| 203.移除链表元素、707.设计链表、206.反转链表

目录 ✿LeetCode203.移除链表元素❀ ✿LeetCode707.设计链表❀ ✿LeetCode206.反转链表❀ ✿LeetCode203.移除链表元素❀ 链接&#xff1a;203.移除链表元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff…

python数据分析

一、数据处理 1.爬取数据 我们将使用Python的requests和BeautifulSoup库来爬取数据。在这个示例中&#xff0c;我们将爬取豆瓣电影Top250的数据。 import requests from bs4 import BeautifulSoup url https://movie.douban.com/top250 headers {User-Agent: Mozilla/5.0 …

策略设计模式解读

目录 问题引进 鸭子问题 传统方案解决鸭子问题的分析和代码实现 传统的方式实现的问题分析和解决方案 策略模式基本介绍 基本介绍 策略模式的原理类图 策略模式解决鸭子问题 策略模式的注意事项和细节 问题引进 鸭子问题 编写鸭子项目&#xff0c;具体要求如下: 1) 有…

【GlobalMapper精品教程】059:基于las点云创建数字高程地形并二三维着色显示

本文讲述在globalmapper免费中文版中基于地形点云las数据创建数字高程地形、数字高程二三维联动可视化并进行数字高程着色显示。 文章目录 一、加载地形点云las数据二、创建数字高程地形三、数字高程二三维联动可视化四、数字高程着色显示相关阅读:ArcGIS实验教程——实验二十…

如何看待 Facebook 上线支付功能?

随着科技的不断进步&#xff0c;电子支付在我们的生活中变得越来越普遍。最近&#xff0c;Facebook宣布推出自己的支付功能&#xff0c;这引起了广泛的关注和讨论。作为世界上最大的社交媒体平台之一&#xff0c;Facebook进入支付领域的举措无疑具有重要意义。那么&#xff0c;…

Jetpack Compose:使用PagerIndicator和Infinity实现滚动的HorizontalPager

Jetpack Compose&#xff1a;使用PagerIndicator和Infinity实现滚动的HorizontalPager 可能你已经知道&#xff0c;Jetpack Compose 默认不包含内置的ViewPager组件。然而&#xff0c;我们可以通过在 build.gradle 文件中添加 accompanist 库依赖&#xff0c;将 ViewPager 功能…

有了这套方案,企业降本增效不再是纸上谈兵 (2)

一、生存压力逼近&#xff0c;企业如何应对经济下行残酷挑战&#xff1f; 当前市场经济下滑&#xff0c;客户预算紧缩和投资削减可能导致IT项目推迟或取消&#xff0c;从而直接影响公司收入和盈利能力。各大厂商都在陆续裁员或调整业务&#xff0c;以人力等成本为主的IT公司也必…

双塔模型dssm实践

最近在学习向量召回&#xff0c;向量召回不得不用到dssm双塔模型&#xff0c;双塔模型的原理非常简单&#xff0c;就是用两个任务塔&#xff0c;一个是query侧的query任务塔&#xff0c;另一个是doc侧的doc任务塔&#xff0c;任务塔向上抽象形成verctor隐向量后&#xff0c;用c…

【多同步挤压变换】基于多同步挤压变换处理时变信号和噪声信号研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

2022年国赛高教杯数学建模B题无人机遂行编队飞行中的纯方位无源定位解题全过程文档及程序

2022年国赛高教杯数学建模 B题 无人机遂行编队飞行中的纯方位无源定位 原题再现 无人机集群在遂行编队飞行时&#xff0c;为避免外界干扰&#xff0c;应尽可能保持电磁静默&#xff0c;少向外发射电磁波信号。为保持编队队形&#xff0c;拟采用纯方位无源定位的方法调整无人机…

win10录屏软件哪个好用?强烈推荐这3款!

案例&#xff1a;想要录制我的电脑屏幕&#xff0c;但是不知道如何选择合适的录屏工具&#xff0c;有没有好用的win10录屏软件&#xff1f; 【我想找一款好用的win10录屏工具&#xff0c;录制我的电脑屏幕&#xff0c;但是找了很久还没有找到&#xff0c;大家有好用的录屏软件…

Kibana:使用 Kibana 自带数据进行可视化(二)

在今天的练习中&#xff0c;我们将使用 Kibana 自带的数据来进行一些可视化的展示。希望对刚开始使用 Kibana 的用户有所帮助。这个是继上一篇文章 “Kibana&#xff1a;使用 Kibana 自带数据进行可视化&#xff08;一&#xff09;” 的续篇。 前提条件 如果你还没有安装好自己…

占据80%中国企业出海市场,亚马逊云科技如何为出海客户提供更多资源和附加值

亚马逊云科技就可以做到&#xff0c;作为占据80%中国企业出海市场的亚马逊云科技&#xff0c;其覆盖全球的业务体系&#xff0c;从亚马逊海外购、亚马逊全球开店、亚马逊智能硬件与服务&#xff0c;Amazon Alexa&#xff0c;Amazon Music都是属于亚马逊云科技“梦之队”的一员。…

【Android】WMS(二)Window的添加

软件盘相关模式 在 Android 应用开发中&#xff0c;软键盘的显示与隐藏是一个经常出现的问题&#xff0c;而 WindowManager 的 LayoutParams 中定义的软键盘相关模式则为开发者提供了一些解决方案。 其中&#xff0c;SoftInputMode 就是用于描述软键盘的显示方式和窗口的调整…

【LeetCode】HOT 100(6)

题单介绍&#xff1a; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识算法与数据结构的新手和想要在短时间内高效提升的人&#xff0c;熟练掌握这 100 道题&#xff0c;你就已经具备了在代码世界通行的基本能力。 目录 题单介绍&#…

python包装与授权

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

ai聊天对话工具哪种好用?这些ai对话聊天工具不要错过

在如今信息爆炸的时代&#xff0c;人工智能技术正在逐渐渗透到我们的生活和工作中。ai对话聊天技术作为其中的一项重要应用&#xff0c;吸引了越来越多的关注。但是&#xff0c;ai对话聊天技术并不是万能的&#xff0c;它需要一定的技巧和策略才能真正发挥其价值。那么&#xf…

CAN总线转串口

一、CAN总线在工程机械中的广泛应用 随着科技的进步和现代施工项目大型化的要求,新一代工程机械需要实现集成化操作和智能控制。CAN总线是国际上应用最广泛的现场总线之一。CAN总线以其高可靠性、实时性、无破坏仲裁、多主等特性&#xff0c;已广泛应用于工程机械中&#xff0c…

这里推荐几个前端动画效果网站

1. AnimistaAnimista 是一个 CSS 动画/转场库和在线工具。它有许多现成的 CSS 动画片段可以直接使用,也可以在线定制动画。 网站地址:Animista - On-Demand CSS Animations Library 2. Animate.cssAnimate.css 是一个免费的 CSS 动画库,里面有 Attention Seekers 、 Bouncing E…

【Java|多线程与高并发】线程安全问题以及synchronized使用实例

文章目录 1. 前言2. 线程安全问题演示3.线程安全问题的原因4.synchronized关键字5. 总结 1. 前言 Java多线程环境下&#xff0c;多个线程同时访问共享资源时可能出现的数据竞争和不一致的情况。 线程安全一直都是一个令人头疼的问题.为了解决这个问题,Java为我们提供了很多方式…