অপসারণ বাস্তবায়ন বিশদ


9

আমার কাছে একটি ছোট বাস্তবায়ন বিশদ প্রশ্ন রয়েছে যা আমি বুঝতে ব্যর্থ ArrayList::removeIf। আমি মনে করি না যে আমি প্রথমে কিছু পূর্বশর্ত ছাড়াই এটি সহজভাবে রেখে দিতে পারি।

যেমন: বাস্তবায়নটি মূলত একটি বাল্ক remove , বিপরীতে ArrayList::remove। উদাহরণের মাধ্যমে বিষয়গুলি বোঝা অনেক সহজ করা উচিত। ধরা যাক আমার এই তালিকা রয়েছে:

List<Integer> list = new ArrayList<>(); // 2, 4, 6, 5, 5
list.add(2);
list.add(4);
list.add(6);
list.add(5);
list.add(5); 

এবং আমি সমান প্রতিটি উপাদান মুছে ফেলতে চাই। আমি কাজ করতে পারে:

Iterator<Integer> iter = list.iterator();
while (iter.hasNext()) {
    int elem = iter.next();
    if (elem % 2 == 0) {
         iter.remove();
    }
}

বা :

list.removeIf(x -> x % 2 == 0);

ফলাফল একই হবে, তবে বাস্তবায়ন খুব আলাদা। যেহেতু আমি যতবার ডাকি ততবার iteratorদৃষ্টিভঙ্গি , অন্তর্নিহিতটিকে একটি "ভাল" অবস্থায় আনতে হবে, যার অর্থ অভ্যন্তরীণ অ্যারেটি আসলে পরিবর্তিত হবে। আবার, প্রতিটি একক কল , অভ্যন্তরীণ কল হবে ।ArrayListremoveArrayListremoveSystem::arrayCopy

পরিপ্রেক্ষিতে removeIf চৌকস হয়। যেহেতু এটি অভ্যন্তরীণভাবে পুনরাবৃত্তি করে, তাই এটি জিনিসগুলিকে আরও অনুকূলিত করতে পারে। এটি যেভাবে এটি করে তা আকর্ষণীয়।

এটি প্রথমে সূচিগুলি গণনা করে যেখানে উপাদানগুলি থেকে সরানো হবে বলে মনে করা হচ্ছে। এটি প্রথমে একটি ক্ষুদ্র BitSet, longমানগুলির একটি অ্যারে গণনা করে করা হয় যেখানে প্রতিটি সূচীতে একটি 64 bitমান (ক long) থাকে। একাধিক 64 bitমান এটিকে একটি করে তোলে BitSet। কোনও নির্দিষ্ট অফসেটে একটি মান সেট করতে, আপনাকে প্রথমে অ্যারেতে সূচকটি খুঁজে বের করতে হবে এবং তারপরে সংশ্লিষ্ট বিটটি সেট করতে হবে। এটি খুব জটিল নয়। ধরা যাক আপনি বিট 65 এবং 3 সেট করতে চান প্রথমে আমাদের একটি প্রয়োজন long [] l = new long[2](কারণ আমরা b৪ বিটের ছাড়িয়েছি, তবে 128 এর বেশি নয়):

|0...(60 more bits here)...000|0...(60 more bits here)...000|

আপনি প্রথমে সূচকটি খুঁজে পান: 65 / 64(তারা আসলে তা করে 65 >> 6) এবং তারপরে সেই সূচীতে ( 1) প্রয়োজনীয় বিটটি রেখে দেয়:

1L << 65 // this will "jump" the first 64 bits, so this will actually become 00000...10. 

একই জিনিস 3। যেমন দীর্ঘতর অ্যারে পরিণত হবে:

|0...(60 more bits here)...010|0...(60 more bits here)...1000|

উত্স কোডে তারা এটিকে বিটসেট বলে - deathRow(সুন্দর নাম!)


আসুন evenএখানে উদাহরণ গ্রহণ করা যাক , যেখানেlist = 2, 4, 6, 5, 5

  • তারা অ্যারে পুনরুক্ত করে এবং এটি deathRow(যেখানে Predicate::testরয়েছে true) তা গণনা করে ।

ডেথরউ = 7 (000 ... 111)

অর্থ সূচকগুলি = [0, 1, 2] অপসারণ করতে হবে

  • তারা এখন সেই মৃত্যুর উপর ভিত্তি করে অন্তর্নিহিত অ্যারেগুলিকে উপাদানগুলি প্রতিস্থাপন করে (এটি কীভাবে করা হয় তার বিশদে যান না)

অভ্যন্তরীণ অ্যারে হয়ে যায়: [5, 5, 6, 5, 5]। মূলত তারা অ্যারের সামনে থাকা উপাদানগুলিকে সরিয়ে দেয়।


আমি শেষ পর্যন্ত প্রশ্ন আনতে পারেন।

এই সময়ে, তারা জানেন:

 w   ->  number of elements that have to remain in the list (2)
 es  ->  the array itself ([5, 5, 6, 5, 5])
 end ->  equal to size, never changed

আমার কাছে এখানে একটি পদক্ষেপ রয়েছে:

void getRidOfElementsFromWToEnd() {
    for(int i=w; i<end; ++i){
       es[i] = null;
    }
    size = w;
}

পরিবর্তে, এটি ঘটে:

private void shiftTailOverGap(Object[] es, int w, int end) {
    System.arraycopy(es, end, es, w, size - end);
    for (int to = size, i = (size -= end - w); i < to; i++)
        es[i] = null;
}

আমি এখানে ভেরিয়েবলগুলির নাম পরিবর্তন করেছি।

ফোন করার কী লাভ:

 System.arraycopy(es, end, es, w, size - end);

বিশেষ করে size - end, যেহেতু end হয় size তা কখনো পরিবর্তন করা (তাই এই সবসময় হয় - সব সময় zero)। এটি মূলত এখানে কোনও NO-OP। আমি এখানে কোন কর্নার কেস মিস করছি?


2
আমি কেবল এই বিবরণগুলি বোঝার জন্য দিনে 1/2 নষ্ট করেছি, এবং এটি এত স্পষ্ট যে এই পদ্ধতিটি অন্য জায়গায়ও ব্যবহৃত হয় । আমি বোকা: |
ইউজিন

সত্যি, তুমি আমাকে বিভ্রান্ত করে রেখেছ। আপনার প্রশ্নটি কি System.arraycopy(es, end, es, w, size - end)এর অন্তর্নিহিত বাস্তবায়ন বিশদ হিসাবে ব্যবহারের আশেপাশে ছিল removeIf? আমার প্রায় মনে হয়েছিল, আমি এর মধ্যে অন্য কিছু প্রশ্নের উত্তর পড়ছিলাম। (উপরের মন্তব্যটি পড়ে) আমি বুঝতে পারি এটি অবশেষে একটি তুচ্ছ প্রশ্নে শেষ হয়েছে। তাই নাকি?
নমন

@ নমন ঠিক এইভাবেই ভয় পেয়েছিল System.arrayCopy। তবুও এটি বিশদগুলির মধ্য দিয়ে একটি মজাদার যাত্রা ছিল (যে অভ্যন্তরীণ বিট সেটটি একই ধারণার মতো হয়ে উঠেছে java.util.BitSet)
ইউজিন

@Naman আপনি আপনি একটি উত্তর যেখানে যে প্রদান করতে না একটি NOOP (ইঙ্গিত: range...) এবং আমি তা গ্রহণ করব।
ইউজিন

1
জাভা ৮-এ ইউজিন, এটি ব্যবহার করে java.util.BitSet। আমার কাছে, BitSetঅপারেশনগুলির পুনরায় বাস্তবায়নটি মূলটির চেয়ে উল্লেখযোগ্যভাবে ভাল দেখাচ্ছে না। পুরো শব্দগুলি এড়িয়ে যাওয়ার সুযোগটি হাতছাড়া হয়েছে।
হলগার

উত্তর:


6

আপনি যে সুনির্দিষ্ট (সাধারণ) কেসটি দেখছেন যে তালিকাটি, আপনি কল removeIfকরেছেন, সেই অনুরূপ ArrayList। শুধুমাত্র এই ক্ষেত্রে, আপনি ধরে নিতে পারেন যে endসর্বদা সমানsize

একটি পাল্টা উদাহরণ হবে:

ArrayList<Integer> l = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6, 7));
l.subList(2, 5).removeIf(i -> i%2 == 1);

তেমনি, একটি আর্গুমেন্টের সাথে removeAllকল করবে যা shiftTailOverGapকোনও এ প্রয়োগ করার সময় endথেকে পৃথক হতে sizeপারেsubList

আপনি কল করলে একই পরিস্থিতি দেখা দেয় clear()। সেক্ষেত্রে, প্রকৃত অপারেশন, ArrayListনিজে থেকে কল করার সময় সঞ্চালনটি এতটাই তুচ্ছ যে এটি shiftTailOverGapপদ্ধতিটিকে কলও করে না । কেবলমাত্র এর মতো কিছু ব্যবহার করার পরে l.subList(a, b).clear()এটি শেষ হয়ে removeRange(a, b)যাবে l, যা ইতিমধ্যে আপনি নিজেরাই খুঁজে পেয়েছেন, এর চেয়ে ছোট হতে পারে এমন shiftTailOverGap(elementData, a, b)একটি অনুরোধ করুন ।bsize

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.