我有一个ArrayList
,其中包含以下字符串;
List<String> e = new ArrayList<String>();
e.add("123");
e.add("122");
e.add("125");
e.add("123");
我想检查列表中的重复项并将其从列表中删除。在这种情况下,我的列表将只有两个值,在这个例子中,它将是值122和125,并且两个123将消失。
最好的方法是什么?我正在考虑使用Set
,但这只会删除其中一个重复项。
答案 0 :(得分:24)
在Java 8中,你可以这样做:
e.removeIf(s -> Collections.frequency(e, s) > 1);
如果是!Java 8,您可以创建HashMap<String, Integer>
。如果字符串已经出现在地图中,请将其键增加1,否则,将其添加到地图中。
例如:
put("123", 1);
现在我们假设您再次拥有“123”,您应该获得密钥的计数并添加一个:
put("123", get("aaa") + 1);
现在,您可以轻松地在地图上进行迭代,并使用其值为&lt;的键创建一个新的数组列表。 2。
参考文献:
答案 1 :(得分:11)
您还可以在Java 8中使用filter
e.stream().filter(s -> Collections.frequency(e, s) == 1).collect(Collectors.toList())
答案 2 :(得分:6)
您可以使用HashMap<String, Integer>
。
您遍历列表,如果哈希映射不包含字符串,则将其与值1一起添加。
另一方面,如果您已经拥有该字符串,则只需递增计数器即可。因此,字符串的映射如下所示:
{"123", 2}
{"122", 1}
{"125", 1}
然后,您将创建一个新列表,其中每个键的值为1.
答案 3 :(得分:4)
这是一个使用map来计算出现次数的非Java 8解决方案:
Map map = new HashMap<String, Integer>()
for (String s : list){
if (map.get(s)==null){
map.put(s, 1);
}
else {
map.put(s, map.get(s)+1);
}
}
List<String> newList = new ArrayList<String>();
// Remove from list if there are multiples of them.
for (Map.Entry<String, String> entry : map.entrySet())
{
if(entry.getValue() > 1){
newList.add(entry.getKey());
}
}
list.removeAll(newList);
答案 4 :(得分:2)
ArrayList中的解决方案
public static void main(String args[]) throws Exception {
List<String> e = new ArrayList<String>();
List<String> duplicate = new ArrayList<String>();
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);
}
}
答案 5 :(得分:2)
List<String> e = new ArrayList<String>();
e.add("123");
e.add("122");
e.add("125");
e.add("123");
e.add("125");
e.add("124");
List<String> sortedList = new ArrayList<String>();
for (String current : e){
if(!sortedList.contains(current)){
sortedList.add(current);
}
else{
sortedList.remove(current);
}
}
e.clear();
e.addAll(sortedList);
答案 6 :(得分:2)
使用流的最简单的解决方案具有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<String, Integer> map = new LinkedHashMap<>();
for (String s : list)
map.merge(s, 1, Integer::sum);
list = new ArrayList<>();
for (Map.Entry<String, Integer> e : map.entrySet())
if (e.getValue() == 1)
list.add(e.getKey());
答案 7 :(得分:1)
像这样(使用Set):
Set<Object> blackList = new Set<>()
public void add(Object object) {
if (blackList.exists(object)) {
return;
}
boolean notExists = set.add(object);
if (!notExists) {
set.remove(object)
blackList.add(object);
}
}
答案 8 :(得分:1)
如果你要去装,那么你可以用两套来实现它。在另一组中维护重复值,如下所示:
List<String> duplicateList = new ArrayList<String>();
duplicateList.add("123");
duplicateList.add("122");
duplicateList.add("125");
duplicateList.add("123");
duplicateList.add("127");
duplicateList.add("127");
System.out.println(duplicateList);
Set<String> nonDuplicateList = new TreeSet<String>();
Set<String> duplicateValues = new TreeSet<String>();
if(nonDuplicateList.size()<duplicateList.size()){
for(String s: duplicateList){
if(!nonDuplicateList.add(s)){
duplicateValues.add(s);
}
}
duplicateList.removeAll(duplicateValues);
System.out.println(duplicateList);
System.out.println(duplicateValues);
}
输出:原始列表:[123,122,125,123,127,127]。删除后 复制:[122,125]重复的值:[123,127]
注意:此解决方案可能未进行优化。你可能会找到一个更好的 比这个解决方案。
答案 9 :(得分:1)
我是Google Guava API的粉丝。使用Collections2实用程序和通用的Predicate实现,可以创建一个实用方法来覆盖多种数据类型。
这假设有问题的对象具有有意义的.equals 实施
@Test
public void testTrimDupList() {
Collection<String> dups = Lists.newArrayList("123", "122", "125", "123");
dups = removeAll("123", dups);
Assert.assertFalse(dups.contains("123"));
Collection<Integer> dups2 = Lists.newArrayList(123, 122, 125,123);
dups2 = removeAll(123, dups2);
Assert.assertFalse(dups2.contains(123));
}
private <T> Collection<T> removeAll(final T element, Collection<T> collection) {
return Collections2.filter(collection, new Predicate<T>(){
@Override
public boolean apply(T arg0) {
return !element.equals(arg0);
}});
}
更多地考虑这个
本页中的大多数其他示例都使用java.util.List API作为基本Collection。我不确定是否使用intent完成,但如果返回的元素必须是List,则可以使用另一个中间方法,如下所示。多态性ftw!
@Test
public void testTrimDupListAsCollection() {
Collection<String> 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<Integer> 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<String> dups = Lists.newArrayList("123", "122", "125", "123");
dups = removeAll("123", dups);
Assert.assertFalse(dups.contains("123"));
List<Integer> dups2 = Lists.newArrayList(123, 122, 125,123);
dups2 = removeAll(123, dups2);
Assert.assertFalse(dups2.contains(123));
}
private <T> List<T> removeAll(final T element, List<T> collection) {
return Lists.newArrayList(removeAll(element, (Collection<T>) collection));
}
private <T> Collection<T> removeAll(final T element, Collection<T> collection) {
return Collections2.filter(collection, new Predicate<T>(){
@Override
public boolean apply(T arg0) {
return !element.equals(arg0);
}});
}
答案 10 :(得分:0)
使用Guava库,使用多集和流:
e = HashMultiset.create(e).entrySet().stream()
.filter(me -> me.getCount() > 1)
.map(me -> me.getElement())
.collect(toList());
对于大型列表( O(n)具有相当大的常数因子),这是相当快且相当快的。但它不保留顺序(如果需要可以使用LinkedHashMultiset
)并且它会创建一个新的列表实例。
也很容易概括,例如,删除所有三次重复。
通常,多集数据结构对于保留在工具箱中非常有用。