当前位置: 首页 > article >正文

C# 集合

集合

  • 概述
  • 集合接口和类型
  • 列表(ArrayList, List)
  • 队列(Queue)
  • 栈(Statck)
  • 链表(LinkedList)
  • 有序表(SortedList)
  • 字典
  • Lookup类
    • 其他字典类
  • HashSet(不重复项的无序列表)
  • 位数组
    • BitArray
    • BitVector32
  • 性能

概述

数组和Array类。数组的大小是固定的。如果元素个数是动态的,就应该使用集合类。
List和ArrayList是与数组相当的集合类。还有其他类型的集合:队列、栈、链表和字典。
集合:

  • 集合接口和类型
  • 列表
  • 队列
  • 链表
  • 有序表
  • 字典
  • Lookup
  • HashSet
  • 位数组
  • 性能

集合接口和类型

集合类可以组合为集合,存储 Object 类型的元素和泛型集合类。在 CLR 2.0 之前,不存在泛型。现在泛型集合类通常是集合的首选类型。泛型集合类是类型安全的,如果使用值类型,是不需要装箱操作的。如果要在集合中添加不同类型的对象,且这些对象不是相互派生的,例如在集合中添加 int和 string 对象,就只需基于对象的集合类。另一组集合类是专用于特定类型的集合,例如
StringCollection 类专用于 string 类型。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

列表(ArrayList, List)

.NET Framework 为动态列表提供了类 ArrayList 和 List。System.Collections.Generic 命名空间中的类 List的用法非常类似于System.Collections 命名空间中的 ArrayList 类。这个类实现了 IList、ICollection 和 IEnumerable 接口。

  • 调用默认的构造函数,就可以创建列表对象。在泛型类 List中,必须在声明中为列表的值指定类型。下面的代码说明了如何声明一个包含 int 的 List和一个包含 Racer 元素的列表。ArrayList是一个非泛型列表,可以将任意 Object 类型作为其元素。
  • 使用默认的构造函数创建一个空列表。元素添加到列表中后,列表的容量就会扩大为可接纳 4 个元素。如果添加了第 5 个元素,列表的大小就重新设置为包含 8 个元素。如果 8 个元素还不够,列表的大小就重新设置为 16。每次都会将列表的容量重新设置为原来的 2 倍。
  • 在C#中,ArrayList 是 .NET Framework 提供的一个动态数组类,它位于 System.Collections 命名空间下。ArrayList 可以根据需要自动调整大小,并且可以存储任意类型的对象(因为它是基于 object 类型的)。然而,在使用 ArrayList 时需要注意,由于它是非泛型集合,所以在添加或检索元素时会发生装箱和拆箱操作,这可能会导致性能损耗,特别是在处理值类型的时候。

主要特点:

  • 动态大小:可以根据需要增加容量。
  • 任意类型:可以包含任何类型的对象,包括混合类型。
  • 索引访问:支持通过索引快速访问元素。

  • 尽管 ArrayList 很灵活,但在现代C#开发中,更推荐使用泛型集合如 List,因为它提供了类型安全、避免了装箱和拆箱带来的性能开销,并且拥有与 ArrayList 类似的功能。List list = new List();list.Add(1);// 不会像 ArrayList 那样需要进行装箱操作
ArrayList objectList = new ArrayList();
List<int> intList = new List<int>();
List<Racer> racers = new List<Racer>();

创建了一个容量为 10 个元素的集合。如果该容量不足以容纳要添加的元素,就把集合的大小重新设置为 20,或 40,每次都是原来的 2 倍。

ArrayList objectList = new ArrayList(10);
List<int> intList = new List<int>(10);

使用Capacity属性可以获取和设置集合的容量:

objectList.Capacity = 20;
intList.Capactity = 20;

容量与集合中的个数不同。集合中元素个数可以用Count属性读取。当然,容量总是大于元素个数。只要不把元素添加到列表中,元素个数就是 0。

Console.WriteLine(intList.Count); // 输出元素个数

如果已经将元素添加到列表中,且不希望添加更多的元素,就可以调用TrimExcess()方法,去除不需要的容量。但是,重新定位是需要时间的,所以如果元素个数超过了容量的 90%,TrimExcess()方法将什么也不做。

intList.TrimExcess(); // 去除不需要的容量。
ArrayList arrayList = new ArrayList();
arrayList.Add(1);
arrayList.Add(2);
arrayList.Add("Hello");
arrayList.Add(3);
arrayList.Add(4);
arrayList.Add("World");
arrayList.Add(5);

           
// TODO:变量数组列表 
Debug.WriteLine("遍历数组列表:"); 
foreach (var item in arrayList)
{ 
    Debug.WriteLine(item);
}

Debug.WriteLine("下标访问数组列表:");
Debug.WriteLine(arrayList[0]);
Debug.WriteLine(arrayList[1]);
Debug.WriteLine(arrayList[2]);
Debug.WriteLine(arrayList[3]);
Debug.WriteLine(arrayList[4]);



arrayList.Remove(3);    // 移除元素3            
arrayList.RemoveAt(3);  // 移除第3个元素
Debug.WriteLine("移除元素3,移除第3个元素");
foreach (var item in arrayList)
{
    Debug.WriteLine(item);
}

arrayList.AddRange(new int[] { 10, 11, 12, 13, 14, 15 });
arrayList.RemoveRange(3, 2);

int[] ints = new int[] { 34, 54, 43, 21, 66 };
arrayList.InsertRange(2, ints); // 第2个位置插入数组
foreach (var item in arrayList)
{
    Debug.WriteLine(item);
}

//arrayList.Sort();       // 排序
//arrayList.Reverse();    // 反序

foreach (var item in arrayList)
{
    Debug.WriteLine(item);
}

//arrayList.Clear();  // 清空
//arrayList.Capacity = 10;
//Debug.WriteLine(arrayList.Count);

队列(Queue)

队列是其元素以先进先出(FIFO)的方式来处理的集合。先放在队列中的元素会先读取。队列的例子有在机场排的队、人力资源部中等待处理求职信的队列、打印队列中等待处理的打印任务、以循环方式等待 CPU 处理的线程。另外,还常常有元素根据其优先级来处理的队列。例如,在机场的队列中,商务舱乘客的处理要优先于经济舱的乘客。这里可以使用多个队列,一个队列对应一个优先级。在机场,这是很常见的,因为商务舱乘客和经济舱乘客有不同的登记队列。打印队列和线程也是这样。可以为一组队列建立一个数组,数组中的一项代表一个优先级。在每个数组项中,都有一个队列,其处理按照 FIFO 的方式进行。

在这里插入图片描述

Queue<string> queueStr = new Queue<string>();
Queue<int> queueInt = new Queue<int>(10);

queueStr.Enqueue("Hi");
queueStr.Enqueue("Hello");
queueStr.Enqueue("World");
Debug.WriteLine(queueStr.Peek());
Debug.WriteLine(queueStr.Dequeue());
Debug.WriteLine(queueStr.First());
Debug.WriteLine(queueStr.Last());             
queueStr.Clear();

queueInt.Enqueue(12);
queueInt.Enqueue(23);
queueInt.Enqueue(25);
queueInt.Enqueue(65);
foreach (var item in queueInt)
{
    Debug.WriteLine(item);
}

for (int i = 0; i < queueInt.Count; i++)
{
    Debug.WriteLine(queueInt.Dequeue());                
}

Debug.WriteLine(queueInt.Count);

栈(Statck)

栈是与队列非常类似的另一个容器,只是要使用不同的方法访问栈。最后添加到栈中的元素会最先读取。栈是一个后进先出(LIFO)容器。
在这里插入图片描述

 Stack<char> alphabet = new Stack<char>();
 alphabet.Push('A');
 alphabet.Push('B');
 alphabet.Push('C');            

 foreach (char c in alphabet)
 {
     Debug.Write(c+",");
 }

 Debug.WriteLine(alphabet.Pop());
 foreach (char c in alphabet)
 {
     Debug.Write(c + ",");
 }

链表(LinkedList)

  • LinkedList 集合类没有非泛型集合的类似版本。LinkedList 是一个双向链表,其元素指向它前面和后面的元素。
  • 链表的优点是,如果将元素插入列表的中间位置,使用链表会非常快。在插入一个元素时,只需修改上一个元素的 Next 引用和下一个元素的 Previous 引用,使它们引用所插入的元素。在 List 和ArrayList 类中,插入一个元素,需要移动该元素后面的所有元素。
  • 当然,链表也有缺点。链表的元素只能一个接一个地访问,这需要较长的时间来查找位于链表中间或尾部的元素。
  • 链表不仅能在列表中存储元素,还可以给每个元素存储下一个元素和上一个元素的信息。这就是LinkedList 包含 LinkedListNode 类型的元素的原因。使用 LinkedListNode 类,可以获得列表中的下一个元素和上一个元素。表 10-5 描述了 LinkedListNode 的属性。

主要特点:

  • 双向链表:每个节点都包含对其前一个和后一个节点的引用。
  • 高效插入和删除:在链表中间插入或删除节点比在数组或列表中更有效率,因为不需要移动其他元素。
  • 类型安全:作为泛型类,LinkedList 提供了类型安全性。
    在这里插入图片描述
 LinkedList<string> linkedList = new LinkedList<string>();
 linkedList.AddLast("Apple");
 linkedList.AddLast("Huawei");
 linkedList.AddFirst("Xiaomi");
 linkedList.AddLast("Huawei");

 foreach (var item in linkedList)
 {
     Debug.WriteLine(item);
 }

 var node = linkedList.Find("Apple");        // 查找元素
 linkedList.AddAfter(node, "Apple After");   // 在指定节点后添加元素
 linkedList.AddBefore(node, "Apple Before"); // 在指定节点前添加元素

 foreach (var item in linkedList)
 {
     Debug.WriteLine(item);
 }

 Debug.WriteLine(linkedList.ElementAt(0));   // 访问第0个元素
 Debug.WriteLine(linkedList.ElementAt(3));   // 访问第3个元素

有序表(SortedList)

  • 如果需要排好序的表,可以使用 SortedList<TKey, TValue>。这个类按照键给元素排序。

主要特点:

  • 有序存储:元素按键的升序排列。
  • 快速查找:由于内部使用了字典结构来存储数据,查找操作非常快。
  • 索引访问:除了通过键访问外,还可以通过索引来访问元素(注意这里的索引是基于键排序后的顺序)。
    在这里插入图片描述
    在这里插入图片描述
SortedList<string, int> sortedList = new SortedList<string, int>();
sortedList.Add("one", 111);
sortedList.Add("two", 222);
sortedList.Add("three", 333);
sortedList.Add("four", 444);
sortedList.Add("five", 555);
foreach (KeyValuePair<string, int> kvp in sortedList)
{
    Debug.WriteLine($"key:{kvp.Key}, value:{ kvp.Value}");
}

// 通过key获取值
int value_one = sortedList["one"];

// 通过索引获取Key
int value = sortedList.Values[2];
string key_two = sortedList.Keys[2];

Debug.WriteLine(value_one);
Debug.WriteLine(key_two);

sortedList.Remove("one");   //  删除键值对

字典

  • 字典表示一种非常复杂的数据结构,这种数据结构允许按照某个键来访问元素。字典也称为映射或散列表。字典的主要特性是能根据键快速查找值。也可以自由添加和删除元素,这有点像 List,但没有在内存中移动后续元素的性能开销。
  • 键的类型:用作字典中键的类型必须重写 Object 类的 GetHashCode()方法。只要字典类需要确定元素的位置,就要调用 GetHashCode()方法。GetHashCode()方法返回的 int 由字典用于计算放置元素的索引。这里不介绍这个算法。我们只需知道,它涉及到素数,所以字典的容量是一个素数。

GetHashCode()方法的实现代码必须满足如下要求:
● 相同的对象应总是返回相同的值。
● 不同的对象可以返回相同的值。
● 应执行得比较快,计算的开销不大。
● 不能抛出异常。
● 应至少使用一个实例字段。
● 散列码值应平均分布在 int 可以存储的整个数字区域上。
● 散列码最好在对象的生存期中不发生变化。
提示:
字典的性能取决于 GetHashCode()方法的实现代码。

主要特点:

  • 键唯一性:在一个字典中,每个键都是唯一的。如果尝试添加一个已经存在的键,旧值会被新值覆盖。
  • 快速查找:由于使用了哈希表,查找、插入和删除操作的时间复杂度接近 O(1)。
  • 类型安全:作为泛型类,Dictionary<TKey, TValue> 提供了类型安全性,避免了装箱和拆箱操作带来的性能损耗。
// 创建一个字符串,整型键值对
Dictionary<string, int> dict = new Dictionary<string, int>();
// 添加键值对
dict.Add("one", 1);
dict.Add("two", 2);
dict.Add("three", 3);

// 通过键访问值
int one = dict["one"];
Debug.WriteLine("one:" + one);

// 修改现有键的值
dict["one"] = 100;
Debug.WriteLine($"one new value is {dict["one"]}");

// 尝试访问不存在的键会抛出KeyNotFoundException异常
// 可以使用TryGetValue方法来安全地访问值
if (dict.TryGetValue("five", out int five))
{
    Debug.WriteLine($"five is {five}");
}
else
{
    Debug.WriteLine("five not found in the dict");
}

// 遍历所有键值对
foreach (KeyValuePair<string, int> pair in dict)
{
    Debug.WriteLine($"{pair.Key} is {pair.Value}");
}

// 遍历键
foreach (string name in dict.Keys)
{
    Debug.WriteLine(name);
}

// 遍历值
foreach (int i in dict.Values)
{
    Debug.WriteLine(i);
}

// 删除指定键的键值对
dict.Remove("one");

// 检查某个键是否存在
if (dict.ContainsKey("one"))
{
    Debug.WriteLine("one is still in the dict");
}
else
{
    Debug.WriteLine("one has bee removed.");
}

//dict.Count 键值对个数
//dict.Clear();   // 清空字典中的所以键值对
//dict.ContainsKey("two"); // 判断字典是否包含指定键
//dict.ContainsValue(5);// 判断字典是否包含指定值(注意这可能效率不高,因为它需要遍历所有值)。


// 使用对象初始化器
Dictionary<string, string> dict2 = new Dictionary<string, string>
{
    {"say","hello" },
    { "do","homework"}
};

foreach (KeyValuePair<string, string> pair in dict2)
{
    Debug.WriteLine($"{pair.Key} is {pair.Value}");
}

Lookup类

Dictionary<TKey, TValue>只为每个键支持一个值。新类 Lookup<TKey, TElement>是.NET 3.5 中新增的,它类似于 Dictionary<TKey, TValue>,但把键映射到一个值集上。这个类在程序集 System.Core中实现,用 System.Linq 命名空间定义。

主要特点:

  • 多值映射:与 Dictionary 不同,Lookup 允许一个键关联到一组值。
  • 不可变性:一旦创建,无法直接修改 Lookup 中的内容。
  • 高效查询:提供了基于键快速查找相关值集合的能力。

区别于 Dictionary:
虽然 Lookup 和 Dictionary 都用于存储键值对,但它们有几个关键的不同点:

  • Dictionary 中每个键只能对应一个值,而 Lookup 可以让一个键对应多个值。
  • Dictionary 在尝试访问不存在的键时会抛出 KeyNotFoundException,而 Lookup 则返回空集合。
  • Lookup 是不可变的,一旦创建就不能修改;而 Dictionary 是可变的,可以随时添加、删除或修改条目。
    在这里插入图片描述
var items = new[]
{
    new { Name = "Apple", Category = "Fruit"},
    new { Name = "Carrot", Category = "Vegetable"},
    new { Name = "Banana", Category = "Fruit" },
    new { Name = "Spinach", Category = "Vegetable" }
};

// 使用ToLookup创建Lookup
var lookup = items.ToLookup(item => item.Category, item => item.Name);

// 遍历Lookup
foreach (var category in lookup)
{
    Debug.WriteLine($"Category: {category.Key}");
    foreach (var name in category)
    {
        Debug.WriteLine($"-{name}");
    }
}

其他字典类

Dictionary<TKey, TValue>是 Framework 中的一个主要字典类,还有其他一些类,当然也有一些非泛型的字典类。
在这里插入图片描述

HashSet(不重复项的无序列表)

.NET 3.5 在 System.Collections.Generic 命名空间中包含一个新的集合类:HashSet。这个集合类包含不重复项的无序列表。这种集合称为"集(set)"。集是一个保留字,所以该类有另一个名称HashSet。这个名称很容易理解,因为这个集合基于散列值,插入元素的操作非常快,不需要像List类那样重排集合。

主要特点:

  • 唯一性:HashSet 中的每个元素都是唯一的。
  • 无序性:元素没有特定的顺序(从.NET Framework 4.7.2开始,HashSet 支持通过调用 OrderBy() 方法来排序,但默认情况下是无序的)。
  • 高性能:由于使用了哈希表,因此在进行插入、删除和查找操作时具有很高的效率。
  • 集合操作支持:如交集、并集、差集等数学集合运算。

特殊功能:

  • SetEquals(): 判断两个集合是否相等(即它们包含相同的元素)。
  • SymmetricExceptWith(): 计算对称差集(即属于其中一个集合但不属于两者的元素)。
  • Overlaps(): 检查两个集合是否有任何共同元素。
  • IsSubsetOf(), IsSupersetOf(): 判断一个集合是否为另一个集合的子集或超集。
    在这里插入图片描述
    在这里插入图片描述
// 创建一个整数类型的 HashSet
HashSet<int> numbers = new HashSet<int>();

// 添加元素
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);

// 尝试添加重复元素
bool added = numbers.Add(3); // 返回false,因为元素已经存在
Debug.WriteLine($"尝试添加重复元素的结果:{added}");

// 遍历HashSet内容
foreach (int num in numbers)
{
    Debug.WriteLine(num);
}

// 删除指定的元素
bool removed = numbers.Remove(2);
Debug.WriteLine($"是否成功移除元素2:{removed}");

// 检查运算是否存在
bool contains = numbers.Contains(1);
Debug.WriteLine($"HashSet 是否包含元素1:{contains}");


// 集合操作
HashSet<int> setA = new HashSet<int> { 1, 2, 3, 4 };
HashSet<int> setB = new HashSet<int> { 3, 4, 5, 6 };

// 计算并集
setA.UnionWith(setB);
Debug.WriteLine("并集结果:");
foreach (var item in setA)
{
    Debug.WriteLine(item + " ");
}

// 计算交集
setA.IntersectWith(setB);
Debug.WriteLine("\n交集结果:");
foreach (var item in setA)
{
    Debug.WriteLine(item + " ");
}

// 计算差集
// 计算差集
setA.ExceptWith(setB);
Debug.WriteLine("\n差集结果(A-B):");
foreach (var item in setA)
{
    Debug.Write(item + " ");
}

位数组

BitArray

  • 如果需要处理许多位,就可以使用类 BitArray 和结构 BitVector32。BitArray 位于命名空间System.Collections,BitVector32 位于命名空间 System.Collections.Specialized。这两种类型最重要的区别是,BitArray 可以重新设置大小,如果事先不知道需要的位数,就可以使用 BitArray,它可以包含非常多的位。BitVector32 是基于栈的,因此比较快。BitVector32 仅包含 32位,存储在一个整数中。

  • 类 BitArray 是一个引用类型,包含一个 int 数组,每 32 位使用一个新整数。

主要特点:

  • 内存效率:由于每个布尔值只占用1位,因此相比于使用 bool[] 数组,BitArray 可以显著减少内存消耗。
  • 动态大小:可以在创建后调整 BitArray 的大小。
  • 可变性:可以添加、移除或修改其中的元素。
  • 集合操作支持:支持按位逻辑运算,如 AND、OR、NOT 和 XOR。

注意事项:

  • 线程安全性:BitArray 不是线程安全的。如果需要在多线程环境中使用,您必须手动同步访问。
  • 性能考虑:虽然 BitArray 在内存使用上非常高效,但由于其内部实现基于位操作,在某些情况下,性能可能不如直接使用 bool[] 高效,特别是在进行频繁的随机访问时。
  • 类型转换:BitArray 提供了将内容复制到 byte[], int[] 等数组的方法,这对于与底层硬件通信或者与其他语言的数据交换非常有用。
    在这里插入图片描述
static void PrintBitArray(BitArray array)
{
    bool[] bits = new bool[array.Count];
    array.CopyTo(bits, 0);
    foreach (bool bit in bits)
    {
        Debug.WriteLine(bit ? "1" : "0");
    }
    Debug.WriteLine("");

}
// 创建一个包含10个false值的BitArray
BitArray bitArray = new BitArray(10);

// 打印初始状态
PrintBitArray(bitArray);

// 设置指定索引位置的值
bitArray.Set(3, true); // 将第4位置设为true
bitArray[7] = true; // 也可以这样设置值

Debug.WriteLine("修改后的BitArray:");
PrintBitArray(bitArray);


// 创建两个BitArray实例
BitArray a = new BitArray(new bool[] { true, false, true, false });
BitArray b = new BitArray(new bool[] { true, true, false, true });

// AND 操作
BitArray andResult = new BitArray(a.Count);
andResult.And(a);
andResult.And(b);
Debug.WriteLine("AND 操作结果:");
PrintBitArray(andResult);

// OR 操作
BitArray orResult = new BitArray(a.Count);
orResult.Or(a);
orResult.Or(b);
Debug.WriteLine("OR 操作结果:");
PrintBitArray(orResult);

// NOT 操作
BitArray notResult = new BitArray(a);
notResult.Not();
Debug.WriteLine("NOT 操作结果:");
PrintBitArray(notResult);

// 调整大小, 初始大小为5
BitArray bitArray2 = new BitArray(5);
bitArray2.SetAll(true); // 将所有位设置为true

// 改变大小到8,并保留原有数据
bitArray2.Length = 8;
Debug.WriteLine("调整大小后的BitArray:");
PrintBitArray(bitArray2);

BitVector32

如果事先知道需要的位数,就可以使用 BitVector32 结构替代 BitArray。BitVector32 效率较高,因为它是一个值类型,在整数栈上存储位。一个整数可以存储 32 位。如果需要更多的位,就可以使用多个 BitVector32 值或 BitArray。BitArray 可以根据需要增大,但 BitVector32 不能。

主要特点:

  • 固定大小:BitVector32 固定为 32 位,因此适用于存储少量数据。
  • 高效性:由于直接操作整数的位,因此非常高效。
  • 分段支持:可以通过定义分段来存储小范围的整数值,而不仅仅是布尔值。

注意事项:

  • 容量限制:由于 BitVector32 只有 32 位,因此它只能存储有限数量的信息。如果需要存储更多的数据,则应考虑使用 BitArray 或其他数据结构。
  • 线程安全性:BitVector32 不是线程安全的。如果需要在多线程环境中使用,必须手动同步访问。
  • 性能:由于其底层基于位操作,BitVector32 在处理布尔值和小整数值时具有很高的性能效率。
    在这里插入图片描述
// 创建一个新的BtVector32
BitVector32 bv = new BitVector32();

// 定义一些标志字段
BitVector32.Section isRunningSection = BitVector32.CreateSection(1);
BitVector32.Section countSection = BitVector32.CreateSection(255, isRunningSection);

// 设置标志
bv[isRunningSection] = 1; // 设置运行状态为true
bv[countSection] = 127; // 设置计数值

// 获取标志
bool isRunning = bv[isRunningSection] == 1;
int count = bv[countSection];

Debug.WriteLine($"Is Running:{isRunning}, Count:{count}");

// 打印整个BitVector32的二进制表示形式
Debug.WriteLine(Convert.ToString(bv.Data, 2).PadLeft(32, '0'));

性能

许多集合类都提供了相同的功能,例如,SortedList 与 SortedDictionary 的功能几乎完全相同。但是,其性能常常有很大区别。一个集合使用的内存少,另一个集合的元素检索速度快。在 MSDN 文档中,集合的方法常常有性能提示,给出了以大 O 记号表示的操作时间:

  • O(1):表示无论集合中有多少数据项,这个操作需要的时间都不变。
  • O(n):表示对于集合中的每个元素,需要增加的时间量都是相同的。
  • O(log n):表示操作需要的时间随集合中元素的增加而增加,但每个元素需要增加的时间不是线性的,而是呈对数曲线。

在这里插入图片描述


http://www.kler.cn/a/585796.html

相关文章:

  • 卷积神经网络(CNN)之 EfficientNet
  • 【RTSP】客户端(三) 音频相关
  • 计算机视觉算法实战——花卉识别(主页有源码)
  • Spring框架详解(IOC容器-上)
  • JVM 如何保证 Java 程序的安全性?
  • TypeScript 高级类型 vs JavaScript:用“杂交水稻”理解类型编程
  • 【redis】set 类型:基本命令
  • 遥感数据获取、处理、分析到模型搭建全流程学习!DeepSeek、Python、OpenCV驱动空天地遥感数据分析
  • WPF程序使用AutoUpdate实现自动更新
  • Secs/Gem第一讲(基于secs4net项目的ChatGpt介绍)
  • 完善机器人:让 DeepSeek 使用Vue Element UI快速搭建 AI 交互页面
  • 【Linux系统编程】管道
  • 什么是mysql索引回表?
  • 杨辉三角形(信息学奥赛一本通-2043)
  • 智慧应急消防解决方案(35页PPT)(文末有下载方式)
  • doris:SQL 方言兼容
  • 【0x80070666】-已安装另一个版本...(Tableau 安装失败)
  • 裸机开发-GPIO外设
  • Android的第一次面试(Java篇)
  • 为什么 JPA 可以通过 findByNameContaining 自动生成 SQL 语句?