2

我有一种方法可以使用类似于此的方法汇总十进制值的集合...

Dim _amountCollection as New List(Of Decimal)
_amountCollection.Add(145.12D)
_amountCollection.Add(11.32D)
_amountCollection.Add(6547.07D)

Dim _totalAmount as Decimal

For Each _individualAmount as Decimal in _amountCollection
  _totalAmount += _individualAmount
Next 

在实际代码中,金额集合中的成员通常比较多,一个总操作中至少需要对50个单独的金额集合进行求和。

这种总量的重新计算经常被调用(至少一次,然后在每次收集内容更改时再次调用),并且在配置文件跟踪中显示为消耗了总操作时间的 2-5%。我正在寻找是否有人知道如何加快此求和运算的速度,或者这是否只是最快的。

在这种情况下缓存是不现实的,因为必须重新计算数量。

****编辑** 对于 Ravadre 和 Joel - 总金额存储在类级别(每个金额集合和总和都包含在类实例中)

有任何想法吗?

4

9 回答 9

3

尝试继承 List(Of Decimal),公开一个 TotalSum 属性。每次调用 Add() 时,都会增加插入的值。这将使您不必遍历列表以获得总和。

编辑:尝试后,最好实现 IList(Of Decimal) 因为 List 的方法不是虚拟的。类似这样的东西(是的,我知道它是 C#...)

public class DecimalSumList : IList<decimal>
{
    readonly IList<decimal> _allValues = new List<decimal>();
    decimal _totalSum;
    public decimal TotalSum { get { return _totalSum; } }
    public void Add(decimal item)
    {
        _totalSum += item;
        _allValues.Add(item);
    }

    public void CopyTo(decimal[] array, int arrayIndex)
    {
        _allValues.CopyTo(array, arrayIndex);
    }

    bool ICollection<decimal>.Remove(decimal item)
    {
        _totalSum -= item;
        return _allValues.Remove(item);
    }

    public int Count
    {
        get { return _allValues.Count; }
    }

    public bool IsReadOnly
    {
        get { throw new NotImplementedException(); }
    }

    public void Remove(decimal item)
    {
        _totalSum -= item;
        _allValues.Remove(item);
    }
    public void Clear()
    {
        _totalSum = 0;
        _allValues.Clear();
    }

    public bool Contains(decimal item)
    {
        return _allValues.Contains(item);
    }

    public IEnumerator<decimal> GetEnumerator()
    {
        return _allValues.GetEnumerator();
    }

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

    public int IndexOf(decimal item)
    {
        return _allValues.IndexOf(item);
    }

    public void Insert(int index, decimal item)
    {
        _totalSum += item;
        _allValues.Insert(index,item);
    }

    public void RemoveAt(int index)
    {
        _totalSum -= _allValues[index];
        _allValues.RemoveAt(index);
    }

    public decimal this[int index]
    {
        get { return _allValues[index]; }
        set { _allValues[index] = value; }
    }
}
于 2009-08-06T14:11:23.893 回答
2

我不清楚您希望如何比依次添加一个数字列表更快地添加一个数字列表!

但是,使用decimals 进行算术运算可能比使用浮点类型慢得多,所以如果您实际上不需要精度,我建议切换。

于 2009-08-06T14:05:51.483 回答
2

如果这些是货币值,请考虑使用缩放整数。对于 2 位小数,您存储美分数而不是美元数,然后在需要输出结果时除以 100。对于 5 位小数,您的变量在内部使用原始数字 * 100000。这应该会提高性能和准确性,尤其是在您进行任何除法时。

于 2009-08-06T17:11:53.860 回答
1

怎么样totalAmount = amountCollection.Sum()

于 2009-08-06T14:04:02.530 回答
1

至少有 50 个单独的金额集合需要在总操作中求和。

缓存每个单独集合的总和怎么样?然后,当集合更改时,您只需将该集合和缓存的总数相加即可。

于 2009-08-06T14:09:30.323 回答
1

总配置文件跟踪时间的 2-5% 并不算多。

即使您将此方法的执行时间减半,您也最多可以节省总运行时间的百分之几。

你最好的选择可能是先去别处看看。

于 2009-08-06T14:11:40.710 回答
0

考虑到您正在使用List(Of T).

其他人建议您使用Sum扩展方法 - 这不会提高性能,因为它的实现与您当前的问题解决方案相同:

<Extension> _
Public Shared Function Sum(ByVal source As IEnumerable(Of Decimal)) As Decimal
    If (source Is Nothing) Then
        Throw Error.ArgumentNull("source")
    End If
    Dim num As Decimal = 0
    Dim num2 As Decimal
    For Each num2 In source
        num = (num + num2)
    Next
    Return num
End Function
于 2009-08-06T14:02:45.153 回答
0

您的另一种选择是使用 for 循环,您不必创建枚举器,但实际上无论如何都没有显着的提升(据我所知)。

为什么不能缓存结果?如果您缓存总数量,并且对于其中一个元素的每次更改,请使用相同的值修改数量(并最终计算所有每 10 次修改以进行同步)。因此,当其中一个元素从 2.5 更改为 3.6 时,您只需将 1.1 添加到您的总量中。为什么这在你的情况下不起作用?

于 2009-08-06T14:05:50.110 回答
0

如果您不想像 statenjason 建议的那样实现 IList/IDictionary 的版本,您可以创建一个包装器:

Public Class TotalDictionaryWrapper
        Private innerDictionary As IDictionary(Of Integer, Decimal)
        Private m_total As Decimal = 0D
        
        Public Sub New(ByVal dictionary As IDictionary(Of Integer, Decimal))
            Me.innerDictionary = dictionary
        End Sub
        
        Public ReadOnly Property Total() As [Decimal]
            Get
                Return Me.m_total
            End Get
        End Property
        
        Public Sub Add(ByVal key As Integer, ByVal value As Decimal)
            Me.innerDictionary.Add(key, value)
            m_total += value
        End Sub
        
        Public Sub Remove(ByVal key As String)
            Dim toRemove As Decimal = Me.innerDictionary(key)
            Me.innerDictionary.Remove(key)
            Me.m_total -= toRemove
        End Sub
        
        Public Sub Update(ByVal key As Integer, ByVal newValue As Decimal)
            Dim oldValue As Decimal = Me.innerDictionary(key)
            Me.innerDictionary(key) = newValue
            Me.m_total -= oldValue
            Me.m_total += newValue
        End Sub
        
        Other methods..

    End Class
于 2009-08-06T15:35:58.173 回答