我需要一个保持插入顺序并具有唯一值的集合。 LinkedHashSet看起来像是要走的路,但是有一个问题 - 当两个项相同时,它会移除最新的一个(这很有意义),这里有一个例子:
set.add("one");
set.add("two");
set.add("three");
set.add("two");
LinkedHashSet
将打印:
one
,two
,three
但我需要的是:
one
,three
,two
这里最好的解决方案是什么?是否有任何可以执行此操作的集合/集合方法,还是应该手动实现它?
答案 0 :(得分:34)
大多数Java Collections可以扩展以进行调整。
子类LinkedHashSet
,覆盖add
方法。
class TweakedHashSet<T> extends LinkedHashSet<T> {
@Override
public boolean add(T e) {
// Get rid of old one.
boolean wasThere = remove(e);
// Add it.
super.add(e);
// Contract is "true if this set did not already contain the specified element"
return !wasThere;
}
}
答案 1 :(得分:18)
您只需使用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]
在Oracle的JRE中,LinkedHashSet
无论如何都由LinkedHashMap
支持,因此没有太大的功能差异,但这里使用的特殊构造函数配置LinkedHashMap
来改变每个<的顺序em> access 不仅适用于插入。这可能听起来太多了,但实际上只会影响已包含的键(Set
意义上的值)的插入。其他受影响的Map
操作(即get
)未被返回的Set
使用。
如果您不使用Java 8,由于类型推断有限,您必须帮助编译器:
Set<String> set
= Collections.newSetFromMap(new LinkedHashMap<String, Boolean>(16, 0.75f, true));
但功能相同。
答案 2 :(得分:6)
初始化您的LinkedHashSet时,您可以覆盖add方法。
Set<String> set = new LinkedHashSet<String>(){
@Override
public boolean add(String s) {
if(contains(s))
remove(s);
return super.add(s);
}
};
现在它给你:
set.add("1");
set.add("2");
set.add("3");
set.add("1");
set.addAll(Collections.singleton("2"));
// [3, 1 ,2]
即使是addAll方法也在工作。
答案 3 :(得分:1)
如果我们不想覆盖已经实现的集合,那么上面提供的所有解决方案都非常出色。我们可以通过使用带有小技巧的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
}
我们可以调用此方法将元素添加到列表中
List<String> list = new ArrayList<>();
addToList(list, "one");
addToList(list, "two");
addToList(list, "three");
addToList(list, "two");
这里唯一的缺点是我们需要每次调用我们的自定义addToList()
方法,而不是list.add()