ജാവ കളക്ഷനുകളിൽ മാസ്റ്റർ ആകാം
മിക്ക ഡെവലപ്പർമാരും ഡിഫോൾട്ട് ആയി ArrayList അല്ലെങ്കിൽ HashSet ആണ് ഉപയോഗിക്കാറുള്ളത്. ലളിതമായ ജോലികൾക്ക് ഇത് മതിയാകും. എന്നാൽ വേഗതയോ വലിയ തോതിലുള്ള ഡാറ്റ കൈകാര്യം ചെയ്യാനോ (scale) ആവശ്യമായി വരുമ്പോൾ ഇത് പരാജയപ്പെടുന്നു.
ഒരിക്കൽ ഞാൻ ഒരു സാധാരണ ArrayList ഉപയോഗിച്ച് ഒരു ഗെയിം ലീഡർബോർഡ് നിർമ്മിച്ചിരുന്നു. ഓരോ തവണ സ്കോർ മാറുമ്പോഴും ഞാൻ അത് സോർട്ട് (sort) ചെയ്യുമായിരുന്നു. ഇത് കാരണം UI നിരന്തരം ഫ്രീസ് ആകുന്നുണ്ടായിരുന്നു. ഞാൻ ഭാഷയെ (language) ഉപയോഗിക്കുന്നതിന് പകരം അതിനോട് പോരാടുകയായിരുന്നു.
തെറ്റായ ടൂളുകൾ ഉപയോഗിക്കുന്നത് നിർത്തുക. വേഗതയേറിയതും വൃത്തിയുള്ളതുമായ കോഡ് എഴുതാൻ ഈ മൂന്ന് സ്പെഷ്യലൈസ്ഡ് കളക്ഷനുകൾ ഉപയോഗിക്കുക.
- Enum Constants-നായി EnumSet ഉപയോഗിക്കുക
എനമ്മുകൾക്കായി (enums) നിങ്ങൾ HashSet ഉപയോഗിക്കുകയാണെങ്കിൽ, അത് പെർഫോമൻസിനെ ബാധിക്കും. ഓരോ തവണ ഇൻസേർട്ട് ചെയ്യുമ്പോഴും എനം ഒരു ഒബ്ജക്റ്റായി മാറുന്നു (boxing). ഇത് അനാവശ്യമായ അധികഭാരം (overhead) ഉണ്ടാക്കുന്നു.
EnumSet ഒരു ബിറ്റ് വെക്റ്റർ (bit vector) ആണ് ഉപയോഗിക്കുന്നത്. ഇത് ഒരു സിംഗിൾ CPU ഇൻസ്ട്രക്ഷൻ ഉപയോഗിച്ച് പരിശോധനകൾ നടത്തുന്നു.
- നിശ്ചിത എനം വാല്യൂകൾ (fixed set of enum values) ഉള്ളപ്പോൾ ഇത് ഉപയോഗിക്കുക.
- ഇത് ഗാർബേജ് കളക്ഷൻ കുറയ്ക്കുന്നു.
- ടൈറ്റ് ലൂപ്പുകളിൽ (tight loops) ഇത് 10 മടങ്ങ് വേഗത വർദ്ധിപ്പിക്കുന്നു.
Before:
Set<Ability> abilities = new HashSet<>();
abilities.add(Ability.FIRE);
After:
EnumSet<Ability> abilities = EnumSet.of(Ability.FIRE);
- Range Queries-നായി NavigableSet ഉപയോഗിക്കുക
ഒരു റേഞ്ച് കണ്ടെത്താനായി സോർട്ട് ചെയ്ത ലിസ്റ്റിലൂടെ മാനുവലായി ലൂപ്പ് ചെയ്യുന്നത് സാവധാനത്തിലുള്ളതും തെറ്റുകൾ സംഭവിക്കാൻ സാധ്യതയുള്ളതുമാണ്. പലപ്പോഴും 'off-by-one' ബഗുകൾ ഇതിലൂടെ ഉണ്ടാകാം.
NavigableSet നിങ്ങളുടെ ഡാറ്റ സ്വയം സോർട്ട് ചെയ്ത് സൂക്ഷിക്കുന്നു. സബ്സെറ്റുകൾക്കായി (subsets) ഇത് O(log n) ലുക്കപ്പുകൾ നൽകുന്നു.
- ഹൈ-സ്കോർ ടേബിളുകൾക്കോ വില പരിധിക്കോ (price ranges) വേണ്ടി ഇത് ഉപയോഗിക്കുക.
- പ്രത്യേക റേഞ്ചുകൾ ലഭിക്കാൻ
headSet()അല്ലെങ്കിൽsubSet()ഉപയോഗിക്കുക. - ഇത് മാനുവൽ സോർട്ടിംഗ് കോഡിന്റെ ആവശ്യം ഇല്ലാതാക്കുന്നു.
Before:
Collections.sort(scores);
List<Integer> topTen = scores.subList(size - 10, size);
After:
NavigableSet<Integer> scores = new TreeSet<>(Comparator.reverseOrder());
scores.add(1542);
NavigableSet<Integer> topTen = scores.headSet(scores.first(), true).stream().limit(10).collect(Collectors.toCollection(TreeSet::new));
- Read-Heavy ലിസ്റ്റുകൾക്കായി CopyOnWriteArrayList ഉപയോഗിക്കുക
ഒരു ArrayList-ൽ സിൻക്രണൈസ്ഡ് ബ്ലോക്കുകൾ (synchronized blocks) ഉപയോഗിക്കുന്നത് ഓരോ റീഡിനെയും സാവധാനത്തിലാക്കും. കൂടാതെ, ഒരു ത്രെഡ് എഴുതിക്കൊണ്ടിരിക്കുമ്പോൾ മറ്റൊന്ന് വായിക്കാൻ ശ്രമിച്ചാൽ ConcurrentModificationException ഉണ്ടാകാനും ഇത് കാരണമാകും.
CopyOnWriteArrayList ഓരോ തവണ എഴുതുമ്പോഴും അറേയുടെ ഒരു പുതിയ കോപ്പി നിർമ്മിക്കുന്നു. റീഡർമാർ അറേയുടെ ഒരു സ്നാപ്പ്ഷോട്ട് (snapshot) ആണ് കാണുന്നത്.
- ഇവന്റ് ലിസണർമാർക്കോ (event listeners) കോൺഫിഗറേഷൻ സെറ്റിംഗുകൾക്കോ വേണ്ടി ഇത് ഉപയോഗിക്കുക.
- റീഡുകൾ എഴുത്തുകളേക്കാൾ (writes) വളരെ കൂടുതലായി നടക്കുമ്പോൾ ഇത് ഉപയോഗിക്കുക.
- ഇത് ലോക്ക്-ഫ്രീ റീഡുകൾ (lock-free reads) അനുവദിക്കുന്നു.
Before:
List<String> log = Collections.synchronizedList(new ArrayList<>());
// Iterating here can crash if a writer joins in.
After:
CopyOnWriteArrayList<String> log = new CopyOnWriteArrayList<>();
// Iteration is safe and never crashes.
എപ്പോഴും ഒരേ രണ്ട് കളക്ഷനുകൾ തന്നെ ഉപയോഗിക്കുന്നത് ഒഴിവാക്കൂ. നിങ്ങളുടെ ഡാറ്റാ പാറ്റേണിന് അനുയോജ്യമായ ടൂൾ തിരഞ്ഞെടുക്കുക.
ഉറവിടം: https://dev.to/timevolt/the-java-collections-force-mastering-the-hidden-gems-like-a-jedi-4438