එකතුවක් හරහා අනුකරණය කිරීම, ලූපයක ඇති වස්තූන් ඉවත් කිරීමේදී සමගාමී විකරණයෙන් වැළකී සිටීම


1202

ඔබට පහත සඳහන් දේ කළ නොහැකි බව අපි කවුරුත් දනිමු ConcurrentModificationException:

for (Object i : l) {
    if (condition(i)) {
        l.remove(i);
    }
}

නමුත් මෙය සමහර විට ක්‍රියාත්මක වන නමුත් සෑම විටම නොවේ. මෙන්න විශේෂිත කේත කිහිපයක්:

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<>();

    for (int i = 0; i < 10; ++i) {
        l.add(4);
        l.add(5);
        l.add(6);
    }

    for (int i : l) {
        if (i == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

ඇත්ත වශයෙන්ම, මෙහි ප්‍රති results ලය වනුයේ:

Exception in thread "main" java.util.ConcurrentModificationException

බහු නූල් එය සිදු නොකලත්. කෙසේ හෝ වේවා.

මෙම ගැටලුවට හොඳම විසඳුම කුමක්ද? මෙම ව්‍යතිරේකය විසි නොකර මම ලූපයක එකතුවෙන් අයිතමයක් ඉවත් කරන්නේ කෙසේද?

මම ද Collectionමෙහි අත්තනෝමතික ලෙස භාවිතා කරමි , අවශ්‍යයෙන්ම නොවේ ArrayList, එබැවින් ඔබට විශ්වාසය තැබිය නොහැක get.


පා readers කයන්ට සටහන: docs.oracle.com/javase/tutorial/collections/interfaces/… කියවීමක් තිබේද , ඔබට කිරීමට අවශ්‍ය දේ සාක්ෂාත් කර ගැනීමට පහසු ක්‍රමයක් එයට තිබිය හැකිය.
GKFX

Answers:


1605

Iterator.remove() ආරක්ෂිතයි, ඔබට එය මේ ආකාරයෙන් භාවිතා කළ හැකිය:

List<String> list = new ArrayList<>();

// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
//     Iterator<String> iterator = list.iterator();
//     while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        // Remove the current element from the iterator and the list.
        iterator.remove();
    }
}

Iterator.remove()නැවත සැකසීමේදී එකතුවක් වෙනස් කිරීමට ඇති එකම ආරක්‍ෂිත ක්‍රමය එය බව සලකන්න ; පුනරාවර්තනය සිදුවන අතරතුර යටින් පවතින එකතුව වෙනත් ආකාරයකින් වෙනස් කර ඇත්නම් හැසිරීම නිශ්චිතව දක්වා නැත .

මුලාශ්‍රය: docs.oracle> එකතු කිරීමේ අතුරුමුහුණත


ඒ හා සමානව, ඔබට අයිතමයක් එකතු කිරීමටListIterator අවශ්‍ය නම් , ඔබට භාවිතා කළ හැකිය, ඔබට භාවිතා කළ හැකි එකම හේතුව නිසා  - එය නිර්මාණය කර ඇත්තේ එයට ඉඩ දීම සඳහා ය.ListIterator#addIterator#remove


ඔබේ නඩුව ඔබ ලැයිස්තුවෙන් ඉවත් කිරීමට උත්සාහ කළ නමුත් උත්සාහ නම් එම සීමා අදාළ putබවට Mapඑහි අන්තර්ගතය එල්ලාවල මහතා අතර.


19
වත්මන් පුනරාවර්තනයේදී ආපසු ලබා දුන් මූලද්‍රව්‍යය හැර වෙනත් මූලද්‍රව්‍යයක් ඉවත් කිරීමට ඔබට අවශ්‍ය නම් කුමක් කළ යුතුද?
ඉයුජන්

2
ඔබට itrerator තුළ .remove භාවිතා කළ යුතු අතර එය කළ හැක්කේ වත්මන් මූලද්‍රව්‍යය ඉවත් කිරීමට පමණි, එබැවින් නැත :)
බිල් කේ

1
සමගාමී ලින්ක්ඩෙක් හෝ කොපි ඕන්රයිට්ආරේලිස්ට් (අවම වශයෙන් මගේ කාරණයේදී) භාවිතා කිරීමට සාපේක්ෂව මෙය මන්දගාමී බව මතක තබා ගන්න
ඩෑන්

1
iterator.next()ඇමතුම for-loop තුළට දැමිය නොහැකිද? එසේ නොවේ නම්, එයට හේතුව පැහැදිලි කිරීමට යමෙකුට හැකිද?
බ්ලේක්

1
OnGonenI වෙනස් කළ නොහැකි එකතු කිරීම් වලින් එය නැවත ක්‍රියාත්මක කරන්නන් සඳහා ක්‍රියාත්මක වේ. List.addඑම අර්ථයෙන්ම "විකල්ප" වේ, නමුත් ලැයිස්තුවකට එකතු කිරීම "අනාරක්ෂිත" යැයි ඔබ නොකියනු ඇත.
රේඩියෝඩෙෆ්

346

මෙය ක්‍රියාත්මක වේ:

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
    if (iter.next() == 5) {
        iter.remove();
    }
}

මම උපකල්පනය කළේ foreach loop යනු නැවත සැකසීම සඳහා සින්ටැක්ටික් සීනි බැවින්, iterator භාවිතා කිරීම ප්‍රයෝජනවත් නොවන බවයි ... නමුත් එය ඔබට මෙම .remove()ක්‍රියාකාරිත්වය ලබා දෙයි .


45
foreach loop යනු නැවත සැකසීම සඳහා සින්ටැක්ටික් සීනි වේ. කෙසේ වෙතත්, ඔබ පෙන්වා දුන් පරිදි, ඔබට ඉරේටරය මත ඉවත් කිරීම ඇමතිය යුතුය - කුමන පුරෝකථනය මඟින් ඔබට ප්‍රවේශය ලබා නොදේ. ඔබ foreach ලූපය දී ඉවත් කිරීමට නොහැකි වන හේතුව නිසා හේතුව (ඔබ වුවත් ඇත ඇත්තටම ඒ අවටනම් යටතේ iterator භාවිතා කරමින්)
madlep

36
බිල් කේගේ පිළිතුරට [කෙලින්ම] නොමැති සන්දර්භය තුළ iter.remove () භාවිතා කිරීමට උදාහරණයක් ලෙස +1.
සංස්කරණය කරන ලද

207

ජාවා 8 සමඟ ඔබට නව removeIfක්‍රමය භාවිතා කළ හැකිය . ඔබේ උදාහරණයට අදාළ වේ:

Collection<Integer> coll = new ArrayList<>();
//populate

coll.removeIf(i -> i == 5);

3
අපොයි! මම හිතුවේ ජාවා 8 හෝ 9 හි යමක් උදව් වේවි කියා. මෙය තවමත් මට වාචිකව පෙනේ, නමුත් මම තවමත් එයට කැමතියි.
ජේම්ස් ටී ස්නෙල්

මෙම අවස්ථාවේ දී සමාන () ක්‍රියාත්මක කිරීම නිර්දේශ කර තිබේද?
අන්මෝල් ගුප්තා

removeIfභාවිතා කරන ආකාරය Iteratorසහ whileලූපය. ඔබට එය java 8java.util.Collection.java
omerhakanbilici

3
performanceomerhakanbilici සමහර ක්‍රියාත්මක ArrayListකිරීම් කාර්ය සාධන හේතූන් මත එය අභිබවා යයි. ඔබ සඳහන් කරන්නේ සුපුරුදු ක්‍රියාත්මක කිරීම පමණි.
ඩිඩියර් එල්

N අන්මොල්ගුප්තා: නැත, equalsමෙහි කිසිසේත් භාවිතා නොවේ, එබැවින් එය ක්‍රියාත්මක කළ යුතු නොවේ. (නමුත් ඇත්ත වශයෙන්ම, ඔබ equalsඔබේ පරීක්ෂණය සඳහා භාවිතා කරන්නේ නම් එය ඔබට අවශ්‍ය ආකාරයට ක්‍රියාත්මක කළ යුතුය.)
Lii

42

ප්‍රශ්නයට දැනටමත් පිළිතුරු ලබා දී ඇති බැවින්, හොඳම ක්‍රමය වන්නේ iterator වස්තුව ඉවත් කිරීමේ ක්‍රමය භාවිතා කිරීමයි, මම දෝෂය "java.util.ConcurrentModificationException"විසි කළ ස්ථානයේ විශේෂතා වෙත යන්නෙමි .

සෑම එකතුවක් පන්තිය Iterator අතුරු මුහුණත ක්රියාත්මක කරන හා සමාන ක්රම සපයන පෞද්ගලික පන්තියට next(), remove()සහhasNext() .

ඊළඟ සඳහා කේතය මේ වගේ දෙයක් ...

public E next() {
    checkForComodification();
    try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
    } catch(IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
    }
}

මෙහිදී ක්‍රමය checkForComodificationක්‍රියාත්මක වේ

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

එබැවින්, ඔබට පෙනෙන පරිදි, ඔබ පැහැදිලිවම එකතුවෙන් මූලද්‍රව්‍යයක් ඉවත් කිරීමට උත්සාහ කරන්නේ නම්. එය ප්රතිඵලයක් modCountවෙනස් වෙමින් expectedModCount, හැර නිසා ConcurrentModificationException.


ඉතා රසවත්. ඔබට ස්තුතියි! මම බොහෝ විට ඉවත් කරන්න () මා අමතන්නේ නැත, ඒ වෙනුවට මම එය එකතු කිරීමෙන් පසු ඉවත් කිරීමට කැමැත්තෙමි. එය හොඳ රටාවක් යැයි කීම නොවේ, මම මෑතකදී කරමින් සිටි දෙය.
ජේම්ස් ටී ස්නෙල්

27

ඔබට සඳහන් කළ ආකාරයටම සෘජුවම iterator භාවිතා කළ හැකිය, නැතහොත් දෙවන එකතුවක් තබා ඔබට ඉවත් කිරීමට අවශ්‍ය සෑම අයිතමයක්ම නව එකතුවට එක් කරන්න, ඉන්පසු සියල්ල ඉවත් කරන්න. මතක ශක්තිය වැඩි කිරීම සහ cpu කාලය යන වියදමින් එක් එක් ලූපයේ වර්ගය-ආරක්ෂාව දිගටම භාවිතා කිරීමට මෙය ඔබට ඉඩ සලසයි (ඔබට ඇත්ත වශයෙන්ම විශාල ලැයිස්තු හෝ පැරණි පරිගණකයක් නොමැති නම් විශාල ගැටළුවක් නොවිය යුතුය)

public static void main(String[] args)
{
    Collection<Integer> l = new ArrayList<Integer>();
    Collection<Integer> itemsToRemove = new ArrayList<>();
    for (int i=0; i < 10; i++) {
        l.add(Integer.of(4));
        l.add(Integer.of(5));
        l.add(Integer.of(6));
    }
    for (Integer i : l)
    {
        if (i.intValue() == 5) {
            itemsToRemove.add(i);
        }
    }

    l.removeAll(itemsToRemove);
    System.out.println(l);
}

7
මම සාමාන්‍යයෙන් කරන්නේ මෙයයි, නමුත් පැහැදිලි ප්‍රකාශකය මට දැනෙන වඩා හොඳ විසඳුමකි.
ක්ලෝඩියු

1
සාධාරණයි, ඔබ අනුකාරකය සමඟ වෙනත් කිසිවක් නොකරන තාක් කල් - එය නිරාවරණය වීමෙන් ඇමතුම වැනි දේ කිරීම පහසු කරයි .නෙක්ස්ට් () ලූපයකට දෙවරක් යනාදිය. විශාල ගැටළුවක් නොවේ, නමුත් ඔබ කරන්නේ නම් ගැටළු ඇති විය හැක. ඇතුළත් කිරීම් මකා දැමීමට ලැයිස්තුවක් හරහා දිවීමට වඩා සංකීර්ණ දෙයක්.
රොඩියෝ ක්ලෝන්

OdeRodeoClown: මුල් ප්‍රශ්නයේදී, ක්ලෝඩියු ඉවත් කරන්නේ එකතුවෙන් මිස අනුකාරකයෙන් නොවේ.
matt b

1
අනුකාරකයෙන් ඉවත් කිරීම යටින් පවතින එකතුවෙන් ඉවත් වේ ... නමුත් අවසාන අදහස් දැක්වීමේදී මා පැවසූ දෙය නම්, ඔබ ලූපයේ මකාදැමීම් සොයනවාට වඩා සංකීර්ණ යමක් කරන්නේ නම් (නිවැරදි දත්ත සැකසීම වැනි) iterator භාවිතා කර සමහරක් කළ හැකිය. දෝෂ සෑදීම පහසුය.
රොඩියෝ ක්ලෝන්

එය අවශ්‍ය නොවන සරල මකාදැමීමේ අගයක් නම් සහ ලූපය කරන්නේ එම එක දෙයක් පමණක් නම්, iterator එක කෙලින්ම භාවිතා කර .remove () අමතන්න.
රොඩියෝ ක්ලෝන්

18

එවැනි අවස්ථාවන්හිදී පොදු උපක්‍රමයක් වන්නේ පසුපසට යාමයි:

for(int i = l.size() - 1; i >= 0; i --) {
  if (l.get(i) == 5) {
    l.remove(i);
  }
}

ඒක තමයි මම ඔබට ජාවා 8, උදා: වඩා හොඳ ක්රම ඇති බව වඩා සතුටු වෙනවා කතා කොට, ' removeIfහෝ filterධාරා මත.


2
මෙය හොඳ උපක්‍රමයකි. නමුත් එය කට්ටල වැනි සුචිගත නොකළ එකතු කිරීම් මත ක්‍රියා නොකරනු ඇති අතර, එය සම්බන්ධිත ලැයිස්තු වල මන්දගාමී වනු ඇත.
ක්ලෝඩියු

La ක්ලවුඩිය ඔව්, මෙය නියත වශයෙන්ම ArrayLists හෝ ඒ හා සමාන එකතු සඳහා පමණි.
ලන්ඩෙයි

මම අරේලිස්ට් එකක් භාවිතා කරමි, මෙය හොඳින් ක්‍රියාත්මක විය, ස්තූතියි.
ස්ටාර්ස්වීපර්

2
දර්ශක නියමයි. එය එතරම් සුලභ නම් ඔබ භාවිතා නොකරන්නේ ඇයි for(int i = l.size(); i-->0;) {?
ජෝන්


12

සූර්යග්‍රහණ එකතුව සමඟ , MutableCollection හිremoveIf අර්ථ දක්වා ඇති ක්‍රමය ක්‍රියාත්මක වනු ඇත:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.lessThan(3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

ජාවා 8 ලැම්බඩා සින්ටැක්ස් සමඟ මෙය පහත පරිදි ලිවිය හැකිය:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.cast(integer -> integer < 3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

ජාවා 8 හි අතුරු මුහුණතේ Predicates.cast()පෙරනිමි removeIfක්‍රමයක් එකතු කර ඇති නිසා මෙහි ඇමතුම අවශ්‍ය වේ java.util.Collection.

සටහන: මම සූර්යග්‍රහණ එකතුව සඳහා කැපවන්නෙක් වෙමි .


10

පවත්නා ලැයිස්තුවේ පිටපතක් සාදා නව පිටපතක් හරහා නැවත කියන්න.

for (String str : new ArrayList<String>(listOfStr))     
{
    listOfStr.remove(/* object reference or index */);
}

19
පිටපතක් සෑදීම සම්පත් නාස්තියක් සේ පෙනේ.
ඇන්ට්සි

3
Nt ඇන්ට්සි එය ලැයිස්තුවේ විශාලත්වය සහ ඇතුළත ඇති වස්තූන්ගේ ity නත්වය මත රඳා පවතී. තවමත් වටිනා සහ වලංගු විසඳුමක්.
mre

මම මෙම ක්‍රමය භාවිතා කර ඇත. එය තව ටිකක් සම්පතක් අවශ්‍යයි, නමුත් වඩා නම්‍යශීලී සහ පැහැදිලි ය.
ටාවෝ ෂැං

ඔබ ලූපය තුළ ඇති වස්තූන් ඉවත් කිරීමට අදහස් නොකරන විට මෙය හොඳ විසඳුමකි, නමුත් ඒවා වෙනත් නූල් වලින් "අහඹු ලෙස" ඉවත් කරනු ලැබේ (උදා: ජාල මෙහෙයුම් දත්ත යාවත්කාලීන කිරීම). ඔබ මෙම පිටපත් බොහෝ දේ කරන බව පෙනේ නම්, ජාවා ක්‍රියාත්මක කිරීමක් පවා මෙය හරියටම කරයි: docs.oracle.com/javase/8/docs/api/java/util/concurrent/…
A1m

8

පුරෝකථන පුඩුවක් මඟින් නැවත එකතු කිරීමකින් කෙනෙකුට ඉවත් කළ නොහැකි බව ජනතාව තරයේ කියා සිටිති. මට අවශ්‍ය වූයේ එය තාක්‍ෂණිකව වැරදියි සහ හරියටම විස්තර කිරීමටයි (OP හි ප්‍රශ්නය මෙය දැන ගැනීම වළක්වාලීමට තරම් දියුණු බව මම දනිමි) එම උපකල්පනය පිටුපස ඇති කේතය:

for (TouchableObj obj : untouchedSet) {  // <--- This is where ConcurrentModificationException strikes
    if (obj.isTouched()) {
        untouchedSet.remove(obj);
        touchedSt.add(obj);
        break;  // this is key to avoiding returning to the foreach
    }
}

එය නැවත සිදු කිරීමෙන් ඔබට ඉවත් කළ නොහැකි Colletionබව නොවේ. එබැවින් breakඉහත කේතයේ.

මෙම පිළිතුර තරමක් විශේෂ ist භාවිතයක් වන අතර මා මෙහි පැමිණි මුල් නූලට වඩාත් ගැලපේ නම්, එය අනුපිටපතක් ලෙස සලකුණු කර ඇත (මෙම නූල වඩාත් සූක්ෂම ලෙස පෙනෙන්නට තිබුණද) අගුළු දමා ඇත.


8

සම්ප්‍රදායික ලූපයක් සමඟ

ArrayList<String> myArray = new ArrayList<>();

for (int i = 0; i < myArray.size(); ) {
    String text = myArray.get(i);
    if (someCondition(text))
        myArray.remove(i);
    else
        i++;   
}

අහ්, එබැවින් එය සැබවින්ම ව්‍යතිරේකය විසි කරන වැඩි දියුණු කරන ලද -ෆෝ-ලූප් ය.
cellepo

FWIW - i++ලූප් බොඩි තුළ නොව ලූප් ගාඩ් හි වර්ධක ලෙස වෙනස් කිරීමෙන් පසුවද එකම කේතය ක්‍රියාත්මක වේ.
cellepo

නිවැරදි කිරීම ^: i++වැඩිවීම කොන්දේසි විරහිත නම් - දැන් මට පෙනේ ඔබ ශරීරයේ එය කරන්නේ එබැවිනි :)
cellepo

2

ListIteratorලැයිස්තුවේ ඇති අයිතම එකතු කිරීමට හෝ ඉවත් කිරීමට A ඔබට ඉඩ දෙයි. ඔබට Carවස්තු ලැයිස්තුවක් ඇතැයි සිතමු :

List<Car> cars = ArrayList<>();
// add cars here...

for (ListIterator<Car> carIterator = cars.listIterator();  carIterator.hasNext(); )
{
   if (<some-condition>)
   { 
      carIterator().remove()
   }
   else if (<some-other-condition>)
   { 
      carIterator().add(aNewCar);
   }
}

ListIterator අතුරුමුහුණතේ ඇති අතිරේක ක්‍රම ( ඉටරේටරයේ දිගුව) සිත්ගන්නා සුළුය - විශේෂයෙන් එහි previousක්‍රමය.
cellepo

1

ඉහත ගැටලුව සඳහා මට යෝජනාවක් ඇත. ද්විතීයික ලැයිස්තුවක් හෝ අමතර කාලයක් අවශ්‍ය නොවේ. කරුණාකර එකම දේ කරන නමුත් වෙනත් ආකාරයකින් කරන උදාහරණයක් සොයා ගන්න.

//"list" is ArrayList<Object>
//"state" is some boolean variable, which when set to true, Object will be removed from the list
int index = 0;
while(index < list.size()) {
    Object r = list.get(index);
    if( state ) {
        list.remove(index);
        index = 0;
        continue;
    }
    index += 1;
}

මෙය සමගාමී ව්‍යතිරේකය මග හැරෙනු ඇත.


1
ප්රශ්නය පැහැදිලිවම සඳහන් කරන්නේ, OP භාවිතා කිරීම අවශ්ය නොවන ArrayListඅතර එබැවින් විශ්වාසය තැබිය නොහැකි get()බවයි. එසේ නොමැතිනම් හොඳ ප්‍රවේශයක් විය හැකිය.
kaskelotti

(පැහැදිලි කිරීම ^) OP අත්තනෝමතික ලෙස භාවිතා කරයි Collection- Collectionඅතුරුමුහුණත ඇතුළත් නොවේ get. (FWIW Listඅතුරුමුහුණතට 'ලබා ගන්න' ඇතුළත් වුවද ).
cellepo

while-ලූප් කිරීම සඳහා මම වෙනමම සවිස්තරාත්මක පිළිතුරක් මෙහි එක් කළෙමි List. නමුත් මෙම පිළිතුර සඳහා +1 මුලින්ම පැමිණි නිසා.
cellepo

1

සමගාමී හැෂ්මැප් හෝ සමගාමී ලින්ක්ඩ්කියු හෝ කොන්ක්‍රන්ට් ස්කිප්ලිස්ට්මැප් තවත් විකල්පයක් විය හැකිය, මන්ද ඔබ අයිතමය ඉවත් කළත් එකතු කළත් ඔවුන් කිසි විටෙකත් සමගාමී මෝඩිෆිකේෂන් එක්සෙප්ෂන් විසි නොකරනු ඇත.


ඔව්, ඒවා සියල්ලම java.util.concurrentපැකේජයේ ඇති බව සලකන්න . එම පැකේජයේ වෙනත් සමාන / පොදු-භාවිත අවස්ථා පන්ති CopyOnWriteArrayList & CopyOnWriteArraySet [නමුත් ඒවාට පමණක් සීමා නොවේ].
cellepo

ඇත්ත වශයෙන්ම, මම දැනගත්තා එම දත්ත ව්‍යුහය වස්තූන් මග ConcurrentModificationExceptionIndexOutOfBoundsException
හැරියද

1

මෙම ප්‍රශ්නය ජාවා 8 ගැන තරම් පැරණි නොවන බව මම දනිමි, නමුත් ජාවා 8 භාවිතා කරන අයට ඔබට පහසුවෙන් ඉවත් කළ හැකිය ():

Collection<Integer> l = new ArrayList<Integer>();

for (int i=0; i < 10; ++i) {
    l.add(new Integer(4));
    l.add(new Integer(5));
    l.add(new Integer(6));
}

l.removeIf(i -> i.intValue() == 5);

1

තවත් ක්‍රමයක් නම් ඔබේ අරා ලැයිස්තුවේ පිටපතක් සෑදීම:

List<Object> l = ...

List<Object> iterationList = ImmutableList.copyOf(l);

for (Object i : iterationList) {
    if (condition(i)) {
        l.remove(i);
    }
}

සටහන: iඑය indexනොව වස්තුවකි. සමහර විට එය ඇමතීම objවඩාත් සුදුසු වනු ඇත.
වාසනාවන්ත

1

හොඳම ක්‍රමය (නිර්දේශිත) යනු java.util.Concurrent පැකේජය භාවිතා කිරීමයි. මෙම පැකේජය භාවිතා කිරීමෙන් ඔබට මෙම ව්‍යතිරේකය පහසුවෙන් වළක්වා ගත හැකිය. වෙනස් කළ කේතය බලන්න

public static void main(String[] args) {
    Collection<Integer> l = new CopyOnWriteArrayList<Integer>();

    for (int i=0; i < 10; ++i) {
        l.add(new Integer(4));
        l.add(new Integer(5));
        l.add(new Integer(6));
    }

    for (Integer i : l) {
        if (i.intValue() == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

කාර්ය සාධන පහර ඔබ සැලකිල්ලට ගත්තාද? ඔබ මෙම ව්‍යුහයට "ලියන" සෑම අවස්ථාවකම එහි අන්තර්ගතය නව වස්තුවකට පිටපත් කරනු ලැබේ. මේ සියල්ල කාර්ය සාධනය සඳහා නරක ය.
ශංඛා 057

0

නඩුවේ ArrayList: ඉවත් (int දර්ශකය) - එය තොරව මගහැරෙන (දර්ශකය පසුගිය අංගයක් තත්වය වේ) නම්System.arraycopy() හා ඒ සඳහා කාලය නොවේ ගනී.

(දර්ශකය අඩු වුවහොත්) ලැයිස්තුවේ මූලද්‍රව්‍යයන් ද අඩු වුවහොත් අරාවකෝපි කාලය වැඩිවේ!

හොඳම remove ලදායී ඉවත් කිරීමේ ක්‍රමය නම් - එහි මූලද්‍රව්‍යයන් බැසීමේ අනුපිළිවෙලින් ඉවත් කිරීමයි: while(list.size()>0)list.remove(list.size()-1);// O (1) while(list.size()>0)list.remove(0);// ගනී O (සාධකීය (n))

//region prepare data
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<Integer> toRemove = new ArrayList<Integer>();
Random rdm = new Random();
long millis;
for (int i = 0; i < 100000; i++) {
    Integer integer = rdm.nextInt();
    ints.add(integer);
}
ArrayList<Integer> intsForIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsDescIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsIterator = new ArrayList<Integer>(ints);
//endregion

// region for index
millis = System.currentTimeMillis();
for (int i = 0; i < intsForIndex.size(); i++) 
   if (intsForIndex.get(i) % 2 == 0) intsForIndex.remove(i--);
System.out.println(System.currentTimeMillis() - millis);
// endregion

// region for index desc
millis = System.currentTimeMillis();
for (int i = intsDescIndex.size() - 1; i >= 0; i--) 
   if (intsDescIndex.get(i) % 2 == 0) intsDescIndex.remove(i);
System.out.println(System.currentTimeMillis() - millis);
//endregion

// region iterator
millis = System.currentTimeMillis();
for (Iterator<Integer> iterator = intsIterator.iterator(); iterator.hasNext(); )
    if (iterator.next() % 2 == 0) iterator.remove();
System.out.println(System.currentTimeMillis() - millis);
//endregion
  • දර්ශක පුඩුවක් සඳහා: 1090 msec
  • ඩෙස්ක් දර්ශකය සඳහා: 519 msec --- හොඳම
  • iterator සඳහා: 1043 msec

0
for (Integer i : l)
{
    if (i.intValue() == 5){
            itemsToRemove.add(i);
            break;
    }
}

ඔබ අභ්‍යන්තර iterator.next () ඇමතුම මඟ හැරියහොත් ලැයිස්තුවෙන් මූලද්‍රව්‍යය ඉවත් කිරීමෙන් පසුව අල්ලා ගැනීම වේ. එය තවමත් ක්‍රියාත්මක වේ! මම මේ වගේ කේත ලිවීමට යෝජනා නොකළත් එය පිටුපස ඇති සංකල්පය තේරුම් ගැනීමට උපකාරී වේ :-)

චියර්ස්!


0

නූල් ආරක්ෂිත එකතු කිරීමේ වෙනස් කිරීම සඳහා උදාහරණය:

public class Example {
    private final List<String> queue = Collections.synchronizedList(new ArrayList<String>());

    public void removeFromQueue() {
        synchronized (queue) {
            Iterator<String> iterator = queue.iterator();
            String string = iterator.next();
            if (string.isEmpty()) {
                iterator.remove();
            }
        }
    }
}

0

මම දන්නවා මෙම ප්‍රශ්නය උපකල්පනය කරන්නේ හුදෙක් Collectionමිස වඩාත් නිශ්චිතවම නොවේ List. නමුත් සැබවින්ම සමඟ වැඩ සිටින මෙම ප්රශ්නය කියවීම අයට Listසඳහනක්, ඔබ මඟහරවා ගත හැකිය ConcurrentModificationExceptionසමඟ while-loop (එය තුල, සංශෝධනය කිරීමේ දී) ඒ වෙනුවට ඔබට වළක්වා ගැනීමට අවශ්ය නම්Iterator ඔබ සාමාන්ය එය වළක්වා ගැනීමට අවශ්ය නම් (එක්කෝ, හෝ සාක්ෂාත් කර ගැනීම විශේෂයෙන් එය වළක්වා එක් එක් මූලද්‍රව්‍යයේ ආරම්භයේ සිට අවසානය දක්වා නැවැත්වීමට වඩා වෙනස් වන ලූප ඇණවුමක් [ Iteratorඑය කළ හැකි එකම ඇණවුම යැයි මම විශ්වාස කරමි ):

* යාවත්කාලීන කිරීම: සාම්ප්‍රදායික -ෆෝ-ලූප් සමඟ සමානකම් සාක්ෂාත් කරගත හැකි බව පැහැදිලි කරන පහත අදහස් බලන්න .

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 1;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i++);

    } else {
        i += 2;
    }
}

එම කේතයෙන් සමගාමී නවීකරණය කිරීමක් නොමැත.

එහිදී අපට පෙනෙන්නේ ලූප් කිරීම ආරම්භයේදීම ආරම්භ නොවන බවත් සෑම මූලද්‍රව්‍යයකම නතර නොවන බවත්ය ( Iteratorඑයම කළ නොහැකි යැයි මම විශ්වාස කරමි ).

FWIW ද getකැඳවනු ලැබීම අපට පෙනේ list, එහි යොමු කිරීම නිකම්ම Collection(වඩා නිශ්චිත- Listවර්ගය වෙනුවට Collection) කළ නොහැකි නම් - Listඅතුරු මුහුණත ඇතුළත් වේ get, නමුත් Collectionඅතුරු මුහුණත එසේ නොවේ. එම වෙනස සඳහා නොවේ නම්, listයොමු කිරීම ඒ වෙනුවට විය හැකිය Collection[එබැවින් තාක්‍ෂණිකව මෙම පිළිතුර ස්පර්ශක පිළිතුරක් වෙනුවට සෘජු පිළිතුරක් වනු ඇත].

FWIWW එකම කේතය නවීකරණය කිරීමෙන් පසුව සෑම මූලද්‍රව්‍යයකම ආරම්භයේදීම ආරම්භ වන ලෙස ක්‍රියා කරයි ( Iteratorඇණවුම මෙන් ):

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 0;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i);

    } else {
        ++i;
    }
}

කෙසේ වෙතත්, ඉවත් කිරීම සඳහා දර්ශක ගණනය කිරීම සඳහා මෙය තවමත් අවශ්‍ය වේ.
OneCricketeer

එසේම, මෙය මෙම පිළිතුර පිළිබඳ වඩාත් සවිස්තරාත්මක පැහැදිලි කිරීමක් පමණි stackoverflow.com/a/43441822/2308683
OneCricketeer

දැන ගැනීම සතුටක් - ස්තූතියි! වෙනත් පිළිතුර මට එය කරන බව ද තේරුම් උදව් කරන වැඩි දියුණු -for-පුඩුවක් විසි කරන බව ConcurrentModificationException, නමුත් නොවේසාම්ප්රදායික මම මේ පිළිතුර ලිවීමට පොළඹවනු ඇයි පෙර බව වටහාගෙන නැහැ (මම වැරදි සහගත, - (වන අනෙකුත් පිළිතුර භාවිතා) -for-පුඩුවක් ව්‍යතිරේකය විසි කරන සියල්ලම ලූප සඳහා යැයි සිතනු ඇත ).
cellepo

0

එක් විසඳුමක් වනුයේ ලැයිස්තුව භ්‍රමණය කර සමගාමී මෝඩිෆිකේෂන් එක්සෙප්ෂන් හෝ ඉන්ඩෙක්ස් ඕට් ඕෆ්බවුන්ඩ්ස්එක්සෙප්ෂන් වළක්වා ගැනීම සඳහා පළමු අංගය ඉවත් කිරීමයි.

int n = list.size();
for(int j=0;j<n;j++){
    //you can also put a condition before remove
    list.remove(0);
    Collections.rotate(list, 1);
}
Collections.rotate(list, -1);

0

මෙය උත්සාහ කරන්න (සමාන ලැයිස්තුවේ ඇති සියලුම අංග ඉවත් කරයි i):

for (Object i : l) {
    if (condition(i)) {
        l = (l.stream().filter((a) -> a != i)).collect(Collectors.toList());
    }
}

0

ඔබට පුනරාවර්තනය ද භාවිතා කළ හැකිය

ජාවා හි පුනරාවර්තනය යනු ක්‍රමවේදයක් අඛණ්ඩව හඳුන්වන ක්‍රියාවලියකි. ජාවා හි තමාටම කියා ගන්නා ක්‍රමයක් පුනරාවර්තන ක්‍රමය ලෙස හැඳින්වේ.


-2

මෙය හොඳම ක්‍රමය නොවිය හැකි නමුත් බොහෝ කුඩා අවස්ථාවන් සඳහා මෙය පිළිගත යුතුය:

"දෙවන හිස්-අරාවක් සාදන්න සහ ඔබට තබා ගැනීමට අවශ්‍ය ඒවා පමණක් එක් කරන්න"

මම මෙය කියවූ ස්ථානයෙන් මට මතක නැත ... යුක්තිය උදෙසා මම මෙම විකිය යමෙකු විසින් සොයාගනු ඇතැයි හෝ බලාපොරොත්තුවෙන් උපයා නොගන්නෙමි.

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.