实现优化双向List,biList 的历程

缘由:聊天系统中,请求历史记录,要把数据插在前面,新来的消息要插在后面,每一条又要按照索引取值。

经常看到 list.insert(0,x),这种代码,真的很烦。。因为每次insert都要去复制数组。就在想既然需要频繁在前面插入数据,为什么不用链表呢,然后发现还有需求是要按索引取值,链表的取值。。只能去不断的next(),也是烦的一笔。于是,OvonicList 应运而生!

思路就是  biList 在需要改变容量的时候,容量变成当前size的3倍,然后把原有的数据放到中间,再去操作。这样,首尾添加的时候,只有当需要扩容的时候(而不是list那样每次)才会复制数组,效率高一点

大概的示意图如上。

需要的字段有,头部,尾部,数量,数组,容量

基本接口,头部添加,尾部添加,按索引取值,按索引移除,迭代器 等等

private int _head;
private int _tail;
private int _size;
private T[] _items;
private int _capacity;

public void AddFirst(T t)
public void AddLast(T t)
public T this[int index]
public void RemoveAt(int index)
public void Clear()
public bool Remove(T t)
public IEnumerator<T> GetEnumerator()

先看看需要改变容量的场景

1.往前面添加的时候头部index到了0    _head == 0

2.往后面添加的时候尾部index到了数组最后一个    _tail == items.Length-1

3.移除项的时候,容量过大,暂定    _size < _items.Length / 4

然后码代码。做性能测试。

 

第一行耗时 是自己写的OvonicList ,第二行是LinkedList ,第三行是List

图一:三种扩容条件放在一个if里面做判断。  100w次测试,发现效率居然还比不上List.insert

纳尼!!!??一定是我写的有问题,于是把三种条件拆分了一下

图二:拆分后,比list稍微快那么一丢丢

还是不应该啊,继续找原因,原来是最开始写的 默认容量3,并且第一个元素插入index为1,并没有真正的插入到中间位置,于是优化了一下初始容量最小为11,并且插入位置 (_capacity - 1) / 2;

图三:这下性能完爆List了,只比链表慢一丢丢

还是不服,又对移除做了下优化,List中按index移除,直接就是从该index'开始从新复制后面的数组去,稍微做了下优化,我判断,如果是在前半部分,就数组往后顺序移动,如果在后半部分,就往前顺序移动,果然又快了一丢丢

图四:中间插入移除的时候,改变复制数组为数组顺序移动,性能再好一点

最后这个结果已经算是比较满意了,满足了快速的双端添加,按索引取值,非常适合聊天系统这种场景。接下来只需要添加一些 addrange、构造函数传入数组之类的了。不过这些,基本很少会用到。最后贴上全部代码

using System;
using System.Collections;
using System.Collections.Generic;

public class biList<T> : IReadOnlyList<T>
{
    private const int Mincapacity = 0xF;
    private int _head;
    private int _tail;
    private int _size;
    private T[] _items;
    private int _capacity;

    public biList() : this(Mincapacity)
    {
    }

    public biList(int capacity)
    {
        _capacity = capacity < Mincapacity ? Mincapacity : capacity;
        _items = new T[_capacity];
        _head = 0;
        _tail = 0;
        _size = 0;
    }

    public void AddFirst(T t)
    {
        if (_size == 0)
        {
            _head = _tail = (_capacity - 1) / 2;
            _items[_head] = t;
            _size++;
            return;
        }

        if (_head == 0)
        {
            Prepare();
        }

        _items[--_head] = t;
        _size++;
    }

    public void AddLast(T t)
    {
        if (_size == 0)
        {
            _head = _tail = (_capacity - 1) / 2;
            _items[_head] = t;
            _size++;
            return;
        }

        if (_tail == _items.Length - 1)
        {
            Prepare();
        }

        _items[++_tail] = t;
        _size++;
    }

    public void Clear()
    {
        Array.Clear(_items, 0, _items.Length);
        _head = 0;
        _tail = 0;
        _size = 0;
    }

    public void RemoveAt(int index)
    {
        IndexCheck(index);

        var realIndex = _head + index;

        //经测试,移除的时候采用method2  效率更高
        //method 1
//            {
//                _size--;
//                Array.Copy(_items, realIndex + 1, _items, realIndex, _tail - realIndex + 1);
//            }

        //method 2
        if (index <= (_size - 1) / 2)
        {
            for (int i = realIndex; i > _head; i--)
            {
                _items[i] = _items[i - 1];
            }

            _items[_head++] = default(T);
            _size--;
        }
        else
        {
            for (int i = realIndex; i < _tail; i++)
            {
                _items[i] = _items[i + 1];
            }

            _items[_tail--] = default(T);
            _size--;
        }

        if (_size < _items.Length / 4)
        {
            Prepare();
        }
    }

    public bool Remove(T t)
    {
        for (int i = _head; i < _size; i++)
        {
            if (EqualityComparer<T>.Default.Equals(_items[i], t))
            {
                RemoveAt(i);
                return true;
            }
        }

        return false;
    }

    private void Prepare()
    {
        _capacity = _size * 3;
        var temp = _items;
        _items = new T[_capacity];
        Array.Copy(temp, _head, _items, _size, _size);
        _head = _size;
        _tail = 2 * _size - 1;
    }

    private void IndexCheck(int index)
    {
        if (index < 0 || index > _size - 1)
        {
            throw new IndexOutOfRangeException();
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        var index = _head - 1;
        while (++index <= _tail)
        {
            yield return _items[index];
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public int Count
    {
        get { return _size; }
    }

    public T this[int index]
    {
        get
        {
            IndexCheck(index);

            return _items[_head + index];
        }
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值