C#的数据结构与类型转换

常见类型转换

在C#中,整数(`int`)和字符串(`string`)之间的转换非常常见,通常用于数据处理、输出、配置读取等场景。以下是几种常用的方法来实现这两种类型之间的转换:

整数转换为字符串

1. 使用 `ToString()` 方法:
   

 int number = 123;
 string strNumber = number.ToString();

2. 使用 `Convert.ToString()` 方法:
  

int number = 123;
string strNumber = Convert.ToString(number);

3.使用字符串插值或 `string.Format()`:
  

 int number = 123;
 string strNumber = string.Format("{0}", number);

4.使用”“+方法,字符串加上int类型的数据,结果还是string 类型,最常用:
   

 int number = 123;
 string strNumber = number+"";
 字符串转换为整数

1. 使用 `int.Parse()` 方法:
 

 string strNumber = "123";
 int number = int.Parse(strNumber);

2. 使用 `Convert.ToInt32()` 方法:
 

string strNumber = "123";
int number = Convert.ToInt32(strNumber);

3. 使用 `TryParse()` 方法:
   这种方法允许你安全地尝试转换,并且在转换失败时不会抛出异常,而是返回一个布尔值指示转换是否成功。
 

string strNumber = "123";
int number;
bool success = int.TryParse(strNumber, out number);

这些方法中,`Parse()` 和 `Convert.ToInt32()` 都会抛出异常如果字符串不能被解析成整数。而 `TryParse()` 方法则提供了一种更安全的方式来处理可能的转换失败。

注意,对于其他数值类型如 `long`, `short`, `double`, `float` 等,也有类似的 `Parse()`, `TryParse()`, 和 `ToString()` 方法可用。

日期类型转换及部分方法

字符串转日期

将字符串转换回DateTime对象,可以使用DateTime.ParseDateTime.ParseExactDateTime.TryParse等方法。

使用DateTime.Parse()方法。

string dateStr = "2024-07-04";
DateTime parsedDate = DateTime.Parse(dateStr);

使用DateTime.ParseExact()方法。这要求你提供一个确切的格式字符串,以确保字符串能准确匹配。

string dateStr = "2024-07-04";
string format = "yyyy-MM-dd";
DateTime parsedDate = DateTime.ParseExact(dateStr, format, CultureInfo.InvariantCulture);

使用 DateTime.TryParse()方法。这是一个更安全的解析方法,因为它不会抛出异常,而是返回一个布尔值指示是否成功。

string dateStr = "2024-07-04";
DateTime parsedDate;
bool success = DateTime.TryParse(dateStr, out parsedDate);
if (success)
{
   Console.WriteLine("成功转化");
}
else
{
   Console.WriteLine("转化失败!!");
}

使用 DateTime.TryParseExact()方法。如果你需要更精确的控制并避免异常,可以使用TryParseExact

string dateStr = "2024-07-04";
string format = "yyyy-MM-dd";
DateTime parsedDate;
bool success = DateTime.TryParseExact(dateStr, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsedDate);
Console.WriteLine(parsedDate.ToString());
获取当前的日期

可以使用DateTime.NowDateTime.UtcNow来获取当前的本地时间和UTC时间。 UTC时间指的是“协调世界时”,这是一种国际上广泛使用的时间标准。

DateTime now = DateTime.Now;       // 获取本地时间
DateTime utcNow = DateTime.UtcNow; // 获取UTC时间
Console.WriteLine(now);
Console.WriteLine(utcNow);
日期转字符串

DateTime对象转换为字符串,可以使用ToString方法,并指定一个格式字符串。

DateTime someDate = new DateTime(2024, 7, 4);
string dateString = someDate.ToString("yyyy-MM-dd"); 
string dateTimeString = someDate.ToString("yyyy-MM-dd HH:mm:ss"); 
Console.WriteLine(dateTimeString);
Console.WriteLine(dateString);

多维数组

在C#中,数组是一种固定大小的数据结构,用于存储相同类型的元素集合。一维、二维和三维数组分别代表了不同维度的数据集合。下面是以上三个类型的数组的例子:

一维数组

一维数组是最简单的数组类型,它只有一条轴线,可以看作是一个列表


int[] numbers = new int[5] { 1, 2, 3, 4, 5 };

int[] numbers = new int[5];
for(int i = 0 ;i<numbers.Length;i++)
{
    numbers[i] = i;
}

// 访问数组元素
foreach(int number in numbers)
{
    Console.WriteLine(number);
}
二维数组

二维数组可以看作是由一维数组构成的矩阵,拥有行和列两个维度。在遍历时需要使用二层for循环。在获取维度的长度时需要GetLength(),传入你想要获取的维度,需要注意的是维度从0开始。

int[,] arr = new int[2,2]{
        { 1,2},
        { 3,5}
    };
for (int i = 0; i < arr.GetLength(0); i++)
{
    for (int j = 0; j < arr.GetLength(1); j++)
    {
        
      Console.Write(a3[i, j] + " ");
    }
    Console.WriteLine();
}
三维数据

三维数组可以想象成一个立方体,它有三个维度:宽度、高度和深度。在遍历时需要使用三层for循环。

int[,,] arr = new int[2, 2, 2]
{
    {
        { 1,2},{ 3,5}
    },
    {
        { 5,6},{7,8 }
    }
};

for (int i = 0; i < arr.GetLength(0); i++)
{
    for (int j = 0; j < arr.GetLength(1); j++)
    {
        for (int k = 0; k < arr.GetLength(2); k++)
        {
            Console.Write(arr[i, j, k] + " ");
        }
        Console.WriteLine();
    }
    Console.WriteLine();
}

案例:随机数实现88笔,共3亿金额的费用记录

要求:有个88笔费用记录,总额3亿,金额在300万~800万之间。

首先我们可以将分配的88个300万先分出来,共2亿6400万,还剩下3600万,利用随机数生成88个在0到500万的数字。我们可以利用for循环来循环87次,并在每次生成随机数时判断是否大于3600万,如果大于则终止循环。接着我们可以判断for循环生成的动态数组是否有87个,如果没有的话,继续重新执行for循环,直到满足条件为止。

在生成符合条件的数组后,进行下一步,判断最后的元素是否大于500万,最后的元素=总金额-87个元素的总和,如果最后元素大于500万则重新继续上述的步骤,直到满足标准为止。

接着将88个元素每个都加300万,符合标准。

最后一步,计算程序耗时,在程序开始时定义start获取当前时间,在程序结尾定义end获取当前时间,然后相减,获得程序运行时间。


List<int> List()
{
    const int NUM = 88;
    const int MAX_MONEY = 5000000;
    const int MIN_MONEY = 0;
    const int SUM_MONEY = 236000000;


    Random rand = new Random();
    int sum = 0;
    List<int> list = new List<int>();
    //生成87个在0到500万的数字,且总和没有超过3600万。
    for (int i = 0; i < NUM - 1; i++)
    {
        int rand_money = rand.Next(MIN_MONEY, MAX_MONEY + 1);

        list.Add(rand_money);
        sum = rand_money + sum;
       // Console.WriteLine(sum);
        if (sum > SUM_MONEY)
        {
            return list;
        }

    }
    return list;
}
List<int> succeed = new List<int>();
//定义开始时间
DateTime start = DateTime.Now;
while (true) 
{ 

    while (true)
    {
        succeed = List();
        if(succeed.Count==87)
        {
            Console.WriteLine("即将生成正确的记录了!!!!");
            break;
        }
        else
        {
            Console.WriteLine("重新生成一次");
        }
    }
    //判断最后一个是否大于500万,如果大于则重新生成。
    int sum = succeed.Sum();
    int last_el = 236000000 - sum;
    if (last_el<=5000000)
    {
        succeed.Add(last_el);
        break;
    }
    else
    {
        Console.WriteLine("最后一个元素不满足条件。。。。。重新生成");
    }

}

//生成最终的结果
for (int i = 0; i < succeed.Count; i++)
{
    succeed[i] = succeed[i] + 3000000;
}
DateTime end = DateTime.Now;
TimeSpan timeSpan = end - start;
Console.WriteLine("花费了{0}ms",timeSpan.TotalMilliseconds);
foreach (var item in succeed)
{
    Console.Write(item+" ");
}

程序运行的结果:

 注意:因为3亿金额,88个记录,还要求300万到800万之间,利用random.next()生成的数,很容易就超出设定金额,所以我测试时使用的金额为5亿,这样可以更快的测试所写的代码是否有问题,经检验,所写代码可以满足业务需求,只不过需要循环很长时间。如果想要改变为3亿,可以将代码的SUM_MONEY变成3600万,还有在判断最后一个是否大于500万下面的代码的判断条件改为3600万。

常见的集合类型的存储结构和它们的作用以及优缺点

动态数组(泛型)

在C#中,“动态数组”这个术语通常指的是那些可以自动调整大小的数组结构,最典型的就是`List<T>`。`List<T>`是.NET Framework提供的一个泛型集合类,它内部使用一个动态数组来存储数据。下面是关于`List<T>`的一些详细信息,包括其存储结构、作用、优缺点以及一个实现案例。

存储结构
`List<T>`内部使用一个动态数组来存储元素。这意味着当数组空间不足时,`List<T>`会创建一个新的更大的数组,并将原有数组中的所有元素复制到新数组中,然后释放旧数组。这个过程称为“扩容”。`List<T>`的默认扩容策略通常是将数组大小翻倍,以减少频繁的数组复制操作。

 作用
`List<T>`的主要作用是提供一种灵活的数据结构,它可以存储任意数量的同类型元素,并且提供了许多内置的方法来操作这些元素,如添加、删除、查找、排序等。

 优点
1. **动态调整大小**:`List<T>`可以自动调整其内部数组的大小,无需程序员手动管理。
2. **类型安全**:由于`List<T>`是泛型的,所以它提供了类型安全,避免了装箱和拆箱的操作,提高了性能。
3. **丰富的API**:`List<T>`提供了许多实用的方法,如`Add()`, `Remove()`, `Contains()`, `Sort()`, `Reverse()`, `Find()`, `ForEach()`等。

缺点
1. **扩容操作开销**:虽然扩容可以减少频繁的数组复制,但每次扩容时仍然需要复制所有现有元素,这可能导致短暂的性能下降。
2. **内存开销**:由于`List<T>`总是预留一定的额外空间以备扩容,所以它可能会消耗比实际需要更多的内存。
3. **插入和删除性能**:在`List<T>`中间位置插入或删除元素会导致所有后续元素移动,这可能会比较慢。

实现案例
下面是一个使用`List<int>`的简单示例:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int>();

        numbers.Add(1);
        numbers.Add(2);
        numbers.Add(3);

        
        Console.WriteLine("The first number is: " + numbers[0]);

        
        numbers[0] = 10;
        Console.WriteLine("The modified first number is: " + numbers[0]);

        
        bool containsTwo = numbers.Contains(2);
        Console.WriteLine("Does the list contain 2? " + containsTwo);

        
        numbers.RemoveAt(1);
        Console.WriteLine("After removing, the list has " + numbers.Count + " elements.");

        
        foreach (int num in numbers)
        {
            Console.WriteLine(num);
        }
    }
}

在这个示例中,我们创建了一个`List<int>`实例,向其中添加了一些整数,然后进行了访问、修改、查找和删除操作。最后,我们遍历了整个列表并打印了所有元素。

动态数组(无泛型)

在C#中,非泛型的动态数组通常指的是`ArrayList`类,这是在.NET Framework早期版本中广泛使用的一种集合类型。

 存储结构
`ArrayList`本质上是一个动态数组,它使用一个数组来存储元素。当数组空间不足时,`ArrayList`会创建一个更大的数组并将所有元素复制过去,这个过程被称为“扩容”。

 作用
`ArrayList`允许你存储任意类型的对象,而无需指定具体类型。这在某些不关心类型安全性的场景下可能有帮助,但它也带来了运行时的类型检查开销和可能的类型转换异常。

优缺点
**优点**:
- 兼容旧版代码,特别是一些遗留系统可能依赖于非泛型集合。
- 可以存储任何类型的对象。

**缺点**:
- 缺乏类型安全性,可能导致运行时错误。
- 性能低于泛型集合,因为需要进行装箱和拆箱操作。

实现案例
下面是一个使用`ArrayList`的简单示例:

using System;
using System.Collections;

class Program
{
    static void Main()
    {
        ArrayList arrList = new ArrayList();
        
        arrList.Add(1);
        arrList.Add("Hello");
        arrList.Add(3.14);
        
        for (int i = 0; i < arrList.Count; i++)
        {
            Console.WriteLine(arrList[i]);
        }
        
        arrList.RemoveAt(1);
        
        Console.WriteLine("After removal:");
        
        foreach (object obj in arrList)
        {
            Console.WriteLine(obj);
        }
    }
}

在这个示例中,我们创建了一个`ArrayList`,向其中添加了不同类型的元素,然后遍历并打印了所有元素。接着,我们移除了其中一个元素,并再次遍历打印了更新后的列表。需要注意的是,当我们从`ArrayList`中读取元素时,需要进行类型转换才能正确地使用这些元素。

链表

在C#中,链表(`System.Collections.Generic.LinkedList<T>`)是一种动态数据结构,用于存储一组元素,其中每个元素都包含一个对其后续元素的引用。链表中的元素不是存储在连续的内存位置,而是通过指针(在C#中是引用)连接在一起,形成一个链状结构。

链表的存储结构
链表由一系列节点组成,每个节点包含两部分:
1. **数据部分**:存储用户数据。
2. **指针部分**:存储指向下一个节点的引用(对于双向链表,还包括指向前一个节点的引用)。

链表的作用
链表主要用于以下场景:
- 当频繁进行插入和删除操作时,链表比数组更高效,因为不需要移动大量元素。
- 当内存分配连续性不是关键因素时,链表可以节省内存,因为它们不需要预先分配大量连续的内存空间。

链表的优缺点
**优点**:
- 插入和删除操作速度快,尤其是对于链表的头部和尾部。
- 动态增长和收缩,不需要重新分配整个数组的内存。

**缺点**:
- 访问元素的时间复杂度为O(n),因为必须从头节点开始遍历直到找到目标元素。
- 额外的内存开销,因为每个节点都需要存储额外的指针。

实现案例
在C#中使用`LinkedList<T>`非常直观,下面是一个简单的示例,展示如何创建链表、添加元素、遍历链表和删除元素:```csharp

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        LinkedList<int> list = new LinkedList<int>();

        
        list.AddLast(1);
        list.AddLast(2);
        list.AddLast(3);

       
        list.AddFirst(0);

        
        foreach (var item in list)
        {
            Console.WriteLine(item);
        }

        
        list.RemoveFirst();

        
        foreach (var item in list)
        {
            Console.WriteLine(item);
        }

       
        list.Remove(list.Find(2));

        
        bool containsThree = list.Contains(3);
        Console.WriteLine(containsThree);
    }
}

这段代码演示了如何使用`LinkedList<T>`来创建一个链表,添加元素,删除元素,并检查链表是否包含特定元素。链表的灵活性和动态性使其在需要频繁修改数据集的情况下非常有用。

队列

在C#中,栈(`Stack<T>`)是一种遵循后进先出原则的线性数据结构。栈内部通常使用数组或链表实现,使得添加(压栈,Push)和移除(弹栈,Pop)操作可以在O(1)时间内完成,即时间复杂度是常量级别的。

存储结构
`Stack<T>`内部可以使用两种主要的存储结构:
1. **数组**:使用一个动态数组来存储元素,栈顶由一个索引变量追踪。
2. **链表**:使用链表结构,最后一个添加的元素位于链表的头部,这样可以直接访问而不必遍历整个链表。

作用
栈广泛应用于多种场景,如函数调用的参数和局部变量的管理、表达式的求值(例如逆波兰表示法)、括号匹配检查、深度优先搜索算法(DFS)等。

优缺点
**优点**:
- 快速的压栈和弹栈操作。
- 简化了数据的管理,因为只需要关注栈顶元素。

**缺点**:
- 不支持从栈的中间位置插入或删除元素。
- 如果栈的大小超过了可用内存,可能会导致溢出。

实现案例
下面是一个使用`Stack<int>`的简单示例:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Stack<int> myStack = new Stack<int>();
        
        myStack.Push(1);
        myStack.Push(2);
        myStack.Push(3);
        
        Console.WriteLine("Popped element: " + myStack.Pop());
        
        int peekedElement = myStack.Peek();
        Console.WriteLine("Peeked element: " + peekedElement);
        
        Console.WriteLine("Count after pop: " + myStack.Count);
    }
}

这个案例创建了一个`Stack<int>`,向其中添加了三个整数,然后弹出了栈顶元素并打印。之后,使用`Peek()`方法查看当前栈顶元素,但不将其移除。最后,输出栈中的元素个数。

哈希

在C#中,哈希表通常是指`Dictionary<TKey, TValue>`或`HashSet<T>`这样的集合,它们底层使用哈希表数据结构。这里我们将重点介绍`Dictionary<TKey, TValue>`,因为它体现了哈希表的主要特点和用途。

存储结构
`Dictionary<TKey, TValue>`内部使用一个哈希表来存储键值对。哈希表是一个数组,每个位置存储一个或多个键值对。为了定位键值对,哈希表使用一个哈希函数将键映射到数组的一个位置上。当两个不同的键映射到同一位置时,会发生哈希冲突,通常通过链地址法(链表)或开放寻址法来解决。

 作用
哈希表的主要作用是在平均情况下提供O(1)时间复杂度的查找、插入和删除操作,这对于需要快速查找和更新数据的应用非常有用。

 优缺点
**优点**:
- 平均情况下,查找、插入和删除操作都非常快。
- 支持任意类型作为键,只要该类型实现了`GetHashCode`和`Equals`方法。

**缺点**:
- 在最坏情况下,如果所有的键都映射到同一个位置,哈希表的性能会退化到O(n)。
- 需要额外的内存来存储哈希表的结构。

 实现案例
下面是一个使用`Dictionary<string, int>`的简单示例:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Dictionary<string, int> myDict = new Dictionary<string, int>();
        
        myDict.Add("apple", 1);
        myDict.Add("banana", 2);
        myDict.Add("cherry", 3);
        
        int value;
        if (myDict.TryGetValue("banana", out value))
        {
            Console.WriteLine(value);
        }
        
        myDict.Remove("cherry");
        
        foreach (KeyValuePair<string, int> pair in myDict)
        {
            Console.WriteLine(pair.Key + ": " + pair.Value);
        }
    }
}

在这个案例中,我们创建了一个`Dictionary<string, int>`,向其中添加了几个键值对,然后使用`TryGetValue`方法尝试获取一个键对应的值。接着,我们移除了其中一个条目,并遍历了字典打印剩余的键值对。

  • 16
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值