List 的 removeAll 方法的效率

Java中,List是最常用到的一种集合类。我们也经常对List进行操作,也没有碰到什么问题。但是刚刚在调用removeAll方法是,碰到了严重的性能问题。

问题是这样的:

原集合:List<T> source,有大概200,000数据。

目标集合:List<T> destination,有大概150,000数据。

两者中都可能有重复的元素,两者中可能有相同的元素。已经实现了T中的hashCode(),equals()方法。我调用了source.removeAll(destination),结果花费了15分钟时间。这真是不可接受的。

下来就是自己瞎折腾,试图实现一个与removeAll()方法功能一样的方法,但是性能要有提高。

思路一:有资料表明,给List中add()数据的速度要比从List中remove()数据的快。试着实现了下,但是效果不明显,与原来的removeAll()差别不大。源代码如下:

	public List<T> removeAll_01(List<T> source, List<T> destination) {
		List<T> result = new LinkedList<T>();
		for(T t : source) {
			if (!destination.contains(t)) {
				result.add(t);
			}
		}
		return result;
	}

思路二:运用Set可以去重这一特性。将source中的元素逐个添加到由destination生成的Set中,如果Set.add(e)为true,说明e应该保留到结果中,否则放弃e。因为source中可能存在重复的元素,因此想到用Map来保存source中的元素与其在source中出现的次数。结果令我大跌眼镜,太JB帅了。性能的提高有好几个数量级。源代码如下:

	public List<T> removeAll_02(List<T> source, List<T> destination) {
		List<T> result = new LinkedList<T>();

		Map<T, Integer> sourceMap = new HashMap<T, Integer>();
		for (T t : source) {
			if (sourceMap.containsKey(t)) {
				sourceMap.put(t, sourceMap.get(t) + 1);
			} else {
				sourceMap.put(t, 1);
			}
		}

		Set<T> all = new HashSet<T>(destination);
		for (Map.Entry<T, Integer> entry : sourceMap.entrySet()) {
			T key = entry.getKey();
			Integer value = entry.getValue();
			if (all.add(key)) {
				for (int i = 0; i < value; i++) {
					result.add(key);
				}
			}
		}
		return result;
	}


思路三:比较下思路二和思路一的代码实现,思路二优于思路一?为什么,为什么?难道Map.containsKey()方法要比List.contains()方法快几何倍数。所以有了思路三。因为Map.containsKey()实际就是Set.contains(),所以对思路一的代码做了少许修改。证实了想法,结果比思路二更好。源代码如下:

	public List<T> removeAll_03(List<T> source, List<T> destination) {
		List<T> result = new LinkedList<T>();
		Set<T> destinationSet = new HashSet<T>(destination);
		for(T t : source) {
			if (!destinationSet.contains(t)) {
				result.add(t);
			}
		}
		return result;
	}


那么,让我没看看具体的下效果如何。为了方便测试,T选用Integer,List中的元素用随机数Random.nextInt(),随机数的最大之为1,000,000。


上述结果表明,随着数据量的变大,思路二和思路三的表现非常出色。为什么会这样呢?归根到底,还是因为Map.containsKey()和Set.contains()的速度快。

好了,这个问题到此先告一段落。之后,再分析下各思路的算法和时间复杂度。

大家可以到http://download.csdn.net/detail/kangxingang/5549289下载完整代码,修改count和maxNumber后,查看执行结果。

转载请注明出处。



  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Java 中有几种方法可以对 List 去重。其中一种方法是使用双重循环来遍历 List,并且比较元素是否相等,如果相等则将重复的元素移除。示例代码如下: public static List removeDuplicate(List list) { for (int i = 0; i < list.size() - 1; i++) { for (int j = list.size() - 1; j > i; j--) { if (list.get(j).equals(list.get(i))) { list.remove(j); } } } return list; } 引用是示例代码,它使用了双重循环的方式来去重。首先,外层循环从第一个元素开始遍历,内层循环从最后一个元素开始遍历,如果内层循环的元素与外层循环的元素相等,则将内层循环的元素移除。最后返回去重后的 List。 另一种方法是创建一个临时的 List,遍历原始的 List,如果临时 List 中不包含当前元素,则将当前元素添加到临时 List 中。示例代码如下: public static List removeDuplicate(List list){ List listTemp = new ArrayList(); for(int i=0;i<list.size();i++){ if(!listTemp.contains(list.get(i))){ listTemp.add(list.get(i)); } } return listTemp; } 引用是示例代码,它使用了临时 List 的方式来去重。首先,创建一个空的临时 List,然后遍历原始的 List,对于每个元素,如果临时 List 中不包含该元素,则将该元素添加到临时 List 中。最后返回去重后的 List。 这些方法可以根据你的具体需求选择使用,根据 List 的大小和性能要求,选择合适的方法可以提高程序的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java中对List去重 Stream去重的解决方法](https://download.csdn.net/download/weixin_38667403/12761286)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Java中List集合去除重复数据的六种方法](https://blog.csdn.net/gb4215287/article/details/122599183)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值