Жадты басқару: тізімді дұрыс қалпына келтіру әдісі

Пайдаланылған еске алу және қоқыс жинаушыға әсер ету үшін, осы екі жүзеге асырудың арасындағы айырмашылық бар ма екенін білгім келеді:

protected List _data = new ArrayList();

// I want to reset this list using another one. First try :
public void set(List newData) {
    _data = newData;
}

// I want to reset this list using another one. Second try :
public void set(List newData) {
    _data.clear();
    _data.addAll(newData);
}

Сонымен қатар, функционалдық айырмашылық бар болса, маған айтыңыз!

6
Бірінші жағдайда сіз Тізім данасын көшіріп отырсыз. Екінші жағдайда, сіз Тізімнің мазмұнының ұсақ көшірмесін жасайсыз. Біріншісі ішкі қоңырау шалушылар үшін, сіз сенім артатын адамдар үшін жақсы. Қоңырау шалушыға сенімсіз болсаңыз, Тізімнің мазмұнының терең көшірмесін жасағыңыз келеді.
қосылды автор Gilbert Le Blanc, көзі
Сіз GC туралы алаңдамаңыз. Ол не істейтінін біледі.
қосылды автор Maroun, көзі
Тек түсінікті болу үшін: Мен бұл код бөлігін оңтайландыруға көңіл бөлмеймін, бірақ екінші жағынан, осы код бөлімдерін таңдаудың критерийлері қажет. Мен өнімділіктің біреуі болуы мүмкін деп ойладым, бірақ «қауіпсіздік» деректері одан да жақсы. Сондықтан қабылданған жауап :)
қосылды автор C4stor, көзі

6 жауаптар

Бірінші мысал, ескі нысанды жаңадан ауыстырады. Егер сіз кез-келген жолмен жаңа тізімді жасасаңыз, бұл жақсы. Егер сіздің жинағыңыз() тізімнің көшірмесін алуы керек, себебі ол қоңырау шалушы арқылы өзгеруі мүмкін, екінші мысал қарастыруға тұрарлық болуы мүмкін.

Екінші мысал Тізімді қайта пайдаланады және бұл мүмкін

  • көшірме жасауды үнемдейді.
  • нашар, себебі көптеген объектілерге сілтеме жасайтын нысанға ие бола аласыз. Егер бұл жеткілікті болса, ол GC уақытын баяулатады.

Жалпы алғанда, сіз ең қарапайым шешім деп санайтын нәрселерді орындауыңыз керек және қолданбаңызды өлшегенде өнімділік туралы алаңдамаңыз. жад профилі бар. және сізде мәселе бар екенін айтады. Егер сіз өлшерсеңіз, сіз дәл болжай аласыз.

AFAIK, ешқандай Java JVM жадты басқару үшін сілтеме санауды қолдайды. Егер ол стандартта тыйым салынбаса, бірақ оны Java-де қолдануды қарастыратын тым көп мәселе бар. мысалы, айналмалы сілтемелер. Қызықты факт, C ++ -тегі ақылды көрсеткіштері сілтеме санауды қолданады :(

3
қосылды

Алғашқыда тек қана қоңырау шалушы ұсынған тізімге сілтеме жасайды. Егер ескі тізімге кез келген жерден қол жеткізе алмасаңыз, ол GC үшін жарамды.

Екіншісі объектінің бар тізімін қолданады және оған қоңырау шалушы тізімінің барлық тармақ сілтемелерін көшіреді. Егер бұл жинақтың өлшемін көбейтсе, онда ол үлкен массивтерді ішіне бөлуді (үлкен массивтерді жинауды) қамтуы мүмкін.

Элементтердің өздігінен жетуі өзгермейді; екі нұсқасы тізімдер дәл сол элементтерге сілтеме жасайды.

Біріншісі әдетте біршама тезірек жылдамырақ, өйткені сіз бір ғана сілтеме ауыстырасыз. Бұл тізімді басқа тізімге көшіруден гөрі аз жұмыс.

Екіншіден, инкапсуляция тұрғысынан жақсы. Елестетіп көріңізші ...

yourThingie.set(myItems);
myItems.add(objectYouNeverWouldHaveAllowed);

Бірінші нұсқада _data </​​code> енді objectYouNeverWouldHaveAllowed . Сіз өзіңіздің сыныбыңыздың шектеулерін _data </​​code> -қа енгізуіңіз мүмкін емес; себебі қоңырау шалушы өздерінің тізімін өзіңізге әкеліп салды, енді олар сіздің нысаныңыздың ішкі жағдайын бақылайды. Олар сіздің объектіңізді қашықтықтан, тіпті кездейсоқ түрде бұзуы мүмкін.

Екіншіде мұндай проблема жоқ. Сіз _data </​​code> басқару элементін сақтайсыз, қоңырау шалушы олардың тізіміне кейінгі өзгерістер сізге әсер етпейді және сіз бірдеңе бұзылған жағдайда (жалаңаш объектті шығаратын гетераны беру сияқты) қауіпсіз.

3
қосылды
@Pete: Бұл өте оңай, шынымен. Егер сіздің нәрсе өзгеруі мүмкін болса, тұтастық сізден өзіңіздің жеке көшірмеңізді талап етеді. Осылайша, иә, 10000 өзгермелі нүктенің тізімі әрбір нүктеге клондалған қажет. Мұндай нәрсе - бұл қажетсіз дыбыссыздықты жасайтын нәрсе және өзгермейтін «нүкте» түрінің пайдасына дәлел. Егер C ++-те өнімділікке көңіл бөлсеңіз - бұл клондалған заттар өзгермейтін болса да, әдепкі бойынша терең клондар. Бұған жол бермеу үшін көшіру конструкторын және/немесе сілтемелерді немесе көрсеткішті (немесе shared_ptr ) шынымен асыра пайдалануыңыз керек.
қосылды автор cHao, көзі
@Pete: s/constructor/copy конструкторы /. Объект «тривиально көшірілетін» болғанша, көшіру конструкторы іске қосылмайды. (C ++ мүмкін емес көшірме құрастырушысын қолдануға болмайды, егер ол объектілерді көшіруді білсе, бірақ көптеген компиляторлар оның орнына битальды көшірмені жасайды). «Тривиально көшірілетін» анықтамасы тұрақты конструктор болса да, тек қана көшіру/жылжыту конструкторлары дисквалификацияланады.
қосылды автор cHao, көзі
@Pete: Нүктеге оралу ... Java барлық жағдайларда де істей алады; ол әдеттегі емес істі өңдеу үшін кодты қосқаныңыз жөн (C ++ сияқты). Әдепкі бойынша, әр түрлі. Сілтеме (сәйкестік) семантикасы Java-де әдепкі болып табылады, ал мағыналық семантикасы C ++-де әдепкі болып табылады.
қосылды автор cHao, көзі
@Pete: Жағдайға байланысты. Жалғыз әмбебап әдіс - сізге қажеті жоқ заттарды жасаудан аулақ болу. Егер бұл опция болмаса, онда жағдайға бейімделген үлгі тәртіпте болуы мүмкін. Бәлкім, сізде көптеген ұпайларды білдіретін сынып сияқты нәрсе болуы мүмкін. Бір масштабты инсенса (жинақтың екі еселенген мөлшері) деректерді қамтуы мүмкін, ал нысан клондау бұл массаның клондауын білдіреді.
қосылды автор cHao, көзі
@Pete: Жалпы? Онша емес. java.io сияқты жағдайларда, шын мәнінде, менің ойымша, деректерді толтырғыңыз келетін өзгермейтін буферіңіз бар. Дегенмен, «ерекше ОО» емес, ол тек ОО-дағы өзгеше. Сізде әлі де инкапсулаланған нысандар жиынтығы бар; олар тек басқаша ұйымдастырылған. Жинақ сілтемесі мен индексі бар қарапайым заттармен қамтамасыз ету өте маңызды және әрбір элементті инкапсулау керек. Олар тіпті клондау кезінде көшіруді қажет етпейді; олар бұрын болмаған жағдайда, ұшақта құрастырылуы мүмкін еді.
қосылды автор cHao, көзі
Бұл өте жақсы нәрсе! Мен кейінірек кіргізу мүмкіндігі туралы ойламаған едім! Бұл шын мәнінде функционалдық айырмашылықтың бір түрі.
қосылды автор C4stor, көзі
Екінші нұсқа, сондай-ақ, әр элементтің сыртқы агентке өзгертілуі керек-болмайтындығына байланысты (егер тізімде «int» сияқты кірістірілген мәндер болмаса) қауіптенеді. Сонда сіз «тізімнің барлық элементтері клондалған болуы керек пе?» Деген сұраққа ие боласыз. Егер сізде 10000 нүктенің тізімі бар болса - сіз тізімді сақтау орнына қосымша жинақта әрбір нүктенің клонын бөлуге тура келеді. Мен Java-ді C ++-қа қарағанда қауіпсіз етуі керек деп ойладым, бірақ оны бірнеше апта бойы пайдаланғаннан кейін, ол мені көбірек қорқытты.
қосылды автор Pete, көзі
@cHao Толығымен келісемін - Java-дегі 'const' болмауы өте кінәсіз көрінетін код үшін инкапсуляцияның көптеген бұзылуына әкеп соқтырады. Кем дегенде, барлық жағдайларды C ++-де өңдеуге болады және егер кодты дұрыс жазсаңыз, нәтиже беретін интерфейс қоңырау шалушы абоненттік шектеулерді көрсетеді. Point векторын көшіру жағдайында, түпнұсқалық сыныбы пайдаланушы анықтаған конструктор болмаса, терең көшірме 1 бөлу және 1 (үлкен жұмыс) мемкі. Java-де, ол кейбір кластер функцияларына 10001 бөлу және 10000 функционалдық қоңырау.
қосылды автор Pete, көзі
@cHao Сіз бұл GC-тің барлық мүмкіндіктерінен аулақ боласыз - бұл мәселе шектеулі құрылғыларға арналған мәселе туралы көп нәрсе?
қосылды автор Pete, көзі
@cHao иә Мен бұл шешім болар еді деп ойладым. Әсіресе, OO емес. Жалпы алғанда, Thing getThing (); орнына void getThing (Thing thing); сияқты қабылдайтындар жақсы болады; инкапсуляция бұзылуын болдырмау үшін және қажетсіз GC параметрі Java-де жалпы идома ма?
қосылды автор Pete, көзі

Негізгі айырмашылықтар:

Іске асыру тізімі:
Ауыстырылған List ArrayList болмауы мүмкін, ол (мысалы) LinkedList болуы мүмкін. Тек өткен тізімді пайдалану сіздің тізіміңіздің элементтеріне олардың индексі арқылы тікелей кірсеңіз, өтініміңіздің жұмысына қатты әсер етуі мүмкін; ArrayList.get (i) - бұл O (1), бірақ LinkedList.get (i) - бұл O (n).

Деректердің тұтастығы:
Егер сіз бұл тізімді пайдалансаңыз, қоңырау шалушы оған сілтеме жасай алады және (кейінірек) сыныпты білмей-ақ, тізімін өзгертеді. Бұл сіздің өтініміңізде күтпеген мінез-құлықты тудыруы мүмкін. Мысалы, тізімдегі мазмұн туралы ақпаратты сақтауға болады, сонда тізімде жарамсыз болып, сыныптың білімісіз өзгеріп, деректердің сәйкессіздігіне әкеледі. Сондай-ақ, сіздің класс тізіміңізді өзгерте алады және клиент білмейді (әдетте), осылайша кері байланыс шындық болуы мүмкін - клиентте деректердің сәйкес келмеуі болуы мүмкін.

Жадтың ағуы: Тізімге сілтеме қоңырау шалушы ретінде де, сіздің сыныпта да болуы мүмкін. Бұл, сондай-ақ, күтілетін кезде қоқыс жинауға жатпайтын тізімге әкелуі мүмкін.

Осы себептерге байланысты (және, бәлкім, көбірек) ең жақсы тәжірибе көшіру тізімге жіберіледі, егер сізде және көшірмені жасамау жақсы түсіндіріледі, сондықтан барлық салдары бар екенін біледі.

0
қосылды

_data = newData; пайдаланған бірінші әдісті ғана қолданамын. _data - тізімге көрсеткіш. Осылайша, сіз жасай алатын нәрсені жасай отырып, сіз _Data-ге жаңа деректерді көрсетуге себеп болады. Өйткені кез келген _data бұрынғыдай көрсетілмесе, онда қоқыс жинаушы оны тастайды.

Сіздің екінші әдісіңіз өте қымбат, мен оны жасамас едім. Иә, ол бірдей нәрсеге қол жеткізеді, бірақ жай көрсеткішті ауыстырғанның орнына ол сізге көп шығынды есептейді.

0
қосылды

Біріншісі - бір анықтамалық тапсырма. Екіншіден, «newData» дегенден '_event' деген сілтемелер тізімін көшіріп алу керек, мүмкін, бұл процесте «_event» өлшемін өзгертіп, әрі көшіруді талап етеді.

БІРАҚ ... «Барлық зұлымдықтардың түбірі болса, мерзімсіз оңтайландыру». (Егер бұл қоңырауды шырқаса, оны іздеңіз!)

Көптеген жағдайларда семантикалық айырмашылығы маңызды. Алдымен сіз «set» -ге берілген деректерге ғана сілтеме жасайсыз - егер қоңырау коды оны кейінірек өзгерткен болса, ол «_events» де өзгереді (өйткені, «_events 'IS' newData '). Бұл қауіпті емес жанама әсерлерге әкелуі мүмкін, егер бұл мақсатқа сай келмесе.

Екіншіден, 'newData' ішіндегі кейінгі өзгерістер '_events' үшін өзгеріс ретінде көрсетілмейді, себебі '_events' - жаңа деректердің көшірмесі. Сіздің дұрыс қолданысыңыздың семантикасы дұрыс. Егер сізде осы «жиынтыққа» сәйкес келетін «алуға» ие болсаңыз, ол кері жағдайда қолданылады.

Егер сіз тек қана '_events' мазмұнын жаңартып отыратын болсаңыз, онда ол нені білдіретінін өзгертпейсіз, оны ақырғы деп қабылдау керек.

Hmmm. Мен жай ғана мысалда '_data' бір рет айтылғанын байқадым. Жоғарыда айтылғандай, '_data' және '_events' - бұл бірдей нәрсе (яғни, бұл типо) - әйтпесе неге «_data» деп айтасыз?

0
қосылды

Құжаттарға сәйкес, бұл clear() :

public void clear() {
     modCount++;
     for (int i = 0; i < size; i++)
         elementData[i] = null;
      size = 0;
 }

Сондықтан, List жаңа тезірек (тізім өлшемі тым үлкен болғанда) болуына сенімдімін, бірақ оны тиімді еске пайдалану жағдайында.

Кейде тиімділік жылдамдықты ғана қамтымайды.

0
қосылды