Java LinkedHashSet-插入顺序和副本-保持最新;“在上面”;

Java LinkedHashSet-插入顺序和副本-保持最新;“在上面”;,java,list,linkedhashset,Java,List,Linkedhashset,我需要一个保持插入顺序并具有唯一值的集合。LinkedHashSet看起来像是一种方式,但有一个问题——当两个项目相等时,它会删除最新的一个项目(这很有意义),下面是一个示例: set.add("one"); set.add("two"); set.add("three"); set.add("two"); LinkedHashSet将打印: 一个,两个,三个 但我需要的是: 一个,三个,两个 这里的最佳解决方案是什么?是否有任何collection/collections方法可以执行此操作,

我需要一个保持插入顺序并具有唯一值的集合。LinkedHashSet看起来像是一种方式,但有一个问题——当两个项目相等时,它会删除最新的一个项目(这很有意义),下面是一个示例:

set.add("one");
set.add("two");
set.add("three");
set.add("two");
LinkedHashSet
将打印:

一个
两个
三个

但我需要的是:

一个,
三个
两个


这里的最佳解决方案是什么?是否有任何collection/collections方法可以执行此操作,或者我应该手动实现它?

初始化LinkedHashSet时,您可以覆盖add方法

Set<String> set = new LinkedHashSet<String>(){
    @Override
    public boolean add(String s) {
        if(contains(s))
            remove(s);
        return super.add(s);
    }
};
甚至addAll方法也在工作。

大多数方法都可以扩展以进行调整

子类,重写该方法

class TweakedHashSet扩展LinkedHashSet{
@凌驾
公共布尔加法(TE){
//把旧的扔掉。
布尔值wasThere=remove(e);
//加上它。
增订(e);
//协定为“如果此集合尚未包含指定元素,则为true”
回来!在那里;
}
}

您只需使用
LinkedHashMap的一项特殊功能即可:

Set<String> set = Collections.newSetFromMap(new LinkedHashMap<>(16, 0.75f, true));
set.add("one");
set.add("two");
set.add("three");
set.add("two");
System.out.println(set); // prints [one, three, two]

但是功能是一样的。

上面提供的所有解决方案都非常好,但是如果我们不想覆盖已经实现的集合的话。我们只需使用一个带有小技巧的ArrayList就可以解决这个问题

我们可以创建一个方法,您可以使用该方法将数据插入列表

public static <T> void addToList(List<T> list, T element) {
    list.remove(element); // Will remove element from list, if list contains it
    list.add(element); // Will add element again to the list 
}
publicstaticvoidaddtolist(列表,T元素){
list.remove(element);//将从列表中删除元素,如果列表包含该元素
list.add(element);//将再次将元素添加到列表中
}
我们可以调用此方法将元素添加到列表中

List<String> list = new ArrayList<>();

addToList(list, "one");
addToList(list, "two");
addToList(list, "three");
addToList(list, "two");
List List=new ArrayList();
地址列表(列表,“一”);
地址列表(列表,“两”);
地址列表(列表“三”);
地址列表(列表,“两”);

这里唯一的缺点是我们每次都需要调用自定义的
addToList()
方法,而不是
list。add()

那么
ArrayList
contains
方法在插入之前如何使用呢?我想您必须实现自己的算法,可能由
HashSet
@AvihooMamka
TreeSet
支持根据自然顺序或给定顺序对元素进行排序,但它本身并不保留插入顺序。请注意,
LinkedHashSet
不是
final
,但您将无法通过用于存储/检索元素的备份映射进行访问。您可以使用map。每次用户输入一封信,您都会更新此地图。@rafakob-
ArrayList
允许重复。@OldCurmudgeon:如果您以前删除过,则不会inserting@rafakob在
LinkedHashSet
中,删除应该更有效,但您必须配置文件才能确保(取决于您的
T
s的典型
hashCode
实现的质量以及您的收藏的典型大小等)@corsiKa:差异不是3或4这样的因素,而是缩放,因此如果集合足够大,则会有性能差异。但是,当然,我们不知道OP的最大集合大小以及与迭代相比查找/修改的频率…@corsiKa:原则上,我们同意t对于一个特定的应用程序,评测是关键,也是对以下问题的分析:在不同的场景/生产环境/不同的客户/等等中是否会出现更多的元素。如果您知道您的任务中存在固有的限制,请使用预期的最大值进行测试和评测…+1我喜欢但我建议将此集合的创建放入一个命名良好的方法(
createAccessOrderSet
?)并添加了一些单元测试,这些测试清楚地说明了它的作用。@Hulk:当然,这对生产代码来说是正确的。然后,您可能会提供重载来提供容量和负载因子参数。我在这里使用了与默认构造函数相同的值,但没有一个有文档记录的工厂方法,它们看起来就像魔术文字感谢读者…很好也很简单。但是我不知道为什么你要按照问题使用列表而不是
LinkedHashSet
。谢谢@shmosel,我已经考虑了ArrayList作为我的代码示例,因为LinkedHashSet没有维护用户想要的顺序,而ArrayList也是解决这个问题最简单、最快的方法。
LinkedHashSet
保持插入顺序,就像
ArrayList
一样。它在删除时效率更高。
list.remove(element)
在O(N)时间内运行。
addToList
方法的性能会迅速下降。另一方面,哈希集在O(1)时间内运行
contains(element)
不管大小。
Set<String> set
    = Collections.newSetFromMap(new LinkedHashMap<String, Boolean>(16, 0.75f, true));
public static <T> void addToList(List<T> list, T element) {
    list.remove(element); // Will remove element from list, if list contains it
    list.add(element); // Will add element again to the list 
}
List<String> list = new ArrayList<>();

addToList(list, "one");
addToList(list, "two");
addToList(list, "three");
addToList(list, "two");