有效地从 ArrayList 中获取子列表

Posted

技术标签:

【中文标题】有效地从 ArrayList 中获取子列表【英文标题】:Get a sublist from an ArrayList efficiently 【发布时间】:2015-11-19 15:06:22 【问题描述】:

我的问题


我有一个包含自定义变量的固定大小的 ArrayList。尽管 ArrayList 具有固定大小,但有时它们中的很多实际上是空的。问题是我需要返回没有空变量的 ArrayList。 需要注意的重要一点: ArrayList 将首先包含其所有非空项,然后是它们下面的所有空项,例如,元素是不混合。示例:[非空,非空,....空,空,空]

我的解决方法


我虽然创建了一个 for 循环来检查(从最后一个索引到第一个索引)ArrayList 中的每个元素,以确定它是否为空。如果为空,那么我将调用此代码:

for (i = size-1; i >=0 ; i--) 
    groupList = new ArrayList<>(groupList.subList(0, i));

我的问题


如果 ArrayList 太大,这种方法可能会特别慢(或者不是?)。我想知道是否存在更好、对性能更友好的解决方案。 AFAIK .subList 方法很昂贵。

【问题讨论】:

.subList 方法是 O(1)。它不复制;它是超级超级便宜。 这显示了一个严重的设计问题。如果您真正想要的是一个不包含空值的可变大小列表,那么制作一个固定大小的列表并在列表中存储空值有什么意义?为什么不至少将第一个空值的索引存储在变量中? @Rainbolt 我主要是在回答 OP 问题的最后一句话,声称“.subList 方法很昂贵。” 【参考方案1】:

您可以拥有binary search 的变体,其中您的自定义比较器是:

两个元素都为空/不为空?他们是平等的 只有一个元素为空? none null 是“更小”。

您正在寻找第一个空元素。

这将花费 O(logn) 时间,其中 n 是数组的大小。

但是,获取不为空的ArrayList 的子列表(假设您要将其复制到新的列表对象)将是复制元素的线性时间,因为您必须“触摸”每个其中。

这为您提供了O(logn + k) 的总时间复杂度,其中k 是非空元素的数量,n 是数组的大小。

【讨论】:

只是改变数组的结束位置而不复制/移动它是 O(1)——这在某些语言/某些实现中应该是可能的。那么 O(logn) 呢? @brendan 是的,但是 AFAIK,特别是在 java 中,它需要创建一个自定义实现。也许ArrayList.removeRange() 有效地(可能)支持这种行为,但它是一种受保护的方法。 是的,听起来像 java...哈哈【参考方案2】:

根据您所有出色的建议,我修改了原始方法,以便我可以获取最后一个(第一个)空项目位置并只调用一次 .subList 方法。这里是:

int lastNullIndex = size - 1;

for (i = lastNullIndex; i >= 0; i--) 
    if (null == groupList.get(i)) 
        lastNullIndex = i;
     else 
        break;
    


groupList = new ArrayList<>(groupList.subList(0, lastNullIndex));
return groupList;

如果您认为它可以进一步修改以获得更好的性能,请告诉我们。

【讨论】:

以上是关于有效地从 ArrayList 中获取子列表的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地从 NSManagedObject 中获取属性的所有有效值?

如何高效地从多个表中查询子记录?

如何有效地从 Android 上的资源中获取短数组?

如何有效地从 Firebase 实时数据库中获取 7 天、30 天的摘要?

Android有效地从输入流中读取

如何使 ListView 的内容成为 ArrayList?