Как получить элемент от набора, не удаляя его?

NullPointerException s - исключения, возникающие при попытке использовать ссылку, которая указывает на отсутствие местоположения в памяти (null), как если бы она ссылалась на объект. Вызов метода по нулевой ссылке или попытка получить доступ к полю нулевой ссылки вызовет функцию NullPointerException. Они наиболее распространены, но другие способы перечислены на странице NullPointerException javadoc.

Вероятно, самый быстрый пример кода, который я мог бы придумать для иллюстрации NullPointerException, be:

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

В первой строке внутри main я явно устанавливаю ссылку Object obj равной null. Это означает, что у меня есть ссылка, но она не указывает на какой-либо объект. После этого я пытаюсь обработать ссылку так, как если бы она указывала на объект, вызывая метод на нем. Это приводит к NullPointerException, потому что нет кода для выполнения в местоположении, на которое указывает ссылка.

(Это техничность, но я думаю, что она упоминает: ссылка, которая указывает на null, равна 't то же, что и указатель C, указывающий на недопустимую ячейку памяти. Нулевой указатель буквально не указывает на в любом месте , который отличается от указаний на местоположение, которое оказывается недопустимым.)

379
задан MSeifert 19 February 2018 в 12:22
поделиться

6 ответов

Две опции, которые не требуют копирования полного набора:

for e in s:
    break
# e is now an element from s

Или...

e = next(iter(s))

, Но в целом, наборы не поддерживают индексацию или разрезание.

480
ответ дан Raymond Hettinger 22 November 2019 в 23:57
поделиться

Наименьшее количество кода было бы:

>>> s = set([1, 2, 3])
>>> list(s)[0]
1

, Очевидно, это создало бы новый список, который содержит каждого члена набора, таким образом, не большой, если Ваш набор является очень большим.

91
ответ дан John 22 November 2019 в 23:57
поделиться

Так как Вы хотите случайный элемент, это будет также работать:

>>> import random
>>> s = set([1,2,3])
>>> random.sample(s, 1)
[2]

документация, кажется, не упоминает производительность random.sample. От действительно быстрого эмпирического теста с огромным списком и огромным набором, это, кажется, постоянное время для списка, но не для набора. Кроме того, повторение по набору не случайно; порядок не определен, но предсказуем:

>>> list(set(range(10))) == range(10)
True 

, Если бы случайность важна и Вам нужен набор элементов в постоянное время (большие наборы), я использовал бы random.sample и преобразовал бы в список сначала:

>>> lst = list(s) # once, O(len(s))?
...
>>> e = random.sample(lst, 1)[0] # constant time
28
ответ дан dF. 22 November 2019 в 23:57
поделиться

Я использую служебную функцию, которую я записал. Его имя является несколько вводящим в заблуждение, потому что это отчасти подразумевает, что мог бы быть случайный объект или что-то как этот.

def anyitem(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None
6
ответ дан Nick 22 November 2019 в 23:57
поделиться

Другая опция состоит в том, чтобы использовать словарь со значениями, о которых Вы не заботитесь. Например,


poor_man_set = {}
poor_man_set[1] = None
poor_man_set[2] = None
poor_man_set[3] = None
...

можно рассматривать ключи как набор за исключением того, что они - просто массив:


keys = poor_man_set.keys()
print "Some key = %s" % keys[0]

побочный эффект А этого выбора состоит в том, что Ваш код будет назад совместим с более старым, пред - set версии Python. Это - возможно, не лучший ответ, но это - другая опция.

Редактирование: можно даже сделать что-то вроде этого для сокрытия того, что Вы использовали dict вместо массива или установили:


poor_man_set = {}
poor_man_set[1] = None
poor_man_set[2] = None
poor_man_set[3] = None
poor_man_set = poor_man_set.keys()
-2
ответ дан Pat Notz 22 November 2019 в 23:57
поделиться

Чтобы представить некоторые временные характеристики различных подходов, рассмотрим следующий код. get () - это мое специальное дополнение к Python setobject.c, которое является просто pop () без удаления элемента.

from timeit import *

stats = ["for i in xrange(1000): iter(s).next()   ",
         "for i in xrange(1000): \n\tfor x in s: \n\t\tbreak",
         "for i in xrange(1000): s.add(s.pop())   ",
         "for i in xrange(1000): s.get()          "]

for stat in stats:
    t = Timer(stat, setup="s=set(range(100))")
    try:
        print "Time for %s:\t %f"%(stat, t.timeit(number=1000))
    except:
        t.print_exc()

Результат:

$ ./test_get.py
Time for for i in xrange(1000): iter(s).next()   :       0.433080
Time for for i in xrange(1000):
        for x in s:
                break:   0.148695
Time for for i in xrange(1000): s.add(s.pop())   :       0.317418
Time for for i in xrange(1000): s.get()          :       0.146673

Это означает, что для / break решение является самым быстрым (иногда быстрее, чем пользовательское решение get ()).

38
ответ дан 22 November 2019 в 23:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: