मेरे पास एक छोटा कार्यान्वयन विवरण प्रश्न है जिसे मैं समझने में विफल हूं 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]
(क्योंकि हम 64 बिट्स से आगे निकल गए, लेकिन 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] हटाए जाने हैं
- वे अब उस मृत्यु के आधार पर अंतर्निहित सरणी में तत्वों को प्रतिस्थापित करते हैं (विवरण में नहीं जा रहा है कि यह कैसे किया जाता है)
आंतरिक सरणी बन जाती है: [५, ५, ६, ५, ५]। मूल रूप से वे उन तत्वों को स्थानांतरित करते हैं जिन्हें सरणी के सामने रहना चाहिए।
मैं अंत में प्रश्न में ला सकता हूं।
इस समय, वे जानते हैं:
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
ऑपरेशन्स को फिर से लागू करना मूल से बेहतर नहीं लगता है। संपूर्ण शब्दों को छोड़ने का अवसर चूक गया है।