开卷目录:
集合
一.汇集接口和类型
接口 |
说明 |
IEnumerable<T> |
如果foreach语句用于集合,就需要IEnumerable接口.这个借口定义了方法GetEnumerator(),他返回一个实现了IEnumerator接口的枚举 |
ICollection<T> |
ICollection<T>接口有泛型集合类实现.使用这个借口可以获得集合中的元素个数(Count属性),把集合复制到数组中(CopyTo()方法),还可以从集合中添加和删除元素(Add(),Remove(),Clear()) |
List<T> |
IList<T>接口用于可通过位置访问其中的元素列表,这个接口定义了一个 索引器,可以在集合的指定位置插入或删除 mount些项(Insert()和Remove()方法).IList<T>接口派生自ICollection<T>接口 |
ISet<T> |
ISet<T>接口是.NET4中新增的.实现这个接口的集允许合并不同的集.获得两个集的交集,检查两个集合是否重叠.ISet<T>接口派生自ICollection<T>接口 |
IDictionary<TKey,TValue> |
IDictionary<TKey,TValue>接口由包含键和值的泛型集合类 实现.使用这个接口可以访问所有的键和值,使用键类型的索引器可以访问某些项,还可以添加或删除某些项 |
ILookup<TKey,TValue> |
ILookup<TKey,TValue>接口类似于IDictionary<TKey,TValue>接口,实现该接口的集合有键和值,且可以通过一个键包含多个值 |
IComparer<T> |
接口ICommparer<T>由比较器实现,通过Comparer()方法给集合中的元素排序 |
IEqualityComparer<T> |
接口IEqualityComparer<T>由一个比较器实现,该比较器可用于字典中的键.使用这个接口,可以对对象进行相等性比较.在.NET中,这个接口也由数组和元组实现 |
IProducerConsumerColllection<T> |
IProducerConsumerCollection<T>接口是.NET4中新增的,它支持新的线程安全的集合类 |
IReadOnlyList<T>、 IReadOnlyDictionary<T>、 IReadOnlyCollection<T> |
初始化后不能修改的集合,只能检索对象,不能添加和删除. |
2.列表
先看看1个实例:
[Serializable]
public class Racer : IComparable<Racer>, IFormattable
{
public int Id { get; private set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Country { get; set; }
public int Wins { get; set; }
public Racer(int id, string firstName, string lastName, string country)
: this(id, firstName, lastName, country, wins: 0)
{
}
public Racer(int id, string firstName, string lastName, string country, int wins)
{
this.Id = id;
this.FirstName = firstName;
this.LastName = lastName;
this.Country = country;
this.Wins = wins;
}
public override string ToString()
{
return String.Format("{0} {1}", FirstName, LastName);
}
public string ToString(string format, IFormatProvider formatProvider)
{
if (format == null) format = "N";
switch (format.ToUpper())
{
case null:
case "N": // name
return ToString();
case "F": // first name
return FirstName;
case "L": // last name
return LastName;
case "W": // Wins
return String.Format("{0}, Wins: {1}", ToString(), Wins);
case "C": // Country
return String.Format("{0}, Country: {1}", ToString(), Country);
case "A": // All
return String.Format("{0}, {1} Wins: {2}", ToString(), Country, Wins);
default:
throw new FormatException(String.Format(formatProvider,
"Format {0} is not supported", format));
}
}
public string ToString(string format)
{
return ToString(format, null);
}
public int CompareTo(Racer other)
{
if (other == null) return -1;
int compare = string.Compare(this.LastName, other.LastName);
if (compare == 0)
return string.Compare(this.FirstName, other.FirstName);
return compare;
}
}
View Code
Immutable Collections(1)
概述
在条分缕析String的源码以前,计划先介绍一些关于JVM的内部存款和储蓄器遍布,那样有助于我们越来越好地去领会String的统一准备:
JVM内部存款和储蓄器模型
Method
Area:方法区,当虚拟机装载多少个class文件时,它会从这一个class文件包括的2进制数据中分析类型消息,然后把那些类型消息(包涵类音讯、常量、静态变量等)放到方法区中,该内存区域被抱无线程共享,当地点法区存在一块出色的内部存款和储蓄器区域,叫常量池(Constant
Pool)。
Heap:堆是Java虚拟机所管理的内部存款和储蓄器中最大的一块。Java堆是被抱有线程共享的1块内部存储器区域,Java中的。
Stack:栈,又叫货仓可能虚拟机栈。JVM为各样新创建的线程都分配二个栈。也便是说,对于贰个Java程序来讲,它的运转正是经过对栈的操作来实现的。栈以帧为单位保存线程的情景。JVM对栈只举办二种操作:以帧为单位的压栈和出栈操作。大家领略,某些线程正在试行的法子称为此线程的当下格局。
Program Count
Register:程序计数器,又叫程序寄存器。JVM帮忙四个线程同时运转,当每一个新线程被成立时,它都将获得它本身的PC寄存器(程序计数器)。假设线程正在实践的是1个Java方法(非native),那么PC寄存器的值将接连指向下一条将被推行的通令,假如艺术是
native的,程序计数器寄存器的值不会被定义。
JVM的主次计数器寄存器的上升的幅度丰硕保险能够具备一个回去地址大概native的指针。
Native Stack:当地方法栈,存款和储蓄当地点艺术的调用状态。
常量池(constant
pool)指的是在编译期被分明,并被保留在已编写翻译的.class文件中的一些数量。它包罗了关于类、方法、接口等中的常量,也囊括字符串常量。Java把内部存款和储蓄器分为堆内部存款和储蓄器跟栈内存,前者首要用来存放在对象,后者用于存放基本项目变量以及对象的引用。
- 不可变对象
- 自定义不可变会集
- Net提供的不可变集合
- 不可变优点
- 不可变对象缺点
创办列表
运用暗许的构造函数创设五个空驶列车表,成分增多到列表后,列表容积会扩展到可接收多少个因素。
即便加多了第三个要素,列表大小会重新初始化为七个成分。每回都会将列表的容积重新安装为本来的2倍.
var intList=new List<int>();
假如列表的容积变了,整个集结将在重新分配到新的内部存款和储蓄器块中,大家得以在开始化时设置它的体量:
List<int> intList=new List<int>(10);
假设列表的个数当先11个,能够设置体量Capacity:
intList.Capacity = 20;
1旦列表的因素已经加多完了,列表会存在多余的体量空间。能够应用TrimExcess方法去除不要的容积:
intList.TrimExcess();
a.集合开始值设定项
动用起先化构造器起始化设定项
int[] arr = { 1, 2, 3 };
var intList = new List<int>(arr) ;
括号中起先化
var intList = new List<int>() { 4, 5 };
b.添法郎素
intList.Add(5);
增加数组
intList.AddRange(new int[] { 3, 5 });
c.插入成分
intList.Insert(3, 4);
d.访问成分
接纳索引获取:
var value = intList[3];
循环遍历:
foreach (var item in intList)
{
var res = item;
}
forEach方法:
class List<T> : IList<T>
{
private T[] items;
public void forEach(Action<T> action)
{
if (action == null) throw new ArgumentNullException("action");
foreach (var item in items)
{
action(item);
}
}
}
下一场大家能够那样调用:
intList.ForEach(m => Console.WriteLine(m));
e.删除成分
按索引删除,极快
intList.RemoveAt(3);
按成分值删除
intList.Remove(4);
f.搜索
在汇集中找寻成分。能够查找索引和因素。
FindIndex通过相称元素值,获得索引:
intList.FindIndex(m => m==4);
FindIndex方法参数Predicate<T>传入相称的表明式,重返相称的成分索引值,Predicate<T>委托代表定义1组条件并规定内定对象是还是不是切合这一个原则的方法
intList.Find(m => m == 4);
intList.FindAll(m => m > 2);
Find重回了同盟原则的元素值,FindAll再次来到了非凡原则的具备因素
g.排序
列表使用Sort方法开展元素排序
intList.Sort();
intList.Sort((m, n) => m);
Sort(Comparison<T>
comparison)方法参数中的委托Comparison含有3个参数,方法将那1个成分举行比较,然后重回相对值,假如回去-壹的,成分需求排前边,重返1的要素须要排前面.
Sort(IComparer<T>
comparer)方法参数中是3个相比接口,接口达成Comparer方法
public enum CompareType
{
FirstName,
LastName,
Country,
Wins
}
public class RacerComparer : IComparer<Racer>
{
private CompareType compareType;
public RacerComparer(CompareType compareType)
{
this.compareType = compareType;
}
public int Compare(Racer x, Racer y)
{
if (x == null && y == null) return 0;
if (x == null) return -1;
if (y == null) return 1;
int result;
switch (compareType)
{
case CompareType.FirstName:
return string.Compare(x.FirstName, y.FirstName);
case CompareType.LastName:
return string.Compare(x.LastName, y.LastName);
case CompareType.Country:
result = string.Compare(x.Country, y.Country);
if (result == 0)
return string.Compare(x.LastName, y.LastName);
else
return result;
case CompareType.Wins:
return x.Wins.CompareTo(y.Wins);
default:
throw new ArgumentException("Invalid Compare Type");
}
}
}
View Code
h.类型调换
Converter委托
public delegate TOutput Converter<in TInput, out TOutput>(TInput input);
ConvertAll可以将一种类型的集合转换为另一种类型的集合。
intList.ConvertAll(m => m.ToString());
文/玄魂
正文
不可变对象
不可变(immutable):
即对象一旦被创设初阶化后,它们的值就无法被退换,之后的每回改动都会发生三个新目的。
var str="mushroomsir";
str.Substring(0, 6)
c#中的string是不可变的,Substring(0,
陆)再次来到的是二个新字符串值,而原字符串在共享域中是不改变的。别的三个StringBuilder是可变的,那也是推荐应用StringBuilder的原故。
var age=18;
当存款和储蓄值18的内部存款和储蓄器分配给age变量时,它的内部存款和储蓄器值也是不能够被涂改的。
age=2;
那会儿会在栈中开辟新值二赋值给age变量,而不能退换1八这几个内部存款和储蓄器里的值,int在c#中也是不可变的。
class Contact
{
public string Name { get; set; }
public string Address { get; set; }
public Contact(string contactName, string contactAddress)
{
Name = contactName;
Address = contactAddress;
}
}
var mutable = new Contact("二毛", "清华");
mutable.Name = "大毛";
mutable.Address = "北大";
大家实例化MutableContact赋值给mutable,随后大家能够修改MutableContact对象内部字段值,它曾经不是开头后的值,可称为可变(mutable)对象。
可变对象在二十八线程并发中国共产党享,是存在有的标题标。四线程下A线程赋值到 Name =
“大毛” 这一步,别的的线程有望读取到的数据正是:
mutable.Name == "大毛";
mutable.Address == "清华";
很明显那样数据完整性就不可能维持,也有称数据撕裂。我们把可变对象更换为不可变对象如下:
public class Contact2
{
public string Name { get; private set; }
public string Address { get; private set; }
private Contact2(string contactName, string contactAddress)
{
Name = contactName;
Address = contactAddress;
}
public static Contact2 CreateContact(string name, string address)
{
return new Contact2(name, address);
}
}
采用时只可以因而Contact贰的构造函数来起首化Name和Address字段。Contact2那时即为不可变对象,因为对象自己是个不可变全体。通过应用不可变对象足以不要操心数据完整性,也能保证数据安全性,不会被其余线程修改。
只读会集
创办集合后,它们是只读的。
3.队列
意味着了3个先进先出的对象集合。当您须要对每一样进行先进先出的拜会时,则接纳队列。当你在列表中加多1项,称为入队,当你从列表中移除1项时,称为出队。
增多队列成分时添加lock,因为二拾10二线程能够同时做客,所以对队列举行锁定访问。
using System;
using System.Collections;
namespace CollectionsApplication
{
class Program
{
static void Main(string[] args)
{
Queue q = new Queue();
q.Enqueue('A');
q.Enqueue('M');
q.Enqueue('G');
q.Enqueue('W');
Console.WriteLine("Current queue: ");
foreach (char c in q)
Console.Write(c + " ");
Console.WriteLine();
q.Enqueue('V');
q.Enqueue('H');
Console.WriteLine("Current queue: ");
foreach (char c in q)
Console.Write(c + " ");
Console.WriteLine();
Console.WriteLine("Removing some values ");
char ch = (char)q.Dequeue();
Console.WriteLine("The removed value: {0}", ch);
ch = (char)q.Dequeue();
Console.WriteLine("The removed value: {0}", ch);
Console.ReadKey();
}
}
}
View Code
当上面的代码被编写翻译和进行时,它会时有爆发下列结果:
Current queue:
A M G W
Current queue:
A M G W V H
Removing values
The removed value: A
The removed value: M
class Program
{
static void Main()
{
var dm = new DocumentManager();
ProcessDocuments.Start(dm);
// Create documents and add them to the DocumentManager
for (int i = 0; i < 1000; i++)
{
Document doc = new Document("Doc " + i.ToString(), "content");
dm.AddDocument(doc);
Console.WriteLine("Added document {0}", doc.Title);
Thread.Sleep(new Random().Next(20));
}
}
}
Program
public class ProcessDocuments
{
public static void Start(DocumentManager dm)
{
Task.Factory.StartNew(new ProcessDocuments(dm).Run);
}
protected ProcessDocuments(DocumentManager dm)
{
if (dm == null)
throw new ArgumentNullException("dm");
documentManager = dm;
}
private DocumentManager documentManager;
protected void Run()
{
while (true)
{
if (documentManager.IsDocumentAvailable)
{
Document doc = documentManager.GetDocument();
Console.WriteLine("Processing document {0}", doc.Title);
}
Thread.Sleep(new Random().Next(20));
}
}
}
ProcessDocuments
public class DocumentManager
{
private readonly Queue<Document> documentQueue = new Queue<Document>();
public void AddDocument(Document doc)
{
lock (this)
{
documentQueue.Enqueue(doc);
}
}
public Document GetDocument()
{
Document doc = null;
lock (this)
{
doc = documentQueue.Dequeue();
}
return doc;
}
public bool IsDocumentAvailable
{
get
{
return documentQueue.Count > 0;
}
}
}
DocumentManager
public class Document
{
public string Title { get; private set; }
public string Content { get; private set; }
public Document(string title, string content)
{
this.Title = title;
this.Content = content;
}
}
Document
4.栈
表示了贰个后进先出的目的集结。当你供给对每一类进行后进先出的拜访时,则应用仓库。当你在列表中增多1项,称为推入要素,当您从列表中移除壹项时,称为弹出元素。
class Program
{
static void Main()
{
var alphabet = new Stack<char>();
alphabet.Push('A');
alphabet.Push('B');
alphabet.Push('C');
Console.Write("First iteration: ");
foreach (char item in alphabet)
{
Console.Write(item);
}
Console.WriteLine();
Console.Write("Second iteration: ");
while (alphabet.Count > 0)
{
Console.Write(alphabet.Pop());
}
Console.WriteLine();
}
}
Program
First iteration: CBA
Second iteration: CBA
5.链表
LinkedList<T>是三个双向链表,其成分指向它前边和后边的因素,那样经过移动下多个因素就足以正向遍历整个链表。通过运动到前一个要素得以反向遍历这几个链表
链表的独到之处是,倘诺将成分插入列表的中间地点,使用链表会异常的快,在插入贰个成分时,只须要修改上三个因素的Next引用和下三个因素的Previous引用,使她们援引所插入的要素。
public class Document
{
public string Title { get; private set; }
public string Content { get; private set; }
public byte Priority { get; private set; }
public Document(string title, string content, byte priority)
{
this.Title = title;
this.Content = content;
this.Priority = priority;
}
}
Document
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
class Program
{
static void Main()
{
PriorityDocumentManager pdm = new PriorityDocumentManager();
pdm.AddDocument(new Document("one", "Sample", 8));
pdm.AddDocument(new Document("two", "Sample", 3));
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", 1));
pdm.AddDocument(new Document("eight", "Sample", 1));
pdm.DisplayAllNodes();
}
}
Program
6.有系列表
SortedList基于键对集结进行排序.
class Program
{
static void Main()
{
var books = new SortedList<string, string>();
books.Add("sty", "");
books.Add("abc", "");
books.Add("123", "");
foreach (var item in books.Keys)
{
Console.WriteLine(item);
}
}
}
123
abc
sty
7.字典
字典:用于在名称/值对中贮存新闻,字典的称号即键不能够重复.
HashTable和Dictionary
一.HashTable大数据量插入数据时须求开支比Dictionary大的多的时日。
二.for措施遍历HashTable和Dictionary速度最快。
三.在foreach格局遍历时Dictionary遍历速度越来越快。
4.HashTable在取值时需求张开类型转变,Dictionary不用做类型转变。
在单线程的时候利用Dictionary更加好有的,10二线程的时候使用HashTable越来越好。
一仍其旧字典SortedList和SortedDictionary
SortedDictionary 泛型类是研究运算复杂度为 O(log n) 的2叉搜索树,其中 n 是字典中的元素数。就这点来讲,它与 SortedList 泛型类相似。那三个类具备相似的对象模型,并且都持有
O(log n)
的索求运算复杂度。这些类的分别在于内部存款和储蓄器的利用以及插入和移除成分的速度:
-
SortedList 使用的内部存储器比 SortedDictionary 少。
-
SortedDictionary 可对未排序的数目实行越来越快的插入和移除操作:它的时刻复杂度为
O(log n),而SortedList 为 O(n)。 -
1经使用排序数据二遍性填充列表,则 SortedList 比 高级编制程序9,之不可变数据类型。SortedDictionary 快。
8.集
包蕴不重复元素的聚众,叫“集”。.NET包罗二个集。HashSet<T>和SortedSet<T>,它们继续ISet;SortedSet是三个静止集.
ISet提供了Add方法,若是HashSet中留存那些成分,再度行使Add方法不会抛出卓殊,再次来到bool值是或不是丰富
var companyTeams = new HashSet<string>() { "Ferrari", "McLaren", "Mercedes" };
var traditionalTeams = new HashSet<string>() { "Ferrari", "McLaren" };
var privateTeams = new HashSet<string>() { "Red Bull", "Lotus", "Toro Rosso", "Force India", "Sauber" };
if (privateTeams.Add("Williams"))
Console.WriteLine("Williams added");
if (!companyTeams.Add("McLaren"))
Console.WriteLine("McLaren was already in this set");
IsSubsetOf方法判断了traditionalTeams集合是否companyTeams的子集
IsSupersetOf方法判断了companyTeams集合是否traditionalTeams的超集(包含它拥有的所有元素,并且多余它的元素)
var companyTeams = new HashSet<string>() { "Ferrari", "McLaren", "Mercedes" };
var traditionalTeams = new HashSet<string>() { "Ferrari", "McLaren" };
var privateTeams = new HashSet<string>() { "Red Bull", "Lotus", "Toro Rosso", "Force India", "Sauber" };
if (traditionalTeams.IsSubsetOf(companyTeams))
{
Console.WriteLine("traditionalTeams is subset of companyTeams");
}
if (companyTeams.IsSupersetOf(traditionalTeams))
{
Console.WriteLine("companyTeams is a superset of traditionalTeams");
}
SortedSet的UnionWith方法可以修改这个集合,并且包含传入的集合
var allTeams = new SortedSet<string>(companyTeams);
allTeams.UnionWith(privateTeams);
allTeams.UnionWith(traditionalTeams);
玖.可验证的聚众
若果须求记录群集何时增多和删除成分的新闻,可以选拔ObservableCollection<T>,这几个作者是为WPF定制的。
ObservableCollection<T>类用于创制自定义集结,在其间接选举择List<T>类,重写虚方法RemoveItem和SetItem()方法触发CollectionChanged事件。
class Program
{
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);
}
}
Console.WriteLine();
}
}
View Code
Data_CollectionChanged方法接收了NotifyCollectionChanged伊芙ntArgs,包罗了汇聚的变通音信,Action属性给出了是还是不是丰盛或删除壹项的音讯,对于删除的项,会设置OldItems属性,列出删除的项
对于增进的项,会安装NewItems属性,列出丰硕的项。
action: Add
starting index for new item(s): 0
new item(s):
One
action: Add
starting index for new item(s): 1
new item(s):
Two
action: Add
starting index for new item(s): 1
new item(s):
Three
action: Remove
starting index for old item(s): 0
old item(s):
One
10.位数组
前言
从
.NET4.0初始,到前几日的4.5,我们得以感受获得微软在互动、八线程、异步编程上带给开拓职员的喜怒哀乐。在多线程开辟中,无可制止的涉及八个线程共享对象难点,Immutable Object(不可变对象)在保证线程安全地点的最首要被呈现出来。简单不可变对象,比方单例,我们得以很自在的成立并爱抚,一些复杂对象,对象引用或许聚众对象的场景
,创设和维护不可变对象变得紧Baba了广大。微软在那上头也做了广大使劲,近期看最令笔者欢腾的正是Immutable Collections了。假如你驾驭函数式编制程序,那么对此料定不会目生。
本来除了线程安全,不可变集合还有任何的选取场景,本文也会持有涉及。
作者近期研读了几篇MSDN Blog中有关Immutable
Collections的英文物博物文(在文后会付出链接)。笔者来看的博客中的代码和本身下载的本子有个别出入,作者依据本身的精通重新整理,改编成此文,水平有限,接待探究。
接轨关系
先看一下文书档案中的注释。
- Strings are constant; their values can not be changed after they are
created.
Stringbuffers support mutable strings.Because String objects are
immutable they can be shared. Forexample: - String
字符串是常量,其值在实例创造后就不可能被修改,但字符串缓冲区支持可变的字符串,因为缓冲区里面包车型大巴不足变字符串对象们方可被共。
String承袭体系
通过注释跟承继关系,我们领悟String被final修饰,而且只要成立就不能退换,并且落成了CharSequence,Comparable以及Serializable接口。
自定义不可变群集
我们去枚举可变集合时,出于线程安全的设想我们往往必要进行加锁管理,幸免该集结在其他线程被更换,而使用不可变集结则能幸免那么些难点。大家经常使用的数据结构都以利用可变方式来完结的,这怎么落到实处多个不可变数据结构呢!以栈来演示,具体代码如下:
public interface IStack<T> : IEnumerable<T>
{
IStack<T> Push(T value);
IStack<T> Pop();
T Peek();
bool IsEmpty { get; }
}
public sealed class Stack<T> : IStack<T>
{
private sealed class EmptyStack : IStack<T>
{
public bool IsEmpty { get { return true; } }
public T Peek() { throw new Exception("Empty stack"); }
public IStack<T> Push(T value) { return new Stack<T>(value, this); }
public IStack<T> Pop() { throw new Exception("Empty stack"); }
public IEnumerator<T> GetEnumerator() { yield break; }
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
private static readonly EmptyStack empty = new EmptyStack();
public static IStack<T> Empty { get { return empty; } }
private readonly T head;
private readonly IStack<T> tail;
private Stack(T head, IStack<T> tail)
{
this.head = head;
this.tail = tail;
}
public bool IsEmpty { get { return false; } }
public T Peek() { return head; }
public IStack<T> Pop() { return tail; }
public IStack<T> Push(T value) { return new Stack<T>(value, this); }
public IEnumerator<T> GetEnumerator()
{
for (IStack<T> stack = this; !stack.IsEmpty; stack = stack.Pop())
yield return stack.Peek();
}
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
View Code
- 入栈时会实例化多个新栈对象
- 将新值通过构造函数字传送入,并存放在新对象Head地方,旧栈对象放在在Tail地方引用
- 出栈时回到当前栈对象的Tail引用的栈对象
采用办法如下:
IStack<int> s1 = Stack<int>.Empty;
IStack<int> s2 = s1.Push(10);
IStack<int> s3 = s2.Push(20);
IStack<int> s4 = s3.Push(30);
IStack<int> v3 = s4.Pop();
foreach (var item in s4)
{
//dosomething
}
历次Push都以一个新目标,旧目标不可修改,那样在枚举集结就没有须求忧郁其余线程修改了。
BitArray类的章程和特性
下表列出了一部分BitArray类的常用属性:
属性 | 描述 |
---|---|
Count | 获取包含在BitArray元素的数量 |
IsReadOnly | 获取一个值,指示BitArray是否是只读 |
Item | 获取或设置在所述BitArray的特定位置的比特的值 |
Length | 获取或设置在BitArray元素的数量 |
下表列出了部分BitArray类的常用方法:
S.N | 方法名称及用途 |
---|---|
1 | public BitArray And( BitArray value ); 执行对指定BitArray的相应元素在当前BitArray元素的按位与运算 |
2 | public bool Get( int index ); 获取在所述BitArray的特定位置的比特的值 |
3 | public BitArray Not(); 反转当前BitArray所有的位值,使设置为true的元素被更改为false,并设置为false元素更改为true |
4 | public BitArray Or( BitArray value ); 在执行对指定BitArray的相应元素在当前BitArray的元素的按位或操作 |
5 | public void Set( int index, bool value ); 设置在所述BitArray为指定值的特定位置的比特值 |
6 | public void SetAll( bool value ); 设置在BitArray所有位设置为指定值 |
7 | public BitArray Xor( BitArray value ); 执行关于对在指定BitArray的相应元素中的当前BitArray的元素按位异或运算 |
当须求仓库储存位,但不精通事先比特数就使用它。您可以通过使用二个平头索引,它从零初始访问BitArray集结中的项。
using System;
using System.Collections;
namespace CollectionsApplication
{
class Program
{
static void Main(string[] args)
{
//creating two bit arrays of size 8
BitArray ba1 = new BitArray(8);
BitArray ba2 = new BitArray(8);
byte[] a = { 60 };
byte[] b = { 13 };
//storing the values 60, and 13 into the bit arrays
ba1 = new BitArray(a);
ba2 = new BitArray(b);
//content of ba1
Console.WriteLine("Bit array ba1: 60");
for (int i = 0; i < ba1.Count; i++)
{
Console.Write("{0, -6} ", ba1[i]);
}
Console.WriteLine();
//content of ba2
Console.WriteLine("Bit array ba2: 13");
for (int i = 0; i < ba2.Count; i++)
{
Console.Write("{0, -6} ", ba2[i]);
}
Console.WriteLine();
BitArray ba3 = new BitArray(8);
ba3 = ba1.And(ba2);
//content of ba3
Console.WriteLine("Bit array ba3 after AND operation: 12");
for (int i = 0; i < ba3.Count; i++)
{
Console.Write("{0, -6} ", ba3[i]);
}
Console.WriteLine();
ba3 = ba1.Or(ba2);
//content of ba3
Console.WriteLine("Bit array ba3 after OR operation: 61");
for (int i = 0; i < ba3.Count; i++)
{
Console.Write("{0, -6} ", ba3[i]);
}
Console.WriteLine();
Console.ReadKey();
}
}
}
View Code
让大家编写翻译和平运动转方面包车型客车先后,那将发生以下结果:
Bit array ba1: 60
False False True True True True False False
Bit array ba2: 13
True False True True False False False False
Bit array ba3 after AND operation: 12
False False True True False False False False
Bit array ba3 after OR operation: 61
True False True True False False False False
一.一 Immutability OBJECT轻松分类
final:
- 修饰类:当用final修饰贰个类时,申明这些类无法被持续。也等于说,String类是不能够被接二连三的,
- 修饰方法:把办法锁定,避防任何承袭类修改它的意思。
- 修饰变量:修饰基本数据类型变量,则其数值1旦在伊始化之后便无法改造;要是是援引类型的变量,则在对其初步化之后便不能够再让其针对性另3个对象。
String类通过final修饰,不可被延续,同时String底层的字符数组也是被final修饰的,char属于主题数据类型,1旦被赋值之后也是不能被修改的,所以String是不可变的。
Net提供的不可变会集
不可变队列,不可变列表等数据结构假设都友好落成职业量确实有点大。万幸的是Net在4.五版本现已提供了不可变会集的根基类库。
使用Nuget安装:
Install-Package Microsoft.Bcl.Immutable
利用如下,和上边大家自定义的大约大同小异:
ImmutableStack<int> a1 = ImmutableStack<int>.Empty;
ImmutableStack<int> a2 = a1.Push(10);
ImmutableStack<int> a3 = a2.Push(20);
ImmutableStack<int> a4 = a3.Push(30);
ImmutableStack<int> iv3 = a4.Pop();
接纳Net不可变列表集合有好几要小心的是,当我们Push值时要再度赋值给原变量才准确,因为push后会生成一个新目的,原a三只是旧值:
ImmutableStack<int> a1 = ImmutableStack<int>.Empty;
a1.Push(10); //不正确,a1仍是空值值,push会生成新的栈。
a1 = a1.Push(10); //需要将新栈重新赋值给a1
NET提供的常用数据结构
- ImmutableStack
- ImmutableQueue
- ImmutableList
- ImmutableHashSet
- ImmutableSortedSet
- ImmutableDictionary<K, V>
- ImmutableSortedDictionary<K, V>
不可变集结和可变集结在算法复杂度上的不一致:
BitVector32
提供了3个大约结构,该组织以三十四人内部存款和储蓄器存款和储蓄布尔和小数值
对于个中接纳的布尔值和小整数,BitVector3贰 比 BitArray 更有效。 BitArray 能够按要求格外地扩张,但它有内部存款和储蓄器和特性方面包车型客车系统开荒,那是类实例所要求的。 比较之下,BitVector3贰 只行使 叁拾4个人。
BitVector32 结构能够设置成包括小平头的若干节或包括布尔值的多少位标记,但无法同时富含两者。BitVector32.Section 是 BitVector32 中的窗口,且由微小数量的连日位结合,接二连三位能够包蕴 CreateSection 中钦赐的最大值。 比方,带有最大值 一 的节只由三个位构成,而富含最大值 5的节由多少个位结合。 能够创设带有最大值 一 的 BitVector32.Section 作为布尔值,从而令你能够在同1 BitVector3二 中贮存整数和布尔值。
BitVector3二 既可以安装为节,也能够设置为位标识,分别有成员能够动用于那二种情景。 比方,BitVector32.Item 属性是作为节设置的 BitVector3二 的索引器,而 BitVector32.Item 属性是作为位标识设置的BitVector3二 的索引器。 CreateMask 成立一种类屏蔽,这个屏蔽可用以访问作为位标记设置的 BitVector3二 中的单个位。
在作为节设置的 BitVector32 上使用屏蔽或然会招致意外的结果。
using System;
using System.Collections.Specialized;
public class SamplesBitVector32 {
public static void Main() {
// Creates and initializes a BitVector32 with all bit flags set to FALSE.
BitVector32 myBV = new BitVector32( 0 );
// Creates masks to isolate each of the first five bit flags.
int myBit1 = BitVector32.CreateMask();
int myBit2 = BitVector32.CreateMask( myBit1 );
int myBit3 = BitVector32.CreateMask( myBit2 );
int myBit4 = BitVector32.CreateMask( myBit3 );
int myBit5 = BitVector32.CreateMask( myBit4 );
// Sets the alternating bits to TRUE.
Console.WriteLine( "Setting alternating bits to TRUE:" );
Console.WriteLine( " Initial: {0}", myBV.ToString() );
myBV[myBit1] = true;
Console.WriteLine( " myBit1 = TRUE: {0}", myBV.ToString() );
myBV[myBit3] = true;
Console.WriteLine( " myBit3 = TRUE: {0}", myBV.ToString() );
myBV[myBit5] = true;
Console.WriteLine( " myBit5 = TRUE: {0}", myBV.ToString() );
}
}
/*
This code produces the following output.
Setting alternating bits to TRUE:
Initial: BitVector32{00000000000000000000000000000000}
myBit1 = TRUE: BitVector32{00000000000000000000000000000001}
myBit3 = TRUE: BitVector32{00000000000000000000000000000101}
myBit5 = TRUE: BitVector32{00000000000000000000000000010101}
*/
BitVector用作节集合
using System;
using System.Collections.Specialized;
public class SamplesBitVector32 {
public static void Main() {
// Creates and initializes a BitVector32.
BitVector32 myBV = new BitVector32( 0 );
// Creates four sections in the BitVector32 with maximum values 6, 3, 1, and 15.
// mySect3, which uses exactly one bit, can also be used as a bit flag.
BitVector32.Section mySect1 = BitVector32.CreateSection( 6 );
BitVector32.Section mySect2 = BitVector32.CreateSection( 3, mySect1 );
BitVector32.Section mySect3 = BitVector32.CreateSection( 1, mySect2 );
BitVector32.Section mySect4 = BitVector32.CreateSection( 15, mySect3 );
// Displays the values of the sections.
Console.WriteLine( "Initial values:" );
Console.WriteLine( "\tmySect1: {0}", myBV[mySect1] );
Console.WriteLine( "\tmySect2: {0}", myBV[mySect2] );
Console.WriteLine( "\tmySect3: {0}", myBV[mySect3] );
Console.WriteLine( "\tmySect4: {0}", myBV[mySect4] );
// Sets each section to a new value and displays the value of the BitVector32 at each step.
Console.WriteLine( "Changing the values of each section:" );
Console.WriteLine( "\tInitial: \t{0}", myBV.ToString() );
myBV[mySect1] = 5;
Console.WriteLine( "\tmySect1 = 5:\t{0}", myBV.ToString() );
myBV[mySect2] = 3;
Console.WriteLine( "\tmySect2 = 3:\t{0}", myBV.ToString() );
myBV[mySect3] = 1;
Console.WriteLine( "\tmySect3 = 1:\t{0}", myBV.ToString() );
myBV[mySect4] = 9;
Console.WriteLine( "\tmySect4 = 9:\t{0}", myBV.ToString() );
// Displays the values of the sections.
Console.WriteLine( "New values:" );
Console.WriteLine( "\tmySect1: {0}", myBV[mySect1] );
Console.WriteLine( "\tmySect2: {0}", myBV[mySect2] );
Console.WriteLine( "\tmySect3: {0}", myBV[mySect3] );
Console.WriteLine( "\tmySect4: {0}", myBV[mySect4] );
}
}
View Code
/*
This code produces the following output.
Initial values:
mySect1: 0
mySect2: 0
mySect3: 0
mySect4: 0
Changing the values of each section:
Initial: BitVector32{00000000000000000000000000000000}
mySect1 = 5: BitVector32{00000000000000000000000000000101}
mySect2 = 3: BitVector32{00000000000000000000000000011101}
mySect3 = 1: BitVector32{00000000000000000000000000111101}
mySect4 = 9: BitVector32{00000000000000000000001001111101}
New values:
mySect1: 5
mySect2: 3
mySect3: 1
mySect4: 9
*/
View Code
1一.不变的聚集
Net提供的不可变集合
ImmutableStack<int> a1 = ImmutableStack<int>.Empty;
ImmutableStack<int> a2 = a1.Push(10);
ImmutableStack<int> a3 = a2.Push(20);
ImmutableStack<int> a4 = a3.Push(30);
ImmutableStack<int> iv3 = a4.Pop();
运用Net不可变列表集结有一些要小心的是,当大家Push值时要重新赋值给原变量才准确,因为push后会生成二个新目的,原a3只是旧值:
ImmutableStack<int> a1 = ImmutableStack<int>.Empty;
a1.Push(10); //不正确,a1仍是空值值,push会生成新的栈。
a1 = a1.Push(10); //需要将新栈重新赋值给a1
NET提供的常用数据结构
1.ImmutableStack
2.ImmutableQueue
3.ImmutableList
4.ImmutableHashSet
5.ImmutableSortedSet
6.ImmutableDictionary<K, V>
7.ImmutableSortedDictionary<K, V>
不可变优点
1.集聚共享安全,从不被改变
2.造访集结时,不要求锁集结(线程安全)
3.修改集合不忧郁旧集结被改造
4.书写更加精简,函数式风格。 var list =
ImmutableList.Empty.Add(拾).Add(20).Add(30);
伍.有限支撑数据完整性,安全性
不可变对象缺点
不可变本人的独到之处就是缺点,当每一次对象/群集操作都会回到个新值。而旧值仍旧会保留壹段时间,这会使内具备极大费用,也会给GC形成回收肩负,质量也比可变集结差的多。
1二.并发集结
线程安全的聚合可防备两个线程以互相冲突的办法访问集合
.NET 的System.Collections.Concurrent提供了多少个平平安安的类和效用:
类 | 说明 |
---|---|
![]() |
BlockingCollection<T> |
![]() |
ConcurrentBag<T> |
![]() |
ConcurrentDictionary<TKey, TValue> |
![]() |
ConcurrentQueue<T> |
![]() |
ConcurrentStack<T> |
![]() |
OrderablePartitioner<TSource> |
![]() |
Partitioner |
![]() |
Partitioner<TSource> |
一)创设管道
将这么些并发群集类用于管道,一个任务向1个集合类写入一些剧情,同时另二个职务从该集合中读取内容
演示中多少个职分形成一个管道.
先是个管道,
第二等级的天职读取文件名,增添到行列,这么些任务运转同时,
第三阶段的职分已经初叶从队列中读取文件名并加载它们的次第,结果被写入另3个队列。
第叁等级同时起步,读取并管理第一个类别的从头到尾的经过,结果被写入贰个字典。
第一品级落成,并且内容已被最后管理,字典获得完整结果时,下一阶段才开首。
第五等级从字典中读取内容,调换数据,然后写入队列中
第四阶段在项中增加颜色音讯,然后把它们拉长到另3个行列中,最后二个品级展现音讯。
第伍到第四品级也能够并发运营.
class Program
{
static void Main(string[] args)
{
StartPipeline();
Console.ReadLine();
}
private static async void StartPipeline()
{
var fileNames = new BlockingCollection<string>();
var lines = new BlockingCollection<string>();
var words = new ConcurrentDictionary<string, int>();
var items = new BlockingCollection<Info>();
var coloredItems = new BlockingCollection<Info>();
Task t1 = PipelineStages.ReadFilenamesAsync(@"../../..", fileNames);
ConsoleHelper.WriteLine("started stage 1");
Task t2 = PipelineStages.LoadContentAsync(fileNames, lines);
ConsoleHelper.WriteLine("started stage 2");
Task t3 = PipelineStages.ProcessContentAsync(lines, words);
await Task.WhenAll(t1, t2, t3);
ConsoleHelper.WriteLine("stages 1, 2, 3 completed");
Task t4 = PipelineStages.TransferContentAsync(words, items);
Task t5 = PipelineStages.AddColorAsync(items, coloredItems);
Task t6 = PipelineStages.ShowContentAsync(coloredItems);
ConsoleHelper.WriteLine("stages 4, 5, 6 started");
await Task.WhenAll(t4, t5, t6);
ConsoleHelper.WriteLine("all stages finished");
}
}
Program
public class ConsoleHelper
{
private static object syncOutput = new object();
public static void WriteLine(string message)
{
lock (syncOutput)
{
Console.WriteLine(message);
}
}
public static void WriteLine(string message, string color)
{
lock (syncOutput)
{
Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), color);
Console.WriteLine(message);
Console.ResetColor();
}
}
}
ConsoleHelper
public static class PipelineStages
{
public static Task ReadFilenamesAsync(string path, BlockingCollection<string> output)
{
return Task.Run(() =>
{
foreach (string filename in Directory.EnumerateFiles(path, "*.cs", SearchOption.AllDirectories))
{
output.Add(filename);
ConsoleHelper.WriteLine(string.Format("stage 1: added {0}", filename));
}
output.CompleteAdding();
});
}
public static async Task LoadContentAsync(BlockingCollection<string> input, BlockingCollection<string> output)
{
foreach (var filename in input.GetConsumingEnumerable())
{
using (FileStream stream = File.OpenRead(filename))
{
var reader = new StreamReader(stream);
string line = null;
while ((line = await reader.ReadLineAsync()) != null)
{
output.Add(line);
ConsoleHelper.WriteLine(string.Format("stage 2: added {0}", line));
}
}
}
output.CompleteAdding();
}
public static Task ProcessContentAsync(BlockingCollection<string> input, ConcurrentDictionary<string, int> output)
{
return Task.Run(() =>
{
foreach (var line in input.GetConsumingEnumerable())
{
string[] words = line.Split(' ', ';', '\t', '{', '}', '(', ')', ':', ',', '"');
foreach (var word in words.Where(w => !string.IsNullOrEmpty(w)))
{
output.AddOrIncrementValue(word);
ConsoleHelper.WriteLine(string.Format("stage 3: added {0}", word));
}
}
});
}
public static Task TransferContentAsync(ConcurrentDictionary<string, int> input, BlockingCollection<Info> output)
{
return Task.Run(() =>
{
foreach (var word in input.Keys)
{
int value;
if (input.TryGetValue(word, out value))
{
var info = new Info { Word = word, Count = value };
output.Add(info);
ConsoleHelper.WriteLine(string.Format("stage 4: added {0}", info));
}
}
output.CompleteAdding();
});
}
public static Task AddColorAsync(BlockingCollection<Info> input, BlockingCollection<Info> output)
{
return Task.Run(() =>
{
foreach (var item in input.GetConsumingEnumerable())
{
if (item.Count > 40)
{
item.Color = "Red";
}
else if (item.Count > 20)
{
item.Color = "Yellow";
}
else
{
item.Color = "Green";
}
output.Add(item);
ConsoleHelper.WriteLine(string.Format("stage 5: added color {1} to {0}", item, item.Color));
}
output.CompleteAdding();
});
}
public static Task ShowContentAsync(BlockingCollection<Info> input)
{
return Task.Run(() =>
{
foreach (var item in input.GetConsumingEnumerable())
{
ConsoleHelper.WriteLine(string.Format("stage 6: {0}", item), item.Color);
}
});
}
}
PipelineStages
2)使用BlockingCollection
第3等级的ReadFilenamesAsync方法,实现了迭代目录文件名。在达成文件名加多后调用output.CompleteAdding();用以文告全数读取器不再等待会集中其余额外的项.倘诺未有调用的话,循环中读取器会增多等待更加多的项.
public static Task ReadFilenamesAsync(string path, BlockingCollection<string> output)
{
return Task.Run(() =>
{
foreach (string filename in Directory.EnumerateFiles(path, "*.cs", SearchOption.AllDirectories))
{
output.Add(filename);
ConsoleHelper.WriteLine(string.Format("stage 1: added {0}", filename));
}
output.CompleteAdding();
});
}
ReadFilenamesAsync
下一阶段读取文件并将器内容增添到另贰个凑合中,由LoadContentAsync方法成功,该方式应用了输入会集传递的文件名,张开文件,把公文中的全体行增添到输出的聚众中。在循环中用输入阻塞群集调用GetConsumingEnumerable()方法,以迭代各样,不采纳也是足以的,但是值会迭代当前情形的集结。不会迭代之后增多的项。
假若在填写集结的同时,使用读取器读取会集,则须要利用GetConsumingEnumerable()方法赢得阻塞群集的枚举器,而不是直接迭代集结
public static async Task LoadContentAsync(BlockingCollection<string> input, BlockingCollection<string> output)
{
foreach (var filename in input.GetConsumingEnumerable())
{
using (FileStream stream = File.OpenRead(filename))
{
var reader = new StreamReader(stream);
string line = null;
while ((line = await reader.ReadLineAsync()) != null)
{
output.Add(line);
ConsoleHelper.WriteLine(string.Format("stage 2: added {0}", line));
}
}
}
output.CompleteAdding();
}
LoadContentAsync
3)使用ConcurrentDictionary
public static Task ProcessContentAsync(BlockingCollection<string> input, ConcurrentDictionary<string, int> output)
{
return Task.Run(() =>
{
foreach (var line in input.GetConsumingEnumerable())
{
string[] words = line.Split(' ', ';', '\t', '{', '}', '(', ')', ':', ',', '"');
foreach (var word in words.Where(w => !string.IsNullOrEmpty(w)))
{
output.AddOrIncrementValue(word);
ConsoleHelper.WriteLine(string.Format("stage 3: added {0}", word));
}
}
});
}
ProcessContentAsync
public static class ConcurrentDictionaryExtension
{
public static void AddOrIncrementValue(this ConcurrentDictionary<string, int> dict, string key)
{
bool success = false;
while (!success)
{
int value;
if (dict.TryGetValue(key, out value))
{
if (dict.TryUpdate(key, value + 1, value))
{
success = true;
}
}
else
{
if (dict.TryAdd(key, 1))
{
success = true;
}
}
}
}
}
ConcurrentDictionaryExtension
在变成第1个等级后,第伍到6等第也得以相互运维,TransferContentAsync从字典中获取数据,举行类型转变,输出到BlockingCollection<string>中
public static Task ProcessContentAsync(BlockingCollection<string> input, ConcurrentDictionary<string, int> output)
{
return Task.Run(() =>
{
foreach (var line in input.GetConsumingEnumerable())
{
string[] words = line.Split(' ', ';', '\t', '{', '}', '(', ')', ':', ',', '"');
foreach (var word in words.Where(w => !string.IsNullOrEmpty(w)))
{
output.AddOrIncrementValue(word);
ConsoleHelper.WriteLine(string.Format("stage 3: added {0}", word));
}
}
});
}
public static Task TransferContentAsync(ConcurrentDictionary<string, int> input, BlockingCollection<Info> output)
{
return Task.Run(() =>
{
foreach (var word in input.Keys)
{
int value;
if (input.TryGetValue(word, out value))
{
var info = new Info { Word = word, Count = value };
output.Add(info);
ConsoleHelper.WriteLine(string.Format("stage 4: added {0}", info));
}
}
output.CompleteAdding();
});
}
public static Task AddColorAsync(BlockingCollection<Info> input, BlockingCollection<Info> output)
{
return Task.Run(() =>
{
foreach (var item in input.GetConsumingEnumerable())
{
if (item.Count > 40)
{
item.Color = "Red";
}
else if (item.Count > 20)
{
item.Color = "Yellow";
}
else
{
item.Color = "Green";
}
output.Add(item);
ConsoleHelper.WriteLine(string.Format("stage 5: added color {1} to {0}", item, item.Color));
}
output.CompleteAdding();
});
}
public static Task ShowContentAsync(BlockingCollection<Info> input)
{
return Task.Run(() =>
{
foreach (var item in input.GetConsumingEnumerable())
{
ConsoleHelper.WriteLine(string.Format("stage 6: {0}", item), item.Color);
}
});
}
View Code
13.性能
会面的章程日常有总体性提示,给出大写O记录操作时间。
O(一)表示无论是集结中有个别许数量项,这一个操作须求的时间都不变。
O(n)代表对此集结试行三个操作供给的轩然大波在最坏情状时是N.
O(log n)表示操作须要的小运随集合相月素的加码而增添
真的的不可变对象
那类对象只能在编写翻译时赋值,在C#中const类型的变量属于那些连串。
CharSequence
CharSequence翻译过来正是字符串,String大家日常也是叫作字符串,不过前者是二个接口,上面看一下接口里面包车型地铁艺术:
int length();
char charAt(int index);
CharSequence subSequence(int start, int end);
public String toString();
}
措施很少,并未观望大家分布的String的办法,那个类应该只是三个通用的接口,那么翻1翻它的得以达成类
CharSequence实现类
CharSequence的得以达成类里面出现了我们很宽泛的StringBuilder跟StringBuffer,先放1放,壹会儿再去商讨他们俩。
不可变优点
- 群集共享安全,从不被改成
- 做客集合时,没有须求锁会集(线程安全)
- 修改集结不忧虑旧群集被改成
- 书写更简洁,函数式风格。 var list =
ImmutableList.Empty.Add(10).Add(20).Add(30); - 有限辅助数据完整性,安全性
非泛型类集结
泛型集结类是在.NET二.0的时候出来的,也正是说在一.0的时候是从未那样便利的事物的。今后多数大家已经不行使那个集结类了,除非在做一些和老代码保持十分的事业的时候。来探视1.0一代的.NET程序猿们都有怎样集结类能够用。
ArraryList后来被List<T>替代。
HashTable 后来被Dictionary<TKey,TValue>替代。
Queue 后来被Queue<T>替代。
SortedList 后来被SortedList<T>替代。
Stack 后来被Stack<T>替代。
叁回初步化对象
运维时初阶化1遍,之后再也不会被退换。标准的单例对象就属于那1类。
成员变量
private final char value[];//final字符数组,一旦赋值,不可更改
private int hash; //缓存String的 hash Code,默认值为 0
private static final ObjectStreamField[] serialPersistentFields =new ObjectStreamField[0];//存储对象的序列化信息
不可变对象缺点
不可变本人的帮助和益处就是缺点,当每一次对象/集结操作都会回去个新值。而旧值依然会保留1段时间,那会使内部存款和储蓄器有十分大开销,也会给GC形成回收负责,质量也比可变集结差的多。
跟string和StringBuild一样,Net提供的不可变群集也大增了批量操作的API,用来幸免大批量创立对象:
ImmutableList<string> immutable = ImmutableList<string>.Empty;
//转换成可批量操作的集合
var immutable2 = immutable.ToBuilder();
immutable2.Add("xx");
immutable2.Add("xxx");
//还原成不可变集合
immutable = immutable2.ToImmutable();
我们来对待下可变集合、不可变Builder集合、不可变集合的本性,增加新对象一千W次:
比较代码如下:
private static void List()
{
var list = new List<object>();
var sp = Stopwatch.StartNew();
for (int i = 0; i < 1000 * 10000; i++)
{
var obj = new object();
list.Add(obj);
}
Console.WriteLine("可变列表集合:"+sp.Elapsed);
}
private static void BuilderImmutableList()
{
var list = ImmutableList<object>.Empty;
var sp = Stopwatch.StartNew();
var blist= list.ToBuilder();
for (int i = 0; i < 1000 * 10000; i++)
{
var obj = new object();
blist.Add(obj);
}
list=blist.ToImmutable();
Console.WriteLine("不可变Builder列表集合:"+sp.Elapsed);
}
private static void ImmutableList()
{
var list = ImmutableList<object>.Empty;
var sp = Stopwatch.StartNew();
for (int i = 0; i < 1000 * 10000; i++)
{
var obj = new object();
list = list.Add(obj);
}
Console.WriteLine("不可变列表集合:" + sp.Elapsed);
}
View Code
其它三个败笔比较有趣,也有这厮忽略。
由于string的不可变特性,所以当大家采纳string在保留敏感新闻时,就需求更加注意。
例如密码 var
pwd=”mushroomsir”,此时密码会以公开存款和储蓄在内部存款和储蓄器中,恐怕你稍后会加密置空等,但这都以会变卦新值的。而公开会长期存款和储蓄在共享域内部存款和储蓄器中,任何能得到dump文件的人都得以见到公开,扩展了密码被窃取的高风险。当然那不是三个新主题材料,net贰.0提供的有SecureString来开始展览安全存款和储蓄,使用时开始展览回复及清理。
IntPtr addr = Marshal.SecureStringToBSTR(secureString);
string temp = Marshal.PtrToStringBSTR(addr);
Marshal.ZeroFreeBSTR(addr);
WriteProcessMemory(...)
线程安全的集结类
ConcurrentQueue 线程安全版本的Queue
ConcurrentStack线程安全版本的Stack
ConcurrentBag线程安全的目标集结
ConcurrentDictionary线程安全的Dictionary
BlockingCollection
浅度不改变和深度不改变
以C#为例,对象自己是Static ReadOnly类型,不过那不可能保险该目的内部成员是线程安全的,那类对象具备浅度不变性,假如能担保对象自己、对象内部任何成员要么嵌套成员都富有不改变性则该目标具备深度不改变性。
眼看,具备深度不改变性的目的是美好的线程安全模型。
构造方法
1.二 安装和接纳
澳门葡京备用网址,毫正确会安装的含义,这里是指从Nuget安装提供不可变群集功效的Dll。
运作条件:vs二〇一一,.NET 四.伍
空参数初阶化
public String(){
this.value = "".value;
}
//将数组的值初始化为空串,此时在栈内存中创建了一个引用,在堆内存中创建了一个对象
//示例代码
String str = new String()
str = "hello";
- 一.先成立了八个空的String对象
- 二.随之又在常量池中开创了多少个”hello”,并赋值给首个String
- 叁.将第3个String的引用传递给第贰个String
那种格局实在创制了五个对象
PM> Install-Package Microsoft.Bcl.Immutable -pre
String初始化
public String(String original){
this.value = original.value;
this.hash = original.hash;
}
//代码示例
String str=new String("hello")
创设了二个对象
你正在从 Microsoft 下载 Microsoft.Bcl.Immutable,有关此程序包的许可协议在 上提供。请检查此程序包是或不是有其余依赖项,那个正视项也许含有各自的许可协议。您若选用程序包及正视项,即构成您接受其许可协议。假诺你不收受这一个许可协议,请从您的器具中删去相关组件。
字符数组初阶化
public String(char value[]){
//将传过来的char拷贝至value数组里面
this.value = Arrays.copyOf(value, value.length);
}
已成功安装“Microsoft.Bcl.Immutable 壹.0.8-beta”。
字节数组初阶化
不点名编码
public String(byte bytes[]){
this(bytes, 0, bytes.length);
}
public String(byte bytes[], int offset, int length){
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(bytes, offset, length);
}
static char[] decode(byte[] ba, int off, int len){
String csn = Charset.defaultCharset().name();
try{ //use char set name decode() variant which provide scaching.
return decode(csn, ba, off, len);
} catch(UnsupportedEncodingException x){
warnUnsupportedCharset(csn);
}
try{
//默认使用 ISO-8859-1 编码格式进行编码操作
return decode("ISO-8859-1", ba, off, len); } catch(UnsupportedEncodingException x){
//异常捕获}
内定编码
String(byte bytes[], Charset charset)
String(byte bytes[], String charsetName)
String(byte bytes[], int offset, int length, Charset charset)
String(byte bytes[], int offset, int length, String charsetName)
byte 是网络传输或存款和储蓄的种类化情势,所以在众多传输和累积的经过中必要将
byte[]
数组和String进行互动转化,byte是字节,char是字符,字节流跟字符流之间转化鲜明需求钦赐编码,不然很只怕会现出乱码,
bytes 字节流是利用 charset 举办编码的,想要将他转移成 unicode 的
char[] 数组,而又确定保证不出新乱码,这将在内定其解码格局
已成功将“Microsoft.Bcl.Immutable 一.0.8-beta”加多到。。。
以此Preview版本的安装手拿包含了之类不可变类型:
·
ImmutableStack<T>
·
ImmutableQueue<T>
·
ImmutableList<T>
·
ImmutableHashSet<T>
·
ImmutableSortedSet<T>
·
ImmutableDictionary<K, V>
·
ImmutableSortedDictionary<K,
V>
每类别型都承继自相应的接口,从而保障从此不可变类型的可扩大性。
先以ImmutableList<T>为例,起始我们的不可变会集之旅。
通过”SB”构造
···
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
···
广大时候我们不会如此去组织,因为StringBuilder跟StringBuffer有toString方法,要是不怀恋线程安全,优先选项StringBuilder。
class Program
equals方法
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
- 1.先推断多少个对象的地方是或不是等于
-
- 再剖断是或不是是String类型
-
三.一旦都以String类型,就先相比较长度是不是等于,然后在可比值
{
hashcode方法
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
- 一.假使String的length==0或然hash值为0,则一向重临0
- 2.上述原则不满意,则透过算法s[0]31^(n-1) + s[1]31^(n-2) +
… + s[n-1]计算hash值
大家清楚,hash值诸多时候用来判别多个对象的值是不是等于,所以须要尽恐怕确认保证唯壹性,后面在分析HashMap原理的时候已经关系过,争辨越少查询的功效也就越高。
static void Main(string[] args)
intern方法
public native String intern();
- Returns a canonical representation for the string object. A pool of
strings, initially empty, is maintained privately by the class .
When the intern method is invoked, if the pool already contains a
string equal to this object as determined by the method, then the
string from the pool is returned. Otherwise, this object is added to
the pool and a reference to this object is returned. It follows that
for any two strings { s} and { t}, { s.intern() == t.intern()} is {
true}if and only if {s.equals(t)} is { true}. - 再次来到二个脚下String的二个稳住表示格局。String的常量池,发轫化为空,被当下类爱护。当此方法被调用的时候,如若常量池中富含有跟当前String值相等的常量,那一个常量就会被再次回到。不然,当前string的值就能被出席常量池,然后重回当前String的引用。如若多个String的intern()调用==时回来true,那么equals方法也是true.
翻译完了,其实正是一句话,假如常量池中有眼下String的值,就赶回那一个值,假诺未有就加进去,重临那么些值的引用,看起来极屌的轨范。
{
String对“+”的重载
笔者们知晓,”+”跟”+=”是Java中仅部分八个重载操作符,除外,Java不协助其余的其它重载操作符,上面通过反编写翻译来看一下Java是怎样开始展览重载的:
public static void main(String[] args) {
String str1="wustor";
String str2= str1+ "Android";
}
反编写翻译Main.java,实施命令 javap -c Main,输出结果
反编译Main文件
或许看不懂全数的代码,然则大家来看了StringBuilder,然后看到了wustor跟Android,以及调用了StringBuilder的append方法。既然编写翻译器已经在底层为我们开始展览优化,那么为啥还要提倡我们有StringBuilder呢?
咱俩精心观察一下方面包车型地铁第贰行代码,new
了一个StringBuilder对象,借使有是在二个循环往复之中,大家利用”+”号实行重载的话就能成立七个StringBuilder的对象,而且,即时编写翻译器都帮我们优化了,不过编写翻译器事先是不知道大家StringBuilder的长短的,并不可能事先分配好缓冲区,也会加大内部存款和储蓄器的支付,而且采纳重载的时候依据java的内部存款和储蓄器分配也会创制八个对象,那么为啥要运用StringBuilder呢,大家稍后会分析。
ImmutableList<string> emptyBusket = ImmutableList.Create<string>();
switch
String的Switch原理
- 一.率先调用String的HashCode方法,得到相应的Code
- 2.通过那几个code然后给每一种case唯1的标志
- 三.由此标志来实行相应的操作
本身感觉挺诧异,所以随后查看一下若是是char类型的看看switch是怎么调换的
public static void main(String[] args) {
char ch = 'a';
switch (ch) {
case 'a':
System.out.println("hello");
break;
case 'b':
System.out.println("world");
break;
default:
break;
}
}
Char的Switch语句
大概跟String差不离,就不多解释了,因此能够看出,Java对String的Switch援助实际上也依旧对int类型的支撑。
}
StringBuilder
由于String对象是不可变的,所以在重载的时候会创建多个对象,而StringBuilder对象是可变的,能够平素动用append方法来进展拼接,上边看看StringBuilder的拼凑。
StringBuilder承继关系
public final class StringBuilder extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
// 空的构造方法
public StringBuilder () {
super(16);
}
//给予一个初始化容量
public StringBuffer(int capacity) {
super(capacity);
}
//使用String进行创建
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
@Override
public StringBuilder append(CharSequence s) {
super.append(s);
return this;
}
我们见到StringBuilder都是在调用父类的法子,而且经过持续关系,大家领悟它是AbstractStringBuilder
的子类,那大家就一连查看它的父类,AbstractStringBuilder
实现了Appendable跟CharSequence 接口,所以它亦可跟String相互转变
}
专注上边包车型大巴代码,我们并未有动用构造函数来伊始化ImmutableList<string>集结,而是接纳名称叫ImmutableList的Create方法,该方法重临2个空的不可变会集。使用空集合在一些处境下得以制止内部存款和储蓄器浪费。
Create方法有多少个重载,能够流传初阶化数据和比较器。
下边大家品尝向这几个集结中增添一些数目。
分子变量
char[] value;//字符数组
int count;//字符数量
class Program
构造方法
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
能够见到AbstractStringBuilder唯有八个构造方法,三个为空完成,还有1个为钦定字符数组的容积,如果事先知情String的长短,并且那一个长度小于16,那么就能够节外省部存储器空间。他的数组和String的不一样等,因为成员变量value数组未有被final修饰所以能够修改他的引用变量的值,即能够引用到新的数组对象。所以StringBuilder对象是可变的
{
append方法
append方法
通过图形能够看看,append有繁多种载方法,其实原理都大致,大家拿char例如子
@Override
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);//检测容量
value[count++] = c;
return this;
}
//判断当前字节数组的容量是否满足需求
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
//目前所需容量超出value数组的容量,进行扩容
expandCapacity(minimumCapacity);
}
//开始扩容
void expandCapacity(int minimumCapacity) {
//将现有容量扩充至value数组的2倍多2
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
//如果扩容后的长度比需要的长度还小,则跟需要的长度进行交换
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
//将数组扩容拷贝
value = Arrays.copyOf(value, newCapacity);
}
static void Main(string[] args)
insert方法
insert方法
insert也有众多种载方法,上边同样以char为例
public AbstractStringBuilder insert(int offset, char c) {
//检测是否需要扩容
ensureCapacityInternal(count + 1);
//拷贝数组
System.arraycopy(value, offset, value, offset + 1, count - offset);
//进行赋值
value[offset] = c;
count += 1;
return this;
}
{
StringBuffer
StringBuilder承袭关系
跟StringBuilder差不多,只然则在具备的主意方面加了一个一并锁而已,不再赘言。
ImmutableList<string> emptyBusket = ImmutableList.Create<string>();
equals与==
equals方法:由于String重新了Object的equas方法,所以1旦三个String对象的值同样,那么就能回到true.
==:这么些相比较的是内部存款和储蓄器地址,上面通过大气的代码示例,来讲Bellamy下刚刚分析的源码
创建方式 | 对象个数 | 引用指向 |
---|---|---|
String a="wustor" | 1 | 常量池 |
String b=new String("wustor") | 1 | 堆内存 |
String c=new String() | 1 | 堆内存 |
String d="wust"+"or" | 3 | 常量池 |
String e=a+b | 3 | 堆内存 |
var fruitBasket = emptyBusket.Add(“apple”);
其他常用方法
valueOf() 调换为字符串
trim() 去掉开首和终极的空格
substring() 截取字符串
indexOf() 查找字符大概子串第二遍出现的地点
toCharArray()转变来字符数组
getBytes()获取字节数组
charAt() 截取2个字符
length() 字符串的尺寸
toLowerCase() 调换为题写
}
总结
- String被final修饰,壹旦被创设,不可能改动
- String类的具有办法都不曾改换字符串自己的值,都以回到了三个新的目标。
- 倘诺你必要一个可修改的字符串,应该采用StringBuilder或者StringBuffer。
- 1经你只需要创建三个字符串,你能够运用双引号的措施,假诺您需求在堆中成立1个新的靶子,你能够挑选构造函数的不二秘技。
- 在应用StringBuilder时全力以赴钦点大小那样会打折扣扩大体量的次数,有助于升高功用。
}
我想您已经看到Immutable Collections和传统集合的一个区别 了,Add方法创建了一个新的集合。这里我们也可以使用AddRange方法批量添加数据创建新的实例。
1.3 Builders
神迹,大家兴许更需求对一个集聚数十次改动手艺到达需求。从地方的以身作则我们领悟,每一趟修改都会创建新的集纳,那就意味着要开拓新的内部存款和储蓄器,并且设有数据拷贝。程序自己的进行成效会下滑同时GC压力会附加。
实则同样的难点再String类型上也存在,反复的修改字符串值存在同样的主题素材,.NET中StringBuilder用来化解那一个标题。类似的IMMUTABLE COLLECTIONS也提供了Builder类型。还要大家全数了在迭代的同时修改集合的手艺!
上边大家来探视Builder的为主采取:
static void Main(string[] args)
{
ImmutableList<string> fruitBusket = ImmutableList.Create<string>(“apple”,”orange”,”pear”);
var builder = fruitBusket.ToBuilder();
foreach (var fruit in fruitBusket)
{
if (fruit == “pear”)
{
builder.Remove(fruit);
}
}
builder.Add(“ananas”);
fruitBusket = builder.ToImmutable();
foreach (var f in fruitBusket)
{
Console.WriteLine(f);
}
Console.Read();
}
在下面的代码中,使用ToBuilder方法获取Builder对象,在终极选择To
ToImmutable方法重回IMMUTABLE COLLECTION。这里须求注意的是ToBuilder方法并未拷贝财富给新Builder对象,Builder的有着操作都和聚众共享内部存款和储蓄器。恐怕你要困惑,既然是共享内存,那么Builder修改数据的时候会集怎么能不成形吗?那是因为ImmutableList的个中数据结构是树,只必要在立异集结的时候制造3个新的引用包涵分化节点的引用就能够。内部的贯彻原理,笔者会在下壹篇博文中持续追究。上面的代码既未有改变原来的IMMUTABLE
COLLECTION也未有拷贝整个集结的中间操作。运营结果如下:
1.4 性能(Performance)
immutable collections 在诸多方面,质量优越可变会集。当然品质上的优势和可变如故不可变的关联并比非常小,主因在于immutable collections内部的数据结构。比方上面包车型地铁代码:
private List<T> collection;
public IReadOnlyList<int> SomeProperty
{
get
{
lock (this)
{
return this.collection.ToList();
}
}
}
每一遍访问都会滋生内部存款和储蓄器拷贝的操作。
而是假设选拔immutable collection就足防止止这几个主题材料:
private ImmutableList<T> collection;
public IReadOnlyList<int> SomeProperty
{
get { return this.collection; }
}
里头原理和对品质的熏陶放在下一篇博客研商,上边包车型地铁列表是在算法复杂度层面包车型地铁看待:
Mutable (amortized) |
Mutable (worst case) |
Immutable |
|
Stack.Push |
O(1) |
O(n) |
O(1) |
Queue.Enqueue |
O(1) |
O(n) |
O(1) |
List.Add |
O(1) |
O(n) |
O(log n) |
HashSet.Add |
O(1) |
O(n) |
O(log n) |
SortedSet.Add |
O(log n) |
O(n) |
O(log n) |
Dictionary.Add |
O(1) |
O(n) |
O(log n) |
SortedDictionary.Add |
O(log n) |
O(n log n) |
O(log n) |
在内部存款和储蓄器使用方面,Immutable Collections要比可变类型的集聚要多,空间换时间,那么些世界上向来不理想的事情。
小结
本篇博文只是浅尝则止,从概念上为你介绍了Immutable Collections的大旨概念,简单利用。
自己并从未拿规范的应用场景来比喻,可是你能够从它们的线程安全,品质,内部存款和储蓄器使用等风味上衡量使用。假如有时机,笔者会将本人的实际利用场景分享给您。
接下去,在下①篇博客中,作者会切磋Immutable Collections的中间原理。只怕你将来不会使用.NET4.五,不过其内部原理却是和平台非亲非故的。
参考资料: