唯一包含一个键但在不同字段上排序的集合
Set that uniquely contains a key but ordered on different field
我正在寻找一个 Java 集合,可能在标准库中,它能够收集以下结构:
class Item {
String key;
double score;
}
并具有以下属性:
- 只允许一个具有相同键的项目(如一组)
- 插入、删除、检查是否存在,最大 O(logn)
- 按分数排序的遍历,在最大 O(logn) 中找到下一个
据我所知,标准 OrderedSet 必须具有与 equals() 接口一致的可比较接口,但我的情况并非如此,因为具有不同键的两个项目可能具有相同的分数。
事实上,我注意到 TreeSet 使用返回 0 的比较器来检查项目是否已经存在。
有什么建议吗?
我认为不存在这样的结构。您没有指定遍历性能要求,因此您可以使用普通 Set 并将值添加到列表中,然后按分数对该列表进行排序以进行遍历。
Only one Item with the same key is allowed (like a set)
您的 Item
class 应该仅使用 key
属性实现 hashCode() 和 equals()。
insert, remove, check existance in constant time
TreeSet
add() 和 remove() 是 O(ln N),因此它们不符合您的条件。
HashSet
add() 和 remove() 通常是 O(1)。
traversal ordered by score
您的性能要求是什么?您将多久遍历一次集合?如果您主要是添加和删除项目而很少遍历它,那么您可以在遍历操作期间将 HashSet
复制到 TreeSet
。
HashSet 不保证其元素的任何顺序。如果您需要此保证,请考虑使用 TreeSet 来保存您的元素
但为了通过关键实现独特性并保持恒定时间覆盖 hashCode()
和 equals()
有效地满足您的需求,如下所示:
class Item {
String key;
double score;
public Item(String key, double score) {
this.key = key;
this.score = score;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item item = (Item) o;
return key.equals(item.key);
}
@Override
public int hashCode() {
return Objects.hash(key);
}
@Override
public String toString() {
return "Item{" +
"key='" + key + '\'' +
", score=" + score +
'}';
}
}
// main
public static void main(String[] args) {
Set<Item> itemSet = new HashSet<>();
itemSet.add(new Item("1", 1));
itemSet.add(new Item("1", 2));
itemSet.add(new Item("2", 1));
//to get a sorted TreeSet
//Add all your objects to the TreeSet, you will get a sorted Set.
//TreeSet myTreeSet = new TreeSet();
//myTreeSet.addAll(itemSet);
//System.out.println(myTreeSet);
}
输出:
Item{key='1', score=1.0}
Item{key='2', score=1.0}
感谢那些用他们的评论和回答让我思考的人。我相信我们可以通过使用来达到要求:
TreeMap<Double, HashSet<Item>>
只是因为(我没说过)两个相同的键产生相同的分数;但更一般地说,有两个集合映射就足够了:一个(有序)以排序字段为键,另一个(未排序)以唯一字段为键。
现在插入已经放宽到 O(log n),你可以用双集来做到这一点,即实现你自己的集,在幕后维护 2 个集.
最好是您可以修改 class Item
以实现 equals()
和 hashCode()
以仅使用 key
字段。在这种情况下,您的 class 将使用 HashSet
和 TreeSet
。如果 hashCode()
涵盖的不仅仅是 key
字段,则使用两个 TreeSet
对象。
final class ItemSet implements NavigableSet<Item> {
private final Set<Item> keySet = new HashSet<>();
// or: new TreeSet<>(Comparator.comparing(Item::getKey));
private final TreeSet<Item> navSet = new TreeSet<>(Comparator.comparingDouble(Item::getScore)
.thenComparing(Item::getKey));
//
// Methods delegating to keySet for unique key access and for unordered access
//
@Override public boolean contains(Object o) { return this.keySet.contains(o); }
@Override public boolean containsAll(Collection<?> c) { return this.keySet.containsAll(c); }
@Override public int size() { return this.keySet.size(); }
@Override public boolean isEmpty() { return this.keySet.isEmpty(); }
//
// Methods delegating to navSet for ordered access
//
@Override public Comparator<? super Item> comparator() { return this.navSet.comparator(); }
@Override public Object[] toArray() { return this.navSet.toArray(); }
@Override public <T> T[] toArray(T[] a) { return this.navSet.toArray(a); }
@Override public Item first() { return this.navSet.first(); }
@Override public Item last() { return this.navSet.last(); }
@Override public Item lower(Item e) { return this.navSet.lower(e); }
@Override public Item floor(Item e) { return this.navSet.floor(e); }
@Override public Item ceiling(Item e) { return this.navSet.ceiling(e); }
@Override public Item higher(Item e) { return this.navSet.higher(e); }
//
// Methods delegating to both keySet and navSet for mutation of this set
//
private final class ItemSetIterator implements Iterator<Item> {
private final Iterator<Item> iterator = ItemSet.this.navSet.iterator();
private Item keyToRemove;
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Item next() {
keyToRemove = iterator.next();
return keyToRemove;
}
@Override
public void remove() {
iterator.remove();
ItemSet.this.keySet.remove(keyToRemove);
keyToRemove = null;
}
}
@Override
public Iterator<Item> iterator() {
return new ItemSetIterator();
}
@Override
public void clear() {
this.keySet.clear();
this.navSet.clear();
}
@Override
public boolean add(Item e) {
if (! this.keySet.add(e))
return false; // item already in set
if (! this.navSet.add(e))
throw new IllegalStateException("Internal state is corrupt");
return true;
}
@Override
public boolean remove(Object o) {
if (! this.keySet.remove(o))
return false; // item not in set
if (! this.navSet.remove(o))
throw new IllegalStateException("Internal state is corrupt");
return true;
}
@Override
public boolean addAll(Collection<? extends Item> c) {
boolean changed = false;
for (Item item : c)
if (add(item))
changed = true;
return changed;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean changed = false;
for (Object o : c)
if (remove(o))
changed = true;
return changed;
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Item pollFirst() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Item pollLast() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public NavigableSet<Item> descendingSet() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Iterator<Item> descendingIterator() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public SortedSet<Item> headSet(Item toElement) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public NavigableSet<Item> headSet(Item toElement, boolean inclusive) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public SortedSet<Item> tailSet(Item fromElement) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public NavigableSet<Item> tailSet(Item fromElement, boolean inclusive) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public SortedSet<Item> subSet(Item fromElement, Item toElement) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public NavigableSet<Item> subSet(Item fromElement, boolean fromInclusive, Item toElement, boolean toInclusive) {
throw new UnsupportedOperationException("Not yet implemented");
}
}
我正在寻找一个 Java 集合,可能在标准库中,它能够收集以下结构:
class Item {
String key;
double score;
}
并具有以下属性:
- 只允许一个具有相同键的项目(如一组)
- 插入、删除、检查是否存在,最大 O(logn)
- 按分数排序的遍历,在最大 O(logn) 中找到下一个
据我所知,标准 OrderedSet 必须具有与 equals() 接口一致的可比较接口,但我的情况并非如此,因为具有不同键的两个项目可能具有相同的分数。
事实上,我注意到 TreeSet 使用返回 0 的比较器来检查项目是否已经存在。
有什么建议吗?
我认为不存在这样的结构。您没有指定遍历性能要求,因此您可以使用普通 Set 并将值添加到列表中,然后按分数对该列表进行排序以进行遍历。
Only one Item with the same key is allowed (like a set)
您的 Item
class 应该仅使用 key
属性实现 hashCode() 和 equals()。
insert, remove, check existance in constant time
TreeSet
add() 和 remove() 是 O(ln N),因此它们不符合您的条件。
HashSet
add() 和 remove() 通常是 O(1)。
traversal ordered by score
您的性能要求是什么?您将多久遍历一次集合?如果您主要是添加和删除项目而很少遍历它,那么您可以在遍历操作期间将 HashSet
复制到 TreeSet
。
HashSet 不保证其元素的任何顺序。如果您需要此保证,请考虑使用 TreeSet 来保存您的元素
但为了通过关键实现独特性并保持恒定时间覆盖 hashCode()
和 equals()
有效地满足您的需求,如下所示:
class Item {
String key;
double score;
public Item(String key, double score) {
this.key = key;
this.score = score;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item item = (Item) o;
return key.equals(item.key);
}
@Override
public int hashCode() {
return Objects.hash(key);
}
@Override
public String toString() {
return "Item{" +
"key='" + key + '\'' +
", score=" + score +
'}';
}
}
// main
public static void main(String[] args) {
Set<Item> itemSet = new HashSet<>();
itemSet.add(new Item("1", 1));
itemSet.add(new Item("1", 2));
itemSet.add(new Item("2", 1));
//to get a sorted TreeSet
//Add all your objects to the TreeSet, you will get a sorted Set.
//TreeSet myTreeSet = new TreeSet();
//myTreeSet.addAll(itemSet);
//System.out.println(myTreeSet);
}
输出:
Item{key='1', score=1.0}
Item{key='2', score=1.0}
感谢那些用他们的评论和回答让我思考的人。我相信我们可以通过使用来达到要求:
TreeMap<Double, HashSet<Item>>
只是因为(我没说过)两个相同的键产生相同的分数;但更一般地说,有两个集合映射就足够了:一个(有序)以排序字段为键,另一个(未排序)以唯一字段为键。
现在插入已经放宽到 O(log n),你可以用双集来做到这一点,即实现你自己的集,在幕后维护 2 个集.
最好是您可以修改 class Item
以实现 equals()
和 hashCode()
以仅使用 key
字段。在这种情况下,您的 class 将使用 HashSet
和 TreeSet
。如果 hashCode()
涵盖的不仅仅是 key
字段,则使用两个 TreeSet
对象。
final class ItemSet implements NavigableSet<Item> {
private final Set<Item> keySet = new HashSet<>();
// or: new TreeSet<>(Comparator.comparing(Item::getKey));
private final TreeSet<Item> navSet = new TreeSet<>(Comparator.comparingDouble(Item::getScore)
.thenComparing(Item::getKey));
//
// Methods delegating to keySet for unique key access and for unordered access
//
@Override public boolean contains(Object o) { return this.keySet.contains(o); }
@Override public boolean containsAll(Collection<?> c) { return this.keySet.containsAll(c); }
@Override public int size() { return this.keySet.size(); }
@Override public boolean isEmpty() { return this.keySet.isEmpty(); }
//
// Methods delegating to navSet for ordered access
//
@Override public Comparator<? super Item> comparator() { return this.navSet.comparator(); }
@Override public Object[] toArray() { return this.navSet.toArray(); }
@Override public <T> T[] toArray(T[] a) { return this.navSet.toArray(a); }
@Override public Item first() { return this.navSet.first(); }
@Override public Item last() { return this.navSet.last(); }
@Override public Item lower(Item e) { return this.navSet.lower(e); }
@Override public Item floor(Item e) { return this.navSet.floor(e); }
@Override public Item ceiling(Item e) { return this.navSet.ceiling(e); }
@Override public Item higher(Item e) { return this.navSet.higher(e); }
//
// Methods delegating to both keySet and navSet for mutation of this set
//
private final class ItemSetIterator implements Iterator<Item> {
private final Iterator<Item> iterator = ItemSet.this.navSet.iterator();
private Item keyToRemove;
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Item next() {
keyToRemove = iterator.next();
return keyToRemove;
}
@Override
public void remove() {
iterator.remove();
ItemSet.this.keySet.remove(keyToRemove);
keyToRemove = null;
}
}
@Override
public Iterator<Item> iterator() {
return new ItemSetIterator();
}
@Override
public void clear() {
this.keySet.clear();
this.navSet.clear();
}
@Override
public boolean add(Item e) {
if (! this.keySet.add(e))
return false; // item already in set
if (! this.navSet.add(e))
throw new IllegalStateException("Internal state is corrupt");
return true;
}
@Override
public boolean remove(Object o) {
if (! this.keySet.remove(o))
return false; // item not in set
if (! this.navSet.remove(o))
throw new IllegalStateException("Internal state is corrupt");
return true;
}
@Override
public boolean addAll(Collection<? extends Item> c) {
boolean changed = false;
for (Item item : c)
if (add(item))
changed = true;
return changed;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean changed = false;
for (Object o : c)
if (remove(o))
changed = true;
return changed;
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Item pollFirst() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Item pollLast() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public NavigableSet<Item> descendingSet() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Iterator<Item> descendingIterator() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public SortedSet<Item> headSet(Item toElement) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public NavigableSet<Item> headSet(Item toElement, boolean inclusive) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public SortedSet<Item> tailSet(Item fromElement) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public NavigableSet<Item> tailSet(Item fromElement, boolean inclusive) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public SortedSet<Item> subSet(Item fromElement, Item toElement) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public NavigableSet<Item> subSet(Item fromElement, boolean fromInclusive, Item toElement, boolean toInclusive) {
throw new UnsupportedOperationException("Not yet implemented");
}
}