C# 集合(9) 持续更新

数组的大小是固定的。如果元素个数动态,就使用集合类。

List<T>是与数组相当的集合类。其他的集合:队列、栈、链表、字典和集。

.NET Framework 1.0 包括非泛型集合类,如 ArrayList 和 HashTable 。

创建List

List<int> list = new List<int>();

使用默认构造函数创建一个空列表。如果列表添加元素后,容量会扩大为 4 个元素。如果添加 第 5 个元素,容量为 8 。

如果列表的容量改变了,整个集合就要重新分配到一个新的内存块中。为了节省时间,可以直接创建一个容量为10的集合。

List<int> list = new List<int>(10);
Console.WriteLine(list.Capacity);
list.Capacity = 20;

TrimExcess 方法

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

list.TrimExcess();

集合初始化值

List<int> intList = new List<int>(){1,2,3,4,5};
List<string> stringList = new List<string>(){"one", "two"};

编译器会把集合初始化值设定项变成,在列表中的每一项调用 Add 方法。

添加元素 Add

List<int> intList = new List<int>(){1,2,3,4,5};
intList.Add(1);
intList.Add(2);

AddRange 一次给集合添加多个元素

intList.AddRange(new int[]{1,3,4});

也可以向构造函数传递

List<int> intList = new List<int>(new int[] { 1, 3, 4 });

插入元素 Insert

 List<int> intList = new List<int>(new int[] { 1, 3, 4 });
 intList.Insert(2,1);
 intList.InsertRange(2, new int[] { 33 });

访问元素

int value = intList[2];

可以通过索引访问的集合类有 ArrayList、StringCollection 和 List<T> 。

foreach 遍历集合中的元素时。

编译器解析 foreach 语句时,利用了 IEnumerator 和 IEnumerable 接口。

 List<int> intList = new List<int>(new int[] { 1, 3, 4 });
 foreach (int value in intList)
 {
     Console.WriteLine(value);
 }
 Console.WriteLine("**************");
 intList.ForEach(Console.WriteLine);
 Console.WriteLine("**************");
 intList.ForEach(ActionFun);
 Console.WriteLine("**************");
 intList.ForEach(i => Console.WriteLine(i));

删除元素

List<int> intList = new List<int>(new int[] { 1, 3, 4 });
//intList.RemoveAt(intList.IndexOf(1));
//intList.Remove(1);
intList.RemoveRange(1,2);
intList.ForEach(Console.WriteLine);

搜索元素

FindIndex

需要一个 Predicate<T> 参数

使用Predicate<T>委托变量引用一个“判断条件函数”,换句话说,就是需要一个 返回类型是 Bool

private static void Main(string[] args)
{
    List<int> intList = new List<int>(new int[] { 1, 3, 4,12,13,14,15 });
    compare = 4;
    Console.WriteLine(intList.FindIndex(ActionFun));
    Console.WriteLine(intList.IndexOf(4));
    Console.WriteLine("输出大的值");
    List<int> bitIntList = intList.FindAll(v => v >= 12);
    bitIntList.ForEach(Console.WriteLine);
}

private static int compare;
private static bool ActionFun(int i)
{
    return compare == i;
}

排序元素

Sort函数

public void Sort();
public void Sort(Comparison<T> comparison);
public void Sort(IComparer<T> comparer);
public void Sort(int index, int count, IComparer<T> comparer);

 

List<int> bitIntList = intList.FindAll(v => v >= 12);
bitIntList.Sort();
bitIntList.ForEach(Console.WriteLine);

翻转元素

bitIntList.Reverse();

类型转化

 List<int> intList = new List<int>(new int[] { 1, 3, 4,12,13,14,15 });
 List<uint> uintList = intList.ConvertAll(r => (uint) r);
 foreach (uint v in uintList)
 {
     Console.WriteLine(v);
 }

只读集合

List<int> intList = new List<int>(new int[] { 1, 3, 4,12,13,14,15 });
ReadOnlyCollection<int> readOnlyList = intList.AsReadOnly();
foreach (int i in readOnlyList)
{
    Console.WriteLine(i);
}

队列

队列是其元素以先进先出(FIFO)的方式处理集合。先放入队列中的元素会先读取。

Enqueue方法在队列的一端添加元素,DeQueue方法在队列一端读取和删除元素。

Peek             从队列中读取第一个元素,但不删除它。

TrimExcess   重新设置队列容量

 

 Queue<int> intQueue = new Queue<int>();
 for (int i = 0; i < 12; i++)
 {
     intQueue.Enqueue(i);
 }

 while (intQueue.Count > 0)
 {
     Console.WriteLine(intQueue.Dequeue());
 }

栈是后进先出(LIFO)的容器。

Push    向末尾添加有元素。

Pop     返回末尾元素,并删除该元素。

Stack<int> intList = new Stack<int>();
for (int i = 0; i < 3; i++)
{
    intList.Push(i);
}

for (int i = 0; i < 3; i++)
{
    Console.WriteLine(intList.Pop());
}

链表

LinkedList<T> 是双向链表。

通过移动下一个元素,可以正向遍历整个链表。

通过移动前一个元素,反向遍历整个链表。

链表优点

如果将元素插入列表的中间位置,使用链表就会非常快。插入一个元素时,只需要修改上一个元素的Next引用和下一个元素的Previous引用,使它们引用所插入的元素。 而在List<T>类中,插入一个元素时,需要移动该元素后面的所有元素。

链表缺点

链表的元素只能一个接一个地访问,所以这需要较长的时间来查找位于链表中间或尾部的元素。

 

使用LinkedListNode<T>类,可以获得列表中的下一个元素和上一个元素。LinkedListNode<T>定义了 List、Next、Previous 和 Value 。

List 属性返回与节点相关的 LinkedList<T>对象, Next 和 Previous,下一个元素 和 上一个元素。  Value 返回与节点相关的元素,其类型是 T 。

 

LinkedList<T>类定义的方法

第一个元素(First)、第二个元素(Last)

指定位置插入元素(  AddAfter()、AddBefore()、AddFirst()、AddLast()  )

删除指定位置元素 (  Remove()、RemoveFirst()、RemoveLast()  )

搜索元素  (   从开头搜索 Find()   从结尾搜索 FindLast()     )

 

public class PriorityDocumentManager
{
  private readonly LinkedList<Document> documentList;

  // priorities 0.9
  private readonly List<LinkedListNode<Document>> priorityNodes;

  public PriorityDocumentManager()
  {
    documentList = new LinkedList<Document>();

    priorityNodes = new List<LinkedListNode<Document>>(10);
    for (int i = 0; i < 10; i++)
    {
      priorityNodes.Add(new LinkedListNode<Document>(null));
    }
  }

  public void AddDocument(Document d)
  {
    // Contract.Requires<ArgumentNullException>(d != null, "argument d must not be null");
    //  if (d == null) throw new ArgumentNullException("d");

    AddDocumentToPriorityNode(d, d.Priority);
  }

  private void AddDocumentToPriorityNode(Document doc, int priority)
  {
    // Contract.Requires<ArgumentException>(priority >= 0 && priority < 10, "priority value must be between 0 and 9");
    //if (priority > 9 || priority < 0)
    //    throw new ArgumentException("Priority must be between 0 and 9");

    if (priorityNodes[priority].Value == null)
    {
      --priority;
      if (priority >= 0)
      {
        // check for the next lower priority
        AddDocumentToPriorityNode(doc, priority);
      }
      else // now no priority node exists with the same priority or lower
      // add the new document to the end
      {
        documentList.AddLast(doc);
        priorityNodes[doc.Priority] = documentList.Last;
      }
      return;
    }
    else // a priority node exists
    {
      LinkedListNode<Document> prioNode = priorityNodes[priority];
      if (priority == doc.Priority)
      // priority node with the same priority exists
      {
        documentList.AddAfter(prioNode, doc);

        // set the priority node to the last document with the same priority
        priorityNodes[doc.Priority] = prioNode.Next;
      }
      else // only priority node with a lower priority exists
      {
        // get the first node of the lower priority
        LinkedListNode<Document> firstPrioNode = prioNode;

        while (firstPrioNode.Previous != null &&
           firstPrioNode.Previous.Value.Priority == prioNode.Value.Priority)
        {
          firstPrioNode = prioNode.Previous;
          prioNode = firstPrioNode;
        }

        documentList.AddBefore(firstPrioNode, doc);

        // set the priority node to the new value
        priorityNodes[doc.Priority] = firstPrioNode.Previous;
      }
    }
  }

  public void DisplayAllNodes()
  {
    foreach (Document doc in documentList)
    {
      Console.WriteLine("priority: {0}, title {1}", doc.Priority, doc.Title);
    }
  }

  // returns the document with the highest priority
  // (that's first in the linked list)
  public Document GetDocument()
  {
    Document doc = documentList.First.Value;
    documentList.RemoveFirst();
    return doc;
  }

}

 

PriorityDocumentManager pdm = new PriorityDocumentManager();
//pdm.AddDocument(new Document("one", "Sample", 8));
pdm.AddDocument(new Document("two", "Sample", 1));
//pdm.AddDocument(new Document("three", "Sample", 4));
//pdm.AddDocument(new Document("four", "Sample", 8));
//pdm.AddDocument(new Document("five", "Sample", 1));
//pdm.AddDocument(new Document("six", "Sample", 9));
pdm.AddDocument(new Document("seven", "Sample", 2));
pdm.AddDocument(new Document("eight", "Sample", 1));

pdm.DisplayAllNodes();

 

有序列表

SortedList mySL1 = new SortedList();
mySL1.Add(1.3, "fox");
mySL1.Add(1.4, "jumped");
mySL1.Add(1.5, "over");
mySL1.Add(1.2, "brown");
mySL1.Add(1.1, "quick");
mySL1.Add(1.0, "The");
mySL1.Add(1.6, "the");
mySL1.Add(1.8, "dog");
mySL1.Add(1.7, "lazy");

//获得指定索引处的键和值
int myIndex = 3;
Console.WriteLine("The key   at index {0} is {1}.", myIndex, mySL1.GetKey(myIndex));
Console.WriteLine("The value at index {0} is {1}.", myIndex, mySL1.GetByIndex(myIndex));

看看存储的顺序

为了避免访问不存在的键。

可以使用 ContainsKey 判断是否存在该键,用 TryGetValue 尝试获得该键的值。

注意 使用 TryGetValue 要保证存在该键,否则会出现 抛出 键不存在的异常。

SortedList<int,string> mySL1 = new SortedList<int, string>();
mySL1.Add(11, "fox");
Console.WriteLine(mySL1.ContainsKey(11));
string value;
if(mySL1.TryGetValue(11,out value))
    Console.WriteLine("key 11 value " + value);

SortedList 键对一个值。如果需要对多个值,可以用 Lookup。

Lookup https://msdn.microsoft.com/en-us/library/bb460184(v=vs.110).aspx

遍历

foreach (KeyValuePair<int,string> sl in mySL1)
{
    Console.WriteLine(sl.Key + "  " + sl.Value);
}

字典

字典允许按照键访问按照键访问元素。字典也称为映射或散列列表。字典主要特性能根据键快速查找值。可以自由添加和删除元素。有点像List<T>类,但没有在内存中移动后续元素的性能开销。

字典中的键会转换为一个散列。利用散列创建一个数字,将索引和值关联起来。然后索引包含一个到值的链接。

键的类型

键是通过重写Object类的GetHasCode方法,GetHasCode必须满足一下要求

  • 相同的对象总是返回相同的值。
  • 不同的对象可以返回相同的值。
  • 执行比较快,计算开销较小。
  • 不能抛出异常。
  • 至少使用一个实例字段
  • 散列代码值平均分布在int可以存在整个数字范围上。
  • 散列代码最好在对象的生存期中不发生变化。

字典的主要性能取决于 GetHashCode 方法的实现。

除了实现GetHashCode方法外还要实现Equals方法。因为不同的键对象可能返回相同的散列代码。字典使用Equals比较键,字典检查两个键是否相等。如果相等 GetHashCode 方法 须返回相同的散列代码。

public override int GetHashCode()
{
    int hashCode = (number ^ number << 16) * 0x15051505;
    return hashCode;
}

先将数字向左移动16位,再与原来的数字进行异或,再结果乘以十六进制数 15051505。散列代码在整数取值区域上的分布相当均匀。

Serializable 表示该对象可以序列化。

http://www.cnblogs.com/zwl12549/archive/2007/08/14/854718.html

示例 

EmployeeId .cs

[Serializable]
public class EmployeeIdException : Exception
{
  public EmployeeIdException(string message) : base(message) { }
}

[Serializable]
public struct EmployeeId : IEquatable<EmployeeId>
{
  private readonly char prefix;
  private readonly int number;

  public EmployeeId(string id)
  {
    // Contract.Requires<ArgumentNullException>(id != null);

    prefix = (id.ToUpper())[0];
    int numLength = id.Length - 1;
    try
    {
      number = int.Parse(id.Substring(1, numLength > 6 ? 6 : numLength));
    }
    catch (FormatException)
    {
      throw new EmployeeIdException("Invalid EmployeeId format");
    }
  }

  public override string ToString()
  {
    return prefix.ToString() + string.Format("{0,6:000000}", number);
  }

  public override int GetHashCode()
  {
      int hashCode = (number ^ number << 16) * 0x15051505;
    return hashCode;
  }

  public bool Equals(EmployeeId other)
  {
    if (other == null) return false;

    return (prefix == other.prefix && number == other.number);
  }

  public override bool Equals(object obj)
  {
    return Equals((EmployeeId)obj);
  }

  public static bool operator ==(EmployeeId left, EmployeeId right)
  {
    return left.Equals(right);
  }

  public static bool operator !=(EmployeeId left, EmployeeId right)
  {
    return !(left == right);
  }
}

Employee.cs

[Serializable]
public class Employee
{
  private string name;
  private decimal salary;
  private readonly EmployeeId id;

  public Employee(EmployeeId id, string name, decimal salary)
  {
    this.id = id;
    this.name = name;
    this.salary = salary;
  }

  public override string ToString()
  {
    return String.Format("{0}: {1, -20} {2:C}",
          id.ToString(), name, salary);
  }
}

Main

static void Main()
{
  var employees = new Dictionary<EmployeeId, Employee>(31);

  var idTony = new EmployeeId("C3755");
  var tony = new Employee(idTony, "Tony Stewart", 379025.00m);
  employees.Add(idTony, tony);
  Console.WriteLine(tony);

  var idCarl = new EmployeeId("F3547");
  var carl = new Employee(idCarl, "Carl Edwards", 403466.00m);
  employees.Add(idCarl, carl);
  Console.WriteLine(carl);

  var idKevin = new EmployeeId("C3386");
  var kevin = new Employee(idKevin, "Kevin Harwick", 415261.00m);
  employees.Add(idKevin, kevin);
  Console.WriteLine(kevin);

  var idMatt = new EmployeeId("F3323");
  var matt = new Employee(idMatt, "Matt Kenseth", 1589390.00m);
  employees[idMatt] = matt;
  Console.WriteLine(matt);

  var idBrad = new EmployeeId("D3234");
  var brad = new Employee(idBrad, "Brad Keselowski", 322295.00m);
  employees[idBrad] = brad;
  Console.WriteLine(brad);



  while (true)
  {
    Console.Write("Enter employee id (X to exit)> ");
    var userInput = Console.ReadLine();
    userInput = userInput.ToUpper();
    if (userInput == "X") break;

    EmployeeId id;
    try
    {
      id = new EmployeeId(userInput);


      Employee employee;
      if (!employees.TryGetValue(id, out employee))
      {
        Console.WriteLine("Employee with id {0} does not exist", id);
      }
      else
      {
        Console.WriteLine(employee);
      }
    }
    catch (EmployeeIdException ex)
    {
      Console.WriteLine(ex.Message);
    }
  }
}

Lookup 键对多个值

Lookup 须调用 ToLookup 方法,该方法返回一个 Lookup<TKey,Telement>对象。ToLookup方法是一个扩展方法,用于实现IEnumerable<T>接口的所有类。

有序的字典

SortedDictionary<TKey, TValue>类是一个二叉搜索树,其中的元素根据键来排序。该键类型必须实现IComparer<TKey>接口。如果键类型不能排序,则还可以创建 IComparer<TKey> 接口的比较容器。将比较器用作有序字典的构造函数的参数。

SortedDictionary跟SortedList 类似。SortedList 类 使用的内存比 SortedDictionary 类少。 SortedDictionary 在插入和删除排序的数据时比较快。

包含不重复元素的集合称为“集(set)”。包括两种集合

HashSet<T> 包含不重复元素的无序列表。

SortedSet<T> 包含不重复元素的有序列表。

它们都实现ISet<T>接口。

HashSet<string> stringHashSet = new HashSet<string>() { "Zhao","Li" };
Console.WriteLine(stringHashSet.Add("Zhao"));
Console.WriteLine(stringHashSet.Add("Bao"));

Add方法 返回布尔值,如果该元素已经存在集,就不添加,并返回false。

IsSubsetOf() 和 IsSupersetOf() 比较集 实现了IEnumerable<T>接口的集合结果。

 

IsSubsetOf 验证 stringHashSet2 每个元素是否都存在 stringHashSet1。

HashSet<string> stringHashSet1 = new HashSet<string>() { "Zhao","Li" };
HashSet<string> stringHashSet2 = new HashSet<string>() { "Zhao", "Li", "wang" };

if (stringHashSet2.IsSubsetOf(stringHashSet1))
{
    Console.WriteLine("stringHashSet2 中每个元素都存在 stringHashSet1 集合方法中 ");
}
else
{
    Console.WriteLine("不存在");
}

IsSupersetOf 判断 有没有额外的元素。

if (stringHashSet2.IsSupersetOf(stringHashSet1))
{
    Console.WriteLine("stringHashSet2 是否有没有 stringHashSet1 额外的元素 ");
}

Overlaps  判断集合是否有重叠。

if (stringHashSet2.Overlaps(stringHashSet1))
{
    Console.WriteLine("有重叠");
}

UnionWith 合并两个集合

SortedSet<string> allTeams = new SortedSet<string>(stringHashSet1);
allTeams.UnionWith(stringHashSet2);
foreach (var team in allTeams)
{
    Console.WriteLine(team);
}

ExceptWith 排除有重叠的集合

SortedSet<string> allTeams = new SortedSet<string>(stringHashSet2);
allTeams.ExceptWith(stringHashSet1);
foreach (var team in allTeams)
{
    Console.WriteLine(team);
}

可观查的集合

ObservableCollection<T> 这个类在WPF中定义的,所以需要引用程序集 WindowBase 。 引用名称空间 using System.Collections.ObjectModel 。

static void Main()
{
    var data = new ObservableCollection<string>();
    data.CollectionChanged += Data_CollectionChanged;
    data.Add("One");
    data.Add("Two");
    data.Insert(1, "Three");
    data.Remove("One");
}

static void Data_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    Console.WriteLine("action: {0}", e.Action.ToString());

    if (e.OldItems != null)
    {
        Console.WriteLine("starting index for old item(s): {0}", e.OldStartingIndex);
        Console.WriteLine("old item(s):");
        foreach (var item in e.OldItems)
        {
            Console.WriteLine(item);
        }
    }
    if (e.NewItems != null)
    {
        Console.WriteLine("starting index for new item(s): {0}", e.NewStartingIndex);
        Console.WriteLine("new item(s): ");
        foreach (var item in e.NewItems)
        {
            Console.WriteLine(item);
        }
    }
}

位数组

BitArray 和 BitVector32

他们最重要的区别是 BitArray 可以重新设置大小,包含非常多的位。

BitVector32 是基于栈的,因此非常快,它仅包含32位,存储一个整数中。

BitArray类

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

方法

Count Length                     Count 和 Length 都返回数组的长度,Length 赋值,可以重新设置长度
Item Get Set                     Item 使用索引器读写数组中的位。 Get和Set 一样
SetAll                           设置所有位的值
Not                              所有值取反
And Or Xor                       And 执行二元And,只要两个数组位都为1时,结果位才是1。 Or 只要一个数组位为1时,结果就是 1。 Xor 异或操作 只有两个位不一样时,才会 1 如  10001 xor 10000 = 00001

 

BitArray bitArray = new BitArray(10);
bitArray.SetAll(true);
bitArray.Set(2,false);
foreach (bool b in bitArray)
{
    Console.Write(b ? 1 : 0);
}
Console.WriteLine();

BitVector32

如果事先知道需要的位数,可以用BitVector32 代替 BitArray 类。BitVector32效率较高,因为是值类型,栈上存储位。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

 

 

 

 

并发集合

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

 

 

 

性能

集合类提供很多相同的功能。 如 SortedList 类 与 SortedDictionary 类 的功能几乎完全相同。 一个集合使用的内存少。另外一个集合元素 检索速度快。

 

转载于:https://www.cnblogs.com/z888/p/5802372.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值