Объединение списка кортежей временного диапазона с перекрывающимися временными диапазонами

Я не думаю, что здесь есть что добавить, большинство ответов прекрасно объясняют различия между статическим вложенным классом и внутренними классами. Однако рассмотрите следующую проблему при использовании вложенных классов и внутренних классов. Как упоминание в нескольких ответах, внутренние классы не могут быть созданы без экземпляра и их вмещающего класса, что означает, что они удерживают указатель на экземпляр их вмещающего класса, что может привести к переполнению памяти или исключению переполнения стека из-за того, что GC не смогут мусор собирать окружающие классы, даже если они больше не используются. Чтобы сделать это, проверьте следующий код:

public class Outer {


    public  class Inner {

    }


    public Inner inner(){
        return new Inner();
    }

    @Override
    protected void finalize() throws Throwable {
    // as you know finalize is called by the garbage collector due to destroying an object instance
        System.out.println("I am destroyed !");
    }
}


public static void main(String arg[]) {

    Outer outer = new Outer();
    Outer.Inner inner = outer.new Inner();

    // out instance is no more used and should be garbage collected !!!
    // However this will not happen as inner instance is still alive i.e used, not null !
    // and outer will be kept in memory until inner is destroyed
    outer = null;

    //
    // inner = null;

    //kick out garbage collector
    System.gc();

}

Если вы удалите комментарий на // inner = null;, программа выйдет «Я уничтожен!», Но если оставить комментарий, это не будет. Причина в том, что белый внутренний экземпляр по-прежнему ссылается, GC не может его собрать и потому что он ссылается (имеет указатель на) внешний экземпляр, который он также не собирает. Наличие достаточного количества этих объектов в вашем проекте и может закончиться нехваткой памяти. По сравнению со статическими внутренними классами, которые не содержат точку для внутреннего экземпляра класса, поскольку он не связан с экземпляром, а связан с классом. Вышеупомянутая программа может печатать «Я уничтожен!» если вы станете классом Inner static и инстанцируются с помощью Outer.Inner i = new Outer.Inner();

29
задан Praveen Gollakota 15 April 2011 в 16:21
поделиться

5 ответов

Несколько способов сделать его более эффективным, Pythonic:

  1. Устранить конструкцию set(), так как алгоритм должен исключать дубликаты во время основного цикла.
  2. Если вам просто нужно перебрать результаты, используйте yield для генерации значений.
  3. Сократите построение промежуточных объектов, например: переместите вызов tuple() в точку, где создаются окончательные значения, избавляя вас от необходимости создавать и выбрасывать дополнительные кортежи, и повторно использовать список saved для хранения текущий временной диапазон для сравнения.

Код:

def merge(times):
    saved = list(times[0])
    for st, en in sorted([sorted(t) for t in times]):
        if st <= saved[1]:
            saved[1] = max(saved[1], en)
        else:
            yield tuple(saved)
            saved[0] = st
            saved[1] = en
    yield tuple(saved)

data = [
    [(1, 5), (2, 4), (3, 6)],
    [(1, 3), (2, 4), (5, 8)]
    ]

for times in data:
    print list(merge(times))
14
ответ дан samplebias 15 April 2011 в 16:21
поделиться

Поздно, но может помочь кому-то, ищущему это. У меня была похожая проблема, но со словарями. Учитывая список временных диапазонов, я хотел найти перекрытия и объединить их, когда это возможно. Небольшая модификация ответа @samplebias привела меня к этому:

Функция слияния:

def merge_range(ranges: list, start_key: str, end_key: str):
    ranges = sorted(ranges, key=lambda x: x[start_key])
    saved = dict(ranges[0])

    for range_set in sorted(ranges, key=lambda x: x[start_key]):
        if range_set[start_key] <= saved[end_key]:
            saved[end_key] = max(saved[end_key], range_set[end_key])
        else:
            yield dict(saved)
            saved[start_key] = range_set[start_key]
            saved[end_key] = range_set[end_key]
    yield dict(saved)

Данные:

data = [
    {'start_time': '09:00:00', 'end_time': '11:30:00'},
    {'start_time': '15:00:00', 'end_time': '15:30:00'},
    {'start_time': '11:00:00', 'end_time': '14:30:00'},
    {'start_time': '09:30:00', 'end_time': '14:00:00'}
]

Исполнение:

print(list(merge_range(ranges=data, start_key='start_time', end_key='end_time')))

Вывод:

[
    {'start_time': '09:00:00', 'end_time': '14:30:00'},
    {'start_time': '15:00:00', 'end_time': '15:30:00'}
]
1
ответ дан Nirmal 15 April 2011 в 16:21
поделиться

Часть сортировки: используйте стандартную сортировку, она правильно сравнивает кортежи.

sorted_tuples = sorted(initial_ranges)

Часть слияния. Это также устраняет дублирующиеся диапазоны, поэтому нет необходимости в set. Предположим, у вас есть current_tuple и next_tuple.

c_start, c_end = current_tuple
n_start, n_end = next_tuple
if n_start <= c_end: 
  merged_tuple = min(c_start, n_start), max(c_end, n_end)

Надеюсь, логика достаточно ясна.

Для просмотра следующего кортежа вы можете использовать индексированный доступ к sorted tuples; в любом случае, это полностью известная последовательность.

1
ответ дан 9000 15 April 2011 в 16:21
поделиться

Сортируйте все границы, затем возьмите все пары, у которых за концом границы следует начало границы.

def mergeOverlapping(initialranges):
    def allBoundaries():
        for r in initialranges:
            yield r[0], True
            yield r[1], False

    def getBoundaries(boundaries):
        yield boundaries[0][0]
        for i in range(1, len(boundaries) - 1):
            if not boundaries[i][1] and boundaries[i + 1][1]:
                yield boundaries[i][0]
                yield boundaries[i + 1][0]
        yield boundaries[-1][0]

    return getBoundaries(sorted(allBoundaries()))

Хм, не так красиво, но по крайней мере было весело писать!

РЕДАКТИРОВАТЬ: Спустя годы, после взрыва, я понял, что мой код был неправильным! Это новая версия просто для удовольствия:

def mergeOverlapping(initialRanges):
    def allBoundaries():
        for r in initialRanges:
            yield r[0], -1
            yield r[1], 1

    def getBoundaries(boundaries):
        openrange = 0
        for value, boundary in boundaries:
            if not openrange:
                yield value
            openrange += boundary
            if not openrange:
                yield value

    def outputAsRanges(b):
        while b:
            yield (b.next(), b.next())

    return outputAsRanges(getBoundaries(sorted(allBoundaries())))

По сути, я отмечаю границы с помощью -1 или 1, а затем сортирую их по значению и выводу только тогда, когда баланс между открытыми и закрытыми скобками равен нулю.

1
ответ дан UncleZeiv 15 April 2011 в 16:21
поделиться

Сортировать кортежи, затем перечислить, если t1.right> = t2.left => объединить и перезапустить с новым списком, ...

->

def f(l, sort = True):
    if sort:
        sl = sorted(tuple(sorted(i)) for i in l)
    else:
        sl = l
    if len(sl) > 1:
        if sl[0][1] >= sl[1][0]:
            sl[0] = (sl[0][0], sl[1][1])
            del sl[1]
            if len(sl) < len(l):
                return f(sl, False)
    return sl
2
ответ дан manji 15 April 2011 в 16:21
поделиться
Другие вопросы по тегам:

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