删除重复项(两个值) – 从ArrayList重复值
我有一个带有以下字符串的ArrayList
;
List e = new ArrayList(); e.add("123"); e.add("122"); e.add("125"); e.add("123");
我想检查列表中的重复项并将其从列表中删除。 在这种情况下,我的列表将只有两个值,在这个例子中,它将是值122和125,并且两个123将消失。
最好的方法是什么? 我正在考虑使用Set
,但这只会删除其中一个重复项。
在Java 8中,您可以:
e.removeIf(s -> Collections.frequency(e, s) > 1);
如果!Java 8,您可以创建HashMap
。 如果字符串已经出现在地图中,则将其键增加1,否则将其添加到地图中。
例如:
put("123", 1);
现在让我们假设您再次拥有“123”,您应该获取密钥的计数并添加一个:
put("123", get("aaa") + 1);
现在,您可以轻松地在地图上进行迭代,并使用其值<2的键创建新的数组列表。
参考文献:
-
ArrayList#removeIf
-
Collections#frequency
-
HashMap
您还可以在Java 8中使用filter
e.stream().filter(s -> Collections.frequency(e, s) == 1).collect(Collectors.toList())
您可以使用HashMap
。
迭代列表,如果哈希映射不包含字符串,则将其与值1一起添加。
另一方面,如果你已经有了字符串,你只需增加计数器。 因此,您的字符串的映射将如下所示:
{"123", 2} {"122", 1} {"125", 1}
然后,您将创建一个新列表,其中每个键的值为1。
这是一个非Java 8解决方案,使用map来计算出现次数:
Map map = new HashMap() for (String s : list){ if (map.get(s)==null){ map.put(s, 1); } else { map.put(s, map.get(s)+1); } } List newList = new ArrayList (); // Remove from list if there are multiples of them. for (Map.Entry entry : map.entrySet()) { if(entry.getValue() > 1){ newList.add(entry.getKey()); } } list.removeAll(newList);
ArrayList中的解决方案
public static void main(String args[]) throws Exception { List e = new ArrayList (); List duplicate = new ArrayList (); e.add("123"); e.add("122"); e.add("125"); e.add("123"); for(String str : e){ if(e.indexOf(str) != e.lastIndexOf(str)){ duplicate.add(str); } } for(String str : duplicate){ e.remove(str); } for(String str : e){ System.out.println(str); } }
使用流的最简单的解决方案具有O(n^2)
时间复杂度。 如果您在包含数百万条目的List
上尝试它们,您将等待非常长的时间。 O(n)
解决方案是:
list = list.stream() .collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting())) .entrySet() .stream() .filter(e -> e.getValue() == 1) .map(Map.Entry::getKey) .collect(Collectors.toList());
在这里,我使用LinkedHashMap
来维护订单。 请注意,静态导入可以简化collect
部分。
这是如此复杂,我认为使用for
循环是这个问题的最佳选择。
Map map = new LinkedHashMap<>(); for (String s : list) map.merge(s, 1, Integer::sum); list = new ArrayList<>(); for (Map.Entry e : map.entrySet()) if (e.getValue() == 1) list.add(e.getKey());
List e = new ArrayList (); e.add("123"); e.add("122"); e.add("125"); e.add("123"); e.add("125"); e.add("124"); List sortedList = new ArrayList (); for (String current : e){ if(!sortedList.contains(current)){ sortedList.add(current); } else{ sortedList.remove(current); } } e.clear(); e.addAll(sortedList);
我是Google Guava API的粉丝。 使用Collections2实用程序和通用Predicate实现,可以创建一个实用程序方法来覆盖多种数据类型。
这假定所讨论的对象具有有意义的.equals实现
@Test public void testTrimDupList() { Collection dups = Lists.newArrayList("123", "122", "125", "123"); dups = removeAll("123", dups); Assert.assertFalse(dups.contains("123")); Collection dups2 = Lists.newArrayList(123, 122, 125,123); dups2 = removeAll(123, dups2); Assert.assertFalse(dups2.contains(123)); } private Collection removeAll(final T element, Collection collection) { return Collections2.filter(collection, new Predicate (){ @Override public boolean apply(T arg0) { return !element.equals(arg0); }}); }
再考虑一下这个
此页面中的大多数其他示例都使用java.util.List API作为基本Collection。 我不确定是否使用intent完成,但如果返回的元素必须是List,则可以使用另一个中间方法,如下所示。 多态性ftw!
@Test public void testTrimDupListAsCollection() { Collection dups = Lists.newArrayList("123", "122", "125", "123"); //List used here only to get access to the .contains method for validating behavior. dups = Lists.newArrayList(removeAll("123", dups)); Assert.assertFalse(dups.contains("123")); Collection dups2 = Lists.newArrayList(123, 122, 125,123); //List used here only to get access to the .contains method for validating behavior. dups2 = Lists.newArrayList(removeAll(123, dups2)); Assert.assertFalse(dups2.contains(123)); } @Test public void testTrimDupListAsList() { List dups = Lists.newArrayList("123", "122", "125", "123"); dups = removeAll("123", dups); Assert.assertFalse(dups.contains("123")); List dups2 = Lists.newArrayList(123, 122, 125,123); dups2 = removeAll(123, dups2); Assert.assertFalse(dups2.contains(123)); } private List removeAll(final T element, List collection) { return Lists.newArrayList(removeAll(element, (Collection ) collection)); } private Collection removeAll(final T element, Collection collection) { return Collections2.filter(collection, new Predicate (){ @Override public boolean apply(T arg0) { return !element.equals(arg0); }}); }
像这样的东西(使用Set ):
Set
如果你要去装,那么你可以用两套来实现它。 在另一组中维护重复值,如下所示:
List duplicateList = new ArrayList (); duplicateList.add("123"); duplicateList.add("122"); duplicateList.add("125"); duplicateList.add("123"); duplicateList.add("127"); duplicateList.add("127"); System.out.println(duplicateList); Set nonDuplicateList = new TreeSet (); Set duplicateValues = new TreeSet (); if(nonDuplicateList.size()
输出:原始列表:[123,122,125,123,127,127]。 删除后
副本:[122,125]重复的值:[123,127]注意:此解决方案可能未进行优化。 你可能会发现更好的
解决方案比这个。
使用Guava库,使用multiset和streams:
e = HashMultiset.create(e).entrySet().stream() .filter(me -> me.getCount() > 1) .map(me -> me.getElement()) .collect(toList());
这对于大型列表(具有相当大的常数因子的O(n))来说是相当快且相当快的。 但它不保留顺序(如果需要,可以使用LinkedHashMultiset
)并创建一个新的列表实例。
也可以很容易地概括,例如,删除所有三次重复。
通常,多集数据结构对于保留在一个工具箱中非常有用。