আমার কাছে একটি ছোট বাস্তবায়ন বিশদ প্রশ্ন রয়েছে যা আমি বুঝতে ব্যর্থ 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
দৃষ্টিভঙ্গি , অন্তর্নিহিতটিকে একটি "ভাল" অবস্থায় আনতে হবে, যার অর্থ অভ্যন্তরীণ অ্যারেটি আসলে পরিবর্তিত হবে। আবার, প্রতিটি একক কল , অভ্যন্তরীণ কল হবে ।ArrayList
remove
ArrayList
remove
System::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। আমি এখানে কোন কর্নার কেস মিস করছি?
System.arraycopy(es, end, es, w, size - end)
এর অন্তর্নিহিত বাস্তবায়ন বিশদ হিসাবে ব্যবহারের আশেপাশে ছিল removeIf
? আমার প্রায় মনে হয়েছিল, আমি এর মধ্যে অন্য কিছু প্রশ্নের উত্তর পড়ছিলাম। (উপরের মন্তব্যটি পড়ে) আমি বুঝতে পারি এটি অবশেষে একটি তুচ্ছ প্রশ্নে শেষ হয়েছে। তাই নাকি?
System.arrayCopy
। তবুও এটি বিশদগুলির মধ্য দিয়ে একটি মজাদার যাত্রা ছিল (যে অভ্যন্তরীণ বিট সেটটি একই ধারণার মতো হয়ে উঠেছে java.util.BitSet
)
range
...) এবং আমি তা গ্রহণ করব।
java.util.BitSet
। আমার কাছে, BitSet
অপারেশনগুলির পুনরায় বাস্তবায়নটি মূলটির চেয়ে উল্লেখযোগ্যভাবে ভাল দেখাচ্ছে না। পুরো শব্দগুলি এড়িয়ে যাওয়ার সুযোগটি হাতছাড়া হয়েছে।