Как получить индекс данного элемента LinkedHashSet без итерации?
Возможно ли это?
Скажите, что у вас
private Set<String> names = new LinkedHashSet<String>();
и Strings
- "Майк", "Джон", "Карен".
Можно ли получить "1" в ответ на "какой индекс" Джон "без итерации?
Следующее работает отлично.. с этим вопросом я задаюсь вопросом, есть ли лучший способ
for (String s : names) {
++i;
if (s.equals(someRandomInputString)) {
break;
}
}
Ответы
Ответ 1
Set
interface не имеет ничего похожего на метод indexOf()
. Вам действительно нужно перебрать его или использовать List
interface, который предлагает indexOf()
.
Если вы хотите, преобразование Set
в List
довольно тривиально, это должно быть передачей Set
через конструктор реализации List
. Например.
List<String> nameList = new ArrayList<String>(nameSet);
// ...
Ответ 2
Я так не верю, но вы можете создать класс оболочки LinkedHashSetWithIndex
, который будет выполнять итерацию для вас, или сохранить отдельную таблицу с индексами каждой записи, если снижение производительности будет приемлемым для вашего варианта использования.
Ответ 3
Хотя это не так эффективно для машины, это достигается в одной строке:
int index = new ArrayList<String>(names).indexOf("John");
Ответ 4
Как правило, невозможно, чтобы Set возвращал индекс, потому что он не обязательно четко определен для конкретной реализации Set. Например, он говорит в документации HashSet
Он не дает никаких гарантий относительно порядка итерации набора; в частности, он не гарантирует, что порядок будет оставаться постоянным с течением времени.
Итак, вы не должны говорить, что тип Set, когда то, что вы на самом деле ожидаете, это Set реализующий som-порядок.
Ответ 5
A Set
- это "коллекция, которая не содержит повторяющихся элементов", и не поддерживает порядок ее элементов. http://download.oracle.com/javase/6/docs/api/java/util/Set.html
Код, который вы указали, не обязательно будет возвращать 1 каждый раз. Итерация через Set
не гарантируется итерации в том же порядке каждый раз; только гарантировано, что он будет перебирать каждый элемент один раз.
Поскольку вы, похоже, заботитесь о порядке элементов, вам нужно использовать List
вместо Set
.
Ответ 6
Вот реализация, которая выполняет вставки, удаления, сохранения, поддерживаемые arraylist для достижения o (1) для get (index).
/**
* @Author Mo. Joseph
*
* Allows you to call get with o(1) instead of o(n) to get an instance by index
*/
public static final class $IndexLinkedHashSet<E> extends LinkedHashSet<E> {
private final ArrayList<E> list = new ArrayList<>();
public $IndexLinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
public $IndexLinkedHashSet() {
super();
}
public $IndexLinkedHashSet(int initialCapacity) {
super(initialCapacity);
}
public $IndexLinkedHashSet(Collection<? extends E> c) {
super(c);
}
@Override
public synchronized boolean add(E e) {
if ( super.add(e) ) {
return list.add(e);
}
return false;
}
@Override
public synchronized boolean remove(Object o) {
if ( super.remove(o) ) {
return list.remove(o);
}
return false;
}
@Override
public synchronized void clear() {
super.clear();
list.clear();
}
public synchronized E get(int index) {
return list.get(index);
}
@Override
public synchronized boolean removeAll(Collection<?> c) {
if ( super.removeAll(c) ) {
return list.removeAll(c);
}
return true;
}
@Override
public synchronized boolean retainAll(Collection<?> c) {
if ( super.retainAll(c) ) {
return list.retainAll(c);
}
return false;
}
/**
* Copied from super class
*/
@Override
public synchronized boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
}
Чтобы проверить это:
public static void main(String[] args) {
$IndexLinkedHashSet<String> abc = new $IndexLinkedHashSet<String>();
abc.add("8");
abc.add("8");
abc.add("8");
abc.add("2");
abc.add("3");
abc.add("4");
abc.add("1");
abc.add("5");
abc.add("8");
System.out.println("Size: " + abc.size());
int i = 0;
while ( i < abc.size()) {
System.out.println( abc.get(i) );
i++;
}
abc.remove("8");
abc.remove("5");
System.out.println("Size: " + abc.size());
i = 0;
while ( i < abc.size()) {
System.out.println( abc.get(i) );
i++;
}
abc.clear();
System.out.println("Size: " + abc.size());
i = 0;
while ( i < abc.size()) {
System.out.println( abc.get(i) );
i++;
}
}
Какие выходы:
Size: 6
8
2
3
4
1
5
Size: 4
2
3
4
1
Size: 0
Конечно, remove, removeAll, retainAll теперь имеет ту же или худшую производительность, что и ArrayList. Но я не использую их, и поэтому я в порядке.
Наслаждайтесь!
EDIT:
Вот другая реализация, которая не расширяет LinkedHashSet, потому что это избыточно. Вместо этого он использует HashSet и ArrayList.
/**
* @Author Mo. Joseph
*
* Allows you to call get with o(1) instead of o(n) to get an instance by index
*/
public static final class $IndexLinkedHashSet<E> implements Set<E> {
private final ArrayList<E> list = new ArrayList<>( );
private final HashSet<E> set = new HashSet<> ( );
public synchronized boolean add(E e) {
if ( set.add(e) ) {
return list.add(e);
}
return false;
}
public synchronized boolean remove(Object o) {
if ( set.remove(o) ) {
return list.remove(o);
}
return false;
}
@Override
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
public synchronized void clear() {
set.clear();
list.clear();
}
public synchronized E get(int index) {
return list.get(index);
}
public synchronized boolean removeAll(Collection<?> c) {
if ( set.removeAll(c) ) {
return list.removeAll(c);
}
return true;
}
public synchronized boolean retainAll(Collection<?> c) {
if ( set.retainAll(c) ) {
return list.retainAll(c);
}
return false;
}
public synchronized boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
@Override
public synchronized int size() {
return set.size();
}
@Override
public synchronized boolean isEmpty() {
return set.isEmpty();
}
@Override
public synchronized boolean contains(Object o) {
return set.contains(o);
}
@Override
public synchronized Iterator<E> iterator() {
return list.iterator();
}
@Override
public synchronized Object[] toArray() {
return list.toArray();
}
@Override
public synchronized <T> T[] toArray(T[] a) {
return list.toArray(a);
}
}
Теперь у вас есть две реализации, я бы предпочел вторую.
Ответ 7
Лучше всего нет, только одно выровненное (которое использует итератор, но неявно):
new ArrayList(names).get(0)