How to maintain a Unique List in Java?

How to create a list of unique/distinct objects (no duplicates) in Java?

Right now I am using HashMap<String, Integer> to do this as the key is overwritten and hence at the end we can get HashMap.getKeySet() which would be unique. But I am sure there should be a better way to do this as the value part is wasted here.

307859 次浏览

您可能希望使用 java.util.Set<E> Interface 的实现类之一,例如 java.util.HashSet<String>集合类。

A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element. As implied by its name, this interface models the mathematical set abstraction.

HashSet<String>(或)任何 Set实现可以为您做这项工作。 Set不允许重复。

这是 HashSet 的 javadoc

You could just use a HashSet<String> to maintain a collection of unique objects. If the Integer values in your map are important, then you can instead use the containsKey method of maps to test whether your key is already in the map.

您可以使用 预备实现:

来自 JAVADoc 的一些信息:

包含 没有重复的元素的集合。更正式的说法是,set 不包含 e1和 e2元素对,因此 e1.equals (e2)最多只包含一个 null 元素。正如其名称所暗示的那样,该接口对数学集抽象进行建模。

注意: 如果使用可变对象作为 set 元素,必须非常小心。当对象是集合中的元素时,如果以影响等于比较的方式更改对象的值,则不指定集合的行为。这一禁令的一个特殊情况是,不允许集合将自身包含为一个元素。`

以下是实施方案:

  • HashSet

    这个类为基本操作(添加、删除、包含和大小)提供了恒定的时间性能,假设哈希函数在桶中正确地分散元素。对这个集合进行迭代需要的时间与 HashSet 实例的大小(元素的数量)加上后台 HashMap 实例的“容量”(桶的数量)之和成正比。因此,如果迭代性能很重要,那么不要将初始容量设置得太高(或负载因子太低) ,这一点非常重要。

    迭代 HashSet时,取得的元素的顺序是未定义的。

  • LinkedHashSet

    Set 接口的哈希表和链表实现,具有可预测的迭代顺序。此实现与 HashSet 的不同之处在于,它维护一个贯穿其所有条目的双链表。这个链表定义了迭代顺序,即元素插入集合的顺序(插入顺序)。注意,如果将元素重新插入到集合中,则插入顺序不受影响。(如果 s.add (e)被调用,则 e 元素将重新插入集合 s 中,而 s.include (e)将在调用之前立即返回 true。)

    那么,上面代码的输出..。

     Set<Integer> linkedHashSet = new LinkedHashSet<>();
    linkedHashSet.add(3);
    linkedHashSet.add(1);
    linkedHashSet.add(2);
    
    
    for (int i : linkedHashSet) {
    System.out.println(i);
    }
    

    必然是

    3
    1
    2
    
  • TreeSet

    This implementation provides guaranteed log(n) time cost for the basic operations (add, remove and contains). By default he elements returned on iteration are sorted by their "natural ordering", so the code above...

     Set<Integer> treeSet = new TreeSet<>();
    treeSet.add(3);
    treeSet.add(1);
    treeSet.add(2);
    
    
    for (int i : treeSet) {
    System.out.println(i);
    }
    

    会输出这个:

    1
    2
    3
    

    (还可以将 Comparator实例传递给 TreeSet构造函数,使其按照不同的顺序对元素进行排序。)

    注意,如果要正确实现 Set 接口,那么由集合维护的顺序(无论是否提供显式比较器)必须与 equals 一致。(有关与等式一致的精确定义,请参见 Comparable 或 Comparator。)这是因为 Set 接口是根据 equals 操作定义的,但是 TreeSet 实例使用其 compareTo (或 compare)方法执行所有元素比较,因此从集合的角度来看,被该方法认为相等的两个元素是相等的。即使集合的排序与等式不一致,集合的行为也是定义良好的; 它只是不遵守 Set 接口的一般约定。

使用 new HashSet<String> 举个例子:

import java.util.HashSet;
import java.util.Set;


public class MainClass {
public static void main(String args[]) {
String[] name1 = { "Amy", "Jose", "Jeremy", "Alice", "Patrick" };


String[] name2 = { "Alan", "Amy", "Jeremy", "Helen", "Alexi" };


String[] name3 = { "Adel", "Aaron", "Amy", "James", "Alice" };


Set<String> letter = new HashSet<String>();


for (int i = 0; i < name1.length; i++)
letter.add(name1[i]);


for (int j = 0; j < name2.length; j++)
letter.add(name2[j]);


for (int k = 0; k < name3.length; k++)
letter.add(name3[k]);


System.out.println(letter.size() + " letters must be sent to: " + letter);


}
}

我想澄清一些事情在这里为原来的海报,其他人已经提到,但没有真正明确说明。当你说你想要一个唯一列表,这是一个有序集的定义。Set Interface 和 List 接口之间的一些其他关键区别是 List 允许指定插入索引。所以,问题是你真的需要列表接口(例如为了与第三方库兼容等) ,或者你可以重新设计你的软件使用集接口?您还必须考虑如何使用该接口。通过索引查找元素重要吗?您希望在您的集合中包含多少元素?如果你有很多元素,排序重要吗?

如果您确实需要一个只有一个惟一约束的 List,那么可以使用 Apache Common Utils 类 org.Apache.Common.Collections.List。SetUniqueList,它将为您提供 List 接口和唯一约束。注意,这破坏了 List 接口。但是,如果需要按索引查找列表,则可以从中获得更好的性能。如果您可以处理 Set 接口,并且数据集较小,那么 LinkedHashSet 可能是一个不错的选择。这取决于软件的设计和意图。

同样,每个集合都有一定的优点和缺点。有些插入快但读取慢,有些读取快但是插入慢,等等。花费大量的时间使用集合文档来充分了解每个类和接口的细节是有意义的。

我不知道这有多高效,但在一个简单的背景下对我有效。

List<int> uniqueNumbers = new ArrayList<>();


public void AddNumberToList(int num)
{
if(!uniqueNumbers .contains(num)) {
uniqueNumbers .add(num);
}
}