菜菜的涨工资申请还在待审批中....数组
做为一个技术人员,技术的问题仍是要解决。通过线上日志的分析,日志采用小时机制,一个小时一个日志文件,同一个小时的日志文件有多个,也就是说同一时间内的日志有可能分散在多个日志文件中,这也是Y总要合并的主要缘由。每一个日志文件大约有500M,大约有100个。此时,若是你阅读到此文章,该怎么作呢?不如先静心想2分钟!!数据结构
要想实现Y总的需求其实仍是有几个难点的:asp.net
那咱们该怎么作呢?其中一个解决方案就是它:堆测试
堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆一般是一个能够被看作一棵树的数组对象。堆老是知足下列性质:ui
对于每一个节点的值都大于等于子树中每一个节点值的堆,咱们叫做“大顶堆”。对于每一个节点的值都小于等于子树中每一个节点值的堆,咱们叫做“小顶堆”。this
彻底二叉树比较适合用数组来存储(链表也能够实现)。为何这么说呢?用数组来存储彻底二叉树是很是节省存储空间的。由于咱们不须要存储左右子节点的指针,单纯地经过数组的下标,就能够找到一个节点的左右子节点和父节点。
通过上图能够发现,数组位置0为空,虽然浪费了一个存储空间,可是当计算元素在数组位置的时候确很是方便:数组下标为X的元素的左子树的下标为2x,右子树的下标为2x+1。
其实实现一个堆很是简单,就是顺着元素所在的路径,向上或者向下对比而后交换位置。spa
添加元素的时候咱们习惯采用自下而上的调整方式来调整堆,咱们在数组的最后一个空闲位置插入新元素,按照堆的下标上标原则查找到父元素对比,若是小于父元素的值(大顶堆),则互相交换。如图:.net
对于大顶堆,堆顶的元素就是最大元素。删除该元素以后,咱们须要把第二大元素提到堆顶位置。依次类推,直到把路径上的全部元素都调整完毕。指针
如下代码通过少量修改甚至不修改的状况下可直接在生产环境应用
/// <summary> /// 小顶堆,T类型须要实现 IComparable 接口 /// </summary> class MinHeap<T> where T : IComparable { private T[] container; // 存放堆元素的容器 private int capacity; // 堆的容量,最大能够放多少个元素 private int count; // 堆中已经存储的数据个数 public MinHeap(int _capacity) { container = new T[_capacity + 1]; capacity = _capacity; count = 0; } //插入一个元素 public bool AddItem(T item) { if (count >= capacity) { return false; } ++count; container[count] = item; int i = count; while (i / 2 > 0 && container[i].CompareTo(container[i / 2]) < 0) { // 自下往上堆化,交换 i 和i/2 元素 T temp = container[i]; container[i] = container[i / 2]; container[i / 2] = temp; i = i / 2; } return true; } //获取最小的元素 public T GetMinItem() { if (count == 0) { return default(T); } T result = container[1]; return result; } //删除最小的元素,即堆顶元素 public bool DeteleMinItem() { if (count == 0) { return false; } container[1] = container[count]; container[count] = default(T); --count; UpdateHeap(container, count, 1); return true; } //从某个节点开始从上向下 堆化 private void UpdateHeap(T[] a, int n, int i) { while (true) { int maxPos = i; //遍历左右子树,肯定那个是最小的元素 if (i * 2 <= n && a[i].CompareTo(a[i * 2]) > 0) { maxPos = i * 2; } if (i * 2 + 1 <= n && a[maxPos].CompareTo(a[i * 2 + 1]) > 0) { maxPos = i * 2 + 1; } if (maxPos == i) { break; } T temp = container[i]; container[i] = container[maxPos]; container[maxPos] = temp; i = maxPos; } } }
//由于须要不停的从log文件读取内容,因此须要一个和log文件保持链接的包装 class LogInfoIndex : IComparable { //标志内容来自于哪一个文件 public int FileIndex { get; set; } //具体的日志文件内容 public LogInfo Data { get; set; } public int CompareTo(object obj) { var tempInfo = obj as LogInfoIndex; if (this.Data.Index > tempInfo.Data.Index) { return 1; } else if (this.Data.Index < tempInfo.Data.Index) { return -1; } return 0; } } class LogInfo { //用int来模拟datetime 类型,由于用int 看的最直观 public int Index { get; set; } public string UserName { get; set; } }
static void WriteFile() { int fileCount = 0; while (fileCount < 10) { string filePath = $@"D:\log\{fileCount}.txt"; int index = 0; while (index < 100000) { LogInfo info = new LogInfo() { Index = index, UserName = Guid.NewGuid().ToString() }; File.AppendAllText(filePath, JsonConvert.SerializeObject(info)+ "\r\n"); index++; } fileCount++; } }
文件内容以下:日志
static void Main(string[] args) { int heapItemCount = 10; int startIndex = 0; StreamReader[] allReader = new StreamReader[10]; MinHeap<LogInfoIndex> container = new MinHeap<LogInfoIndex>(heapItemCount); //首先每一个文件读取一条信息 while(startIndex< heapItemCount) { string filePath = $@"D:\log\{startIndex}.txt"; System.IO.StreamReader reader = new System.IO.StreamReader(filePath); allReader[startIndex] = reader; string content= reader.ReadLine(); var contentObj = JsonConvert.DeserializeObject<LogInfo>(content); LogInfoIndex item = new LogInfoIndex() { FileIndex= startIndex , Data= contentObj }; container.AddItem(item); startIndex++; } //而后开始循环出堆,入堆 while (true) { var heapFirstItem = container.GetMinItem(); if (heapFirstItem == null) { break; } container.DeteleMinItem(); File.AppendAllText($@"D:\log\total.txt", JsonConvert.SerializeObject(heapFirstItem.Data) + "\r\n"); var nextContent = allReader[heapFirstItem.FileIndex].ReadLine(); if (string.IsNullOrWhiteSpace( nextContent)) { //若是其中一个文件已经读取完毕 则跳过 continue; } var contentObj = JsonConvert.DeserializeObject<LogInfo>(nextContent); LogInfoIndex item = new LogInfoIndex() { FileIndex = heapFirstItem.FileIndex, Data = contentObj }; container.AddItem(item); } //释放StreamReader foreach (var reader in allReader) { reader.Dispose(); } Console.WriteLine("完成"); Console.Read(); }
结果以下:
添加关注,查看更精美版本,收获更多精彩