让我们来测量向数组元素添加多个值的开销。在Java中,我们可以用新对象[K]来代替Θ(K)来表示开销。当我们为了增加开销而将前一个数组元素复制到现有数组中时,(使用System.arraycopy),这也不会改变。因此,在最坏情况下的开销 Ci 取决于 A.add(x) ,我们可以用一个简单的增量表达式来概括:
Ci(K,M)={ α1, if M>k; α2(K+1),if K=M , 上式中K的值为A.size(), M >= K,其表示的是 A 当前的数据量(即 A.data.length) 且 αi 是一个恒定的量。
所以,我们可以保守的说 C(K, M, I)∈Θ(K).
现在让我们考虑实现的成本Cd,如图4.1所示,当必须增加容量时,我们总是将容量加倍。即 Cd(K, M) ={α1,ifM > K;α3(2K+ 1),ifM=K 。 最坏情况下的成本看起来是一样的;大小增加2的因子只是改变了常数因子,我们仍然可以使用与之前相同的公式:Cd(K, M)∈O(K)。
现在考虑加倍策略。我们将分析其使用的潜在函数,从第1章第4小节中展示的数据中选择一个常数作为 ai,取一个花费为均值的操作 i,再找一个合适的潜在的 Φ ,Φ>0。则有: ai=ci+ Φi+1−Φi , 其中 ci 表示 第 ith 个加法操作的实际开销。 在这种情况下,一个潜在的表达式是 Φi= 4i−2Si+ 2S0 ,Si 表示的是在第 ith 个操作之前数组的容量。在第一次扩容后,我们总是能得到 2i >= Si ,所以对于所有的 i 总有 Φi≥0。
我们可以取数组中第i个加法之前的项数为i,假设我们从0开始进行加法。第i次加法的实际成本ci,如果i < Si,则为1个时间单位,否则(当i=Si时)分配一个多倍数组、复制所有现有项,然后再添加一个的成本,我们可以将其作为2Si时间单位(当然要选择合适的“时间单位”)。因此,在当 i < Si 时, 我们得到:
public ConcatList (List L0, List L1){…}不支持添加和删除对象的可选操作,但给出了L0和L1连接的视图。也就是说,这样一个列表上的get(i)在执行get操作时,在L0和L1的连接中给出元素i(也就是说,对L0和L1引用的列表的更改反映在连接的列表中)。还要确保iterator和listIterator能够工作