Я не думаю, что здесь есть что добавить, большинство ответов прекрасно объясняют различия между статическим вложенным классом и внутренними классами. Однако рассмотрите следующую проблему при использовании вложенных классов и внутренних классов. Как упоминание в нескольких ответах, внутренние классы не могут быть созданы без экземпляра и их вмещающего класса, что означает, что они удерживают указатель на экземпляр их вмещающего класса, что может привести к переполнению памяти или исключению переполнения стека из-за того, что 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();
Несколько способов сделать его более эффективным, Pythonic:
set()
, так как алгоритм должен исключать дубликаты во время основного цикла. yield
для генерации значений. 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))
Поздно, но может помочь кому-то, ищущему это. У меня была похожая проблема, но со словарями. Учитывая список временных диапазонов, я хотел найти перекрытия и объединить их, когда это возможно. Небольшая модификация ответа @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'}
]
Часть сортировки: используйте стандартную сортировку, она правильно сравнивает кортежи.
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
; в любом случае, это полностью известная последовательность.
Сортируйте все границы, затем возьмите все пары, у которых за концом границы следует начало границы.
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, а затем сортирую их по значению и выводу только тогда, когда баланс между открытыми и закрытыми скобками равен нулю.
Сортировать кортежи, затем перечислить, если 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