为什么Java的TreeSet没有get()方法?

34

如果我想检索和更新存储在TreeSet中的对象怎么办?

之所以这样问,是因为我想能够维护一些数据结构来存储学生。我希望它可以按照成绩排序(这是Student的一个实例变量),并且即使在我更新一个或多个成绩后,它仍然需要保持排序。

因此,在简要查看Java集合后,我决定使用TreeSet,并设置一个比较器,通过他们的成绩来比较两个学生。问题是,我刚发现TreeSet没有get()方法!

任何帮助和建议将不胜感激。

11个回答

30
你会期望在一个Set上的get()方法做什么?
- Set没有索引,所以get(int index)不起作用。(如果你想通过索引获取元素,请使用List)。 - get(Object obj)也不合理,因为你已经有了你要获取的对象。 - 已经有一个contains()方法来检查一个Set是否包含一个对象。 - 如果你想对集合中的所有元素执行某些操作,可以遍历Set。

2
正是我想写的:+1。不过对于索引的情况,我会写“使用List”而不是“毫无意义”。 - piet.t
或者使用 Map 根据某个键来存储对象。 - Rohit Jain
36
我不同意第二点。如果对象的比较方法只比较内容而不比较引用,那么get(Object obj)是有用的。那么如果我想要获取Set中元素的引用呢? - cloud
2
我认同在SortedSet中使用get()是有意义的。 - bryant1410
3
如果我有一个TreeSet,它已经排好序了,但是它没有get()方法,那该怎么办呢? - Z.better
显示剩余12条评论

15

你可以使用迭代器从TreeSet中检索元素。你可以尝试像这样:

Iterator<Integer> it = treeSet.iterator();

Integer current = 0;
while(it.hasNext() ) {
current = it.next();

}
希望这有所帮助。

4
我有一个案例,使用了两个TreeSet(因为它们在搜索方面更快)。其中一个树非常大,并且树中的对象是不同的,所以我创建了一个模拟对象(第二个树的类型为2),并使用来自小树中对象的数据来排序字段,检查是否有匹配项。现在我需要检查在第二个树中找到的对象的值,以便在报告中添加价值。
使用迭代器而不是二分搜索来检索所需的对象会破坏使用二叉树的目的。第二个树的大小超过5GB,查找与第一个树(200MB)中的数据匹配的项。我需要一种对于这么大量的数据有意义的搜索策略,因此我选择了二叉搜索树。条目是唯一的。

3
通常情况下,当你已经拥有一个集合中的元素时,你不需要再去检索它。你可能需要从集合中删除该元素或者判断它是否属于集合。现在你想要按照年级对学生进行索引,这样索引就是年级,而不是对象本身。使用 Map 是解决方案。
如果我是你,我会使用以下结构来快速检索所有年级相同的学生(他们也按年级排序):
private SortedMap<Integer,Set<Student>> _studentsByGrade = new TreeMap<Integer,Set<Student>>();

public void updateStudent(Student student, int oldGrade, int newGrade)
{
  getOrCreateContainer(oldGrade).remove(student);
  getOrCreateContainer(newGrade).add(student);
  student.setGrade(newGrade);
}

public Set<Student> getOrCreateContainer(int grade)
{
  Set<Student> set = _studentsByGrade.get(grade);
  if(set==null)
  {
    set = new HashSet<Student>();
    _studentsByGrade.put(grade, set);
  }
  return set;
}

不要忘记在你的学生类中重载equals和hashcode方法以确保其正确工作。

如果您想轻松快速地进行Java索引,请检查cqengine库,但上面介绍的解决方案对您的使用也是可以的。


3
确实没有按引用或按索引获取的方法。但是,有一种简单的方法可以构建按引用获取的方法。 这可以通过使用方法boolean contains(E input)E ceiling(E input)来实现。实际上,根据ceiling方法的Javadoc:

返回此集合中大于或等于给定元素的最小元素;如果没有这样的元素,则返回{@code null}。

因此,如果我们预先知道元素在集合中,则对ceiling的调用保证返回等于输入的元素。

import java.util.Comparator;
import java.util.TreeSet;

public class ExtendedTreeSet<E> extends TreeSet<E> {

  public ExtendedTreeSet(Comparator<? super E> comparator) {
    super(comparator);
  }

  public E get(E input) {
    return this.contains(input) ? this.ceiling(input) : null;
  }
}

2
TreeSet在插入时进行排序。如果按学生的成绩排序并在添加后进行修改,则项目不再排序(与之前相同的顺序)。 TreeSet还不使用equals()来确定元素是否已经添加,而是使用比较器(相同的顺序=相同的项)。因此,如果两个学生有相同的成绩,则只添加其中一个。从Javadoc中可以看出:

TreeSet实例使用其compareTo(或compare)方法执行所有元素比较,因此通过此方法判断为相等的两个元素在集合的角度上是相等的。

您可以使用HashSet,并且每当需要时按成绩对学生进行排序(创建一个新列表包含学生,对其进行排序并迭代)。

基本上没有实际的方法可以动态地完成它。 - so.very.tired
1
至少不要使用标准集合。只要排序可以是任意的(任何比较器或可比较实现),集合就需要一种方法来观察包含的对象是否发生变化。这将需要两个接口,一个用于可观察对象(由对象实现),另一个用于观察者(带有回调方法的监听器接口,用于观察者,即集合)。 - Peter Walser

1

这是我为自己找到的问题答案,但我认为集合应该有一个get(elem)方法,但正如你所知道的那样,实际上并没有

给你:

set.subSet(elem,true,elem,true).floor(elem);

这将返回与您要查找的对象相等的第一个对象注意elem必须与您要查找的元素相等,您将获得所需的对象或者为集合分配一个比较器以将它们匹配为equals

我很惊讶之前没有人想到过这个。

需要点赞 :D


1
如果包含完全相同的对象,楼层将返回您正在寻找的确切对象。
if(set.contains(searchingObject)) {
   addonPartNumber =  p.floor(searchingObject);
}

1
你可以迭代树来检索其对象。 那NavigableSet呢?它有短距离导航的方法,如
E ceiling(E e) E floor(E e)
E higher(E e) E lower(E e)

0

你也可以使用 for-each 循环来获取 TreeSet 中的所有元素。

TreeSet<String> words = new TreeSet<String>();
for(String w : words) {
    System.out.println(w);
}

你可以执行迭代操作将 TreeSet 中的唯一单词复制到列表中,这样你就可以使用 get(); 了。
希望能帮到你。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接