জাভাতে মানচিত্রের মান বাড়ানোর সর্বাধিক দক্ষ উপায়


377

আমি আশা করি এই প্রশ্নটি এই ফোরামের জন্য খুব মৌলিক হিসাবে বিবেচিত হবে না, তবে আমরা দেখতে পাব। আমি ভাবছি কীভাবে আরও ভাল পারফরম্যান্সের জন্য কিছু কোড রিফ্যাক্টর করা যায় যা বেশ কয়েকবার চালাচ্ছে।

বলুন আমি একটি মানচিত্র (সম্ভবত একটি হ্যাশম্যাপ) ব্যবহার করে একটি শব্দ ফ্রিকোয়েন্সি তালিকা তৈরি করছি, যেখানে প্রতিটি কী শব্দের সাথে একটি স্ট্রিং যা গণনা করা হচ্ছে এবং মানটি একটি পূর্ণসংখ্যা যা প্রতিবার শব্দের একটি টোকেন পাওয়া যায়।

পার্লে, এই জাতীয় মান বৃদ্ধি করা তুচ্ছ সহজ হতে পারে:

$map{$word}++;

তবে জাভাতে, এটি আরও জটিল। এখানে বর্তমানে আমি যেভাবে এটি করছি:

int count = map.containsKey(word) ? map.get(word) : 0;
map.put(word, count + 1);

কোনটি অবশ্যই নতুন জাভা সংস্করণগুলিতে অটোবক্সিং বৈশিষ্ট্যের উপর নির্ভর করে। আমি অবাক হয়েছি আপনি যদি এই জাতীয় মান বাড়ানোর আরও কার্যকর উপায় প্রস্তাব করতে পারেন। সংগ্রহের কাঠামোটি রদ করার জন্য এবং এর পরিবর্তে অন্য কিছু ব্যবহার করার জন্য কি ভাল পারফরম্যান্সের কারণ রয়েছে?

আপডেট: আমি বেশ কয়েকটি উত্তরের একটি পরীক্ষা করেছি। নিচে দেখ.


আমি java.util.Hashtable জন্য একই হবে বলে মনে করি।
jrudolph

2
অবশ্যই যদি একই হয় তবে হ্যাশটেবল একটি মানচিত্রে কার্যকর inf
হুইস্কিসিয়ার

জাভা 8: computeIfAbsent উদাহরণ: stackoverflow.com/a/37439971/1216775
akhil_mittal

উত্তর:


366

কিছু পরীক্ষার ফলাফল

আমি এই প্রশ্নের অনেক ভাল উত্তর পেয়েছি - ধন্যবাদ লোকেরা - তাই আমি কয়েকটি পরীক্ষা চালানোর সিদ্ধান্ত নিয়েছি এবং কোন পদ্ধতিটি আসলে দ্রুততম তা খুঁজে বের করার সিদ্ধান্ত নিয়েছি। আমি যে পাঁচটি পদ্ধতি পরীক্ষা করেছি সেগুলি হ'ল:

  • "কনটেনস্কি" পদ্ধতিটি আমি প্রশ্নটিতে উপস্থাপন করেছি
  • আলেকসান্দার দিমিত্রভ পরামর্শ দিয়েছেন "টেস্টফোরনুল" পদ্ধতি
  • হ্যাঙ্ক গে দ্বারা প্রস্তাবিত "অ্যাটমিকলং" পদ্ধতি
  • "ট্রভ" পদ্ধতিটি জ্রুডল্ফ দ্বারা প্রস্তাবিত
  • phax.myopenid.com দ্বারা প্রস্তাবিত "MutableInt" পদ্ধতি

পদ্ধতি

আমি যা করেছি তা এখানে ...

  1. নীচে প্রদর্শিত পার্থক্য বাদে অভিন্ন ছিল এমন পাঁচটি শ্রেণি তৈরি করে। প্রতিটি ক্লাসকে আমি উপস্থাপিত দৃশ্যের একটি অপারেশন সঞ্চালন করতে হয়েছিল: একটি 10 ​​এমবি ফাইল খোলার এবং এটি পড়তে, তারপরে ফাইলের সমস্ত টোকেন শব্দের একটি ফ্রিকোয়েন্সি গণনা সম্পাদন করা হয়। যেহেতু এটিতে গড়ে মাত্র 3 সেকেন্ড সময় লেগেছিল, তাই আমি 10 বার এটি ফ্রিকোয়েন্সি গণনা (I / O নয়) সম্পাদন করেছি।
  2. 10 টি পুনরাবৃত্তির লুপটি টাইমড করে তবে আই / ও অপারেশন নয় এবং জাভা কুকবুকে আইয়ান ডারউইনের পদ্ধতিটি মূলত ব্যবহার করে মোট সময় (ঘড়ি সেকেন্ডে) রেকর্ড করা হয়েছে ।
  3. পাঁচটি সিরিজ পাঁচটি পরীক্ষা করেছেন এবং তারপরে আরও তিনবার এটি করেছিলেন।
  4. প্রতিটি পদ্ধতির জন্য চারটি ফলাফল গড় হয়েছে।

ফলাফল

যারা আগ্রহী তাদের জন্য আমি প্রথমে ফলাফল এবং নীচের কোডটি উপস্থাপন করব।

ContainsKey পদ্ধতি, আশা ছিল হিসাবে, ধীরতম, তাই আমি যে পদ্ধতি গতি তুলনায় প্রতিটি পদ্ধতির গতি দেব।

  • কন্টেনস্কি : 30.654 সেকেন্ড (বেসলাইন)
  • পারমাণবিক লং: 29.780 সেকেন্ড (দ্রুত হিসাবে 1.03 গুণ)
  • পরীক্ষার জন্য নাল: ২৮.৮০৪ সেকেন্ড (দ্রুত হিসাবে ১.০6 বার)
  • ট্র্যাভ : 26.313 সেকেন্ড (1.16 বার দ্রুত)
  • পরিবর্তনীয় আইটেম: 25.747 সেকেন্ড (1.19 বার দ্রুত)

উপসংহার

এটি প্রদর্শিত হবে যে কেবলমাত্র পরিবর্তনীয় পদ্ধতি এবং ট্র্যাভ পদ্ধতিটি দ্রুততর দ্রুতগতিতে কেবলমাত্র তারা 10% -রও বেশি পারফরম্যান্সকে উত্সাহ দেয়। তবে থ্রেডিং যদি সমস্যা হয় তবে অ্যাটমিকলং অন্যদের তুলনায় আরও আকর্ষণীয় হতে পারে (আমি সত্যই নিশ্চিত নই)। আমি finalভেরিয়েবলগুলির সাথে টেস্টফরনলও চালিয়েছি , তবে পার্থক্যটি নগন্য ছিল।

নোট করুন যে আমি বিভিন্ন পরিস্থিতিতে মেমরির ব্যবহারের প্রোফাইল দিইনি। যে কারও কাছ থেকে মিউটেবিল ইনট এবং ট্র্যাভ পদ্ধতিগুলি মেমরির ব্যবহারকে প্রভাবিত করতে পারে তার মধ্যে ভাল অন্তর্দৃষ্টি রয়েছে এমন কারও কাছ থেকে শুনে আমি খুশি হব।

ব্যক্তিগতভাবে, আমি মিউটেবলআইন্ট পদ্ধতিটিকে সবচেয়ে আকর্ষণীয় বলে মনে করি, কারণ এতে কোনও তৃতীয় পক্ষের ক্লাস লোড করার প্রয়োজন হয় না। সুতরাং যদি আমি এটির সাথে সমস্যা না আবিষ্কার করি তবে আমি সম্ভবত এই পথেই যেতে পারি।

কোড

প্রতিটি পদ্ধতি থেকে এখানে গুরুত্বপূর্ণ কোড।

ContainsKey

import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
int count = freq.containsKey(word) ? freq.get(word) : 0;
freq.put(word, count + 1);

TestForNull

import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
Integer count = freq.get(word);
if (count == null) {
    freq.put(word, 1);
}
else {
    freq.put(word, count + 1);
}

AtomicLong

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
...
final ConcurrentMap<String, AtomicLong> map = 
    new ConcurrentHashMap<String, AtomicLong>();
...
map.putIfAbsent(word, new AtomicLong(0));
map.get(word).incrementAndGet();

Trove

import gnu.trove.TObjectIntHashMap;
...
TObjectIntHashMap<String> freq = new TObjectIntHashMap<String>();
...
freq.adjustOrPutValue(word, 1, 1);

MutableInt

import java.util.HashMap;
import java.util.Map;
...
class MutableInt {
  int value = 1; // note that we start at 1 since we're counting
  public void increment () { ++value;      }
  public int  get ()       { return value; }
}
...
Map<String, MutableInt> freq = new HashMap<String, MutableInt>();
...
MutableInt count = freq.get(word);
if (count == null) {
    freq.put(word, new MutableInt());
}
else {
    count.increment();
}

3
দুর্দান্ত কাজ, ভাল কাজ। একটি সামান্য মন্তব্য - অ্যাটমিকলং কোডে putIfAbmitted () কল মানচিত্রে ইতিমধ্যে থাকলেও একটি নতুন অ্যাটমিকলং (0) ইনস্ট্যান্ট করবে। পরিবর্তে (মানচিত্র.জেট (কী) == নাল) ব্যবহার করতে যদি আপনি এটি টুইট করেন তবে আপনি সম্ভবত পরীক্ষার ফলাফলগুলিতে একটি উন্নতি পাবেন।
লেইহ ক্যালডওয়েল

2
মিউটেবলইন্টের অনুরূপ পদ্ধতির সাথে আমি সম্প্রতি একই কাজ করেছি। আমি এটি সর্বোত্তম সমাধানটি শুনে খুশি (আমি কেবল ধরে নিলাম যে এটি কোনও পরীক্ষা না করেই হয়েছিল)।
22:50

আপনি আমার চেয়ে দ্রুত, কিপ শুনে খুব ভালো লাগল। ;-) আপনি যদি সেই পদ্ধতির কোনও ত্রুটি আবিষ্কার করেন তবে আমাকে জানান।
গ্রেগরি

4
পারমাণবিক দীর্ঘ ক্ষেত্রে এটি এক ধাপে করা আরও দক্ষ হবে না (সুতরাং আপনার 2 এর পরিবর্তে কেবল 1 ব্যয়বহুল গতিসম্পন্ন অপারেশন রয়েছে) "মানচিত্র.পুটআইফএবসেন্ট (শব্দ, নতুন অ্যাটমিকলং (0))। ইনক্রিমেন্টএন্ডগেট ();"
স্মার্টনাট 700

1
@ গ্রেগরি আপনি জাভা 8 এর বিবেচনা করেছেন freq.compute(word, (key, count) -> count == null ? 1 : count + 1)? অভ্যন্তরীণভাবে এটি containsKeyল্যাম্বডায় এর চেয়ে কম হ্যাশযুক্ত অনুসন্ধান করে , এটি কীভাবে অন্যের সাথে তুলনা করে তা দেখতে আকর্ষণীয় হবে।
TWiStErRob

255

জাভা 8 ব্যবহারের সাথে এখন আরও একটি ছোট উপায় রয়েছে Map::merge

myMap.merge(key, 1, Integer::sum)

এর মানে কি:

  • যদি কীটি বিদ্যমান না থাকে তবে 1 হিসাবে মান হিসাবে রাখুন
  • অন্যথায় কীতে সংযুক্ত মানের সাথে যোগফল 1

আরও তথ্য এখানে


সর্বদা জাভা ভালবাসা 8. এটি কি পারমাণবিক? বা আমি এটি একটি সুসংগত সঙ্গে ঘিরে রাখা উচিত?
টিইনা

4
এটি আমার পক্ষে কাজ করে বলে মনে হচ্ছে না তবে map.merge(key, 1, (a, b) -> a + b); করেছিল
27:38

2
@ টিইনা পারমাণবিক বৈশিষ্ট্যগুলি বাস্তবায়নের জন্য নির্দিষ্ট, সিএফ। দস্তাবেজগুলি : "ডিফল্ট বাস্তবায়নটি এই পদ্ধতির সিঙ্ক্রোনাইজেশন বা পারমাণবিক বৈশিষ্ট্য সম্পর্কে কোনও গ্যারান্টি দেয় না at পারমাণবিকভাবে শুধুমাত্র যদি মান উপস্থিত না হয়। "
জেনসগ্রাম

2
Integer::sumগ্রোভির জন্য, এটি একটি বায়ু ফাংশন হিসাবে গ্রহণ করবে না , এবং এটি লেখার মতো @ রাস্টার উত্তরটি পছন্দ করবে না। এটি আমার পক্ষে কাজ করেছেMap.merge(key, 1, { a, b -> a + b})
জোকিওনে

2
@ রুস্টার, আমি জানি আপনার মন্তব্যটি এক বছর আগে হয়েছে তবে আপনি কী মনে করতে পারেন যে এটি আপনার পক্ষে কার্যকর হয়নি? আপনি কি একটি সংকলন ত্রুটি পেয়েছিলেন বা মান বাড়ানো হয়নি?
পল

44

2016 সালে একটি সামান্য গবেষণা: https://github.com/leventov/java-word-count , বেঞ্চমার্ক উত্স কোড

প্রতি পদ্ধতিতে সর্বোত্তম ফলাফল (ছোট আরও ভাল):

                 time, ms
kolobokeCompile  18.8
koloboke         19.8
trove            20.8
fastutil         22.7
mutableInt       24.3
atomicInteger    25.3
eclipse          26.9
hashMap          28.0
hppc             33.6
hppcRt           36.5

সময়-স্থানের ফলাফল:


2
ধন্যবাদ, এটি সত্যিই সহায়ক ছিল। পেয়ারাটির মাল্টিটসেট (উদাহরণস্বরূপ, হ্যাশমুলিটসেট) বেঞ্চমার্কে যুক্ত করা ভাল।
কেবাদ

34

গুগল পেয়ারা আপনার বন্ধু ...

... কমপক্ষে কিছু ক্ষেত্রে। তাদের এই দুর্দান্ত অ্যাটমিকলংম্যাপ রয়েছে । বিশেষত দুর্দান্ত কারণ আপনি নিজের মানচিত্রে দীর্ঘমেয়াদী মূল্য নিয়ে কাজ করছেন।

যেমন

AtomicLongMap<String> map = AtomicLongMap.create();
[...]
map.getAndIncrement(word);

মানটিতে আরও 1 যুক্ত করা আরও সম্ভব:

map.getAndAdd(word, 112L); 

7
AtomicLongMap#getAndAddএকটি আদিম লাগে longএবং মোড়ক ক্লাসে না; করার কোন লাভ নেই new Long()। এবং AtomicLongMapএকটি প্যারামিটারাইজড টাইপ; আপনার এটি হিসাবে ঘোষণা করা উচিত ছিল AtomicLongMap<String>
হেল্ডার পেরেরা

32

@ হ্যাঙ্ক গে

আমার নিজের (বরং অপ্রয়োজনীয়) মন্তব্যের ফলো-আপ হিসাবে: ট্রভকে যেতে যাওয়ার মতো মনে হচ্ছে। যদি কোন কারনের জন্য, আপনি স্ট্যান্ডার্ড JDK দিয়ে বিদ্ধ করতে চেয়েছিলেন, ConcurrentMap এবং AtomicLong কোড একটি করতে পারেন অতি ক্ষুদ্র বিট সুন্দর, যদিও YMMV।

    final ConcurrentMap<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong>();
    map.putIfAbsent("foo", new AtomicLong(0));
    map.get("foo").incrementAndGet();

1মানচিত্রে মান হিসাবে ছেড়ে যাবে foo। বাস্তবে, থ্রেডিংয়ের প্রতি বন্ধুত্ব বাড়ানোই এই পদ্ধতির সুপারিশ করার মতো।


9
PutIfAbsent () মান প্রদান করে। স্থানীয় ভেরিয়েবলে ফেরত মান সংরক্ষণ করা এবং কলটি পুনরায় ফিরে আসার চেয়ে ইনক্রিমেন্টএন্ডগেট () এ ব্যবহার করা বড় উন্নতি হতে পারে।
স্মার্টনাট 700

যদি নির্দিষ্ট কী ইতিমধ্যে মানচিত্রের অভ্যন্তরে কোনও মানের সাথে যুক্ত না থাকে তবে putIfAbsent একটি নাল মান ফিরিয়ে দিতে পারে তাই আমি প্রত্যাবর্তিত মানটি ব্যবহারে যত্নবান হব। docs.oracle.com/javase/8/docs/api/java/util/…
বম্বুর

27
Map<String, Integer> map = new HashMap<>();
String key = "a random key";
int count = map.getOrDefault(key, 0); // ensure count will be one of 0,1,2,3,...
map.put(key, count + 1);

এবং এইভাবেই আপনি সহজ কোড সহ একটি মান বাড়ান।

বেনিফিট:

  • কোনও নতুন শ্রেণি যুক্ত করার বা কোনও পরিবর্তনীয় ইন্টের ধারণাটি ব্যবহার করার দরকার নেই
  • কোনও লাইব্রেরির উপর নির্ভর করে না
  • ঠিক কী হচ্ছে তা বোঝা সহজ (খুব বেশি বিমূর্ততা নয়)

downside:

  • হ্যাশ ম্যাপটি () পেতে এবং () রাখার জন্য দু'বার অনুসন্ধান করা হবে। সুতরাং এটি সর্বাধিক পারফরম্যান্ট কোড হবে না।

তাত্ত্বিকভাবে, একবার আপনি () কল করলে, আপনি ইতিমধ্যে কোথায় () রাখবেন তা জানেন, সুতরাং আপনাকে আবার অনুসন্ধান করতে হবে না। তবে হ্যাশ ম্যাপে অনুসন্ধান করতে সাধারণত খুব ন্যূনতম সময় লাগে যা আপনি এই ধরনের পারফরম্যান্স ইস্যুকে এড়িয়ে যেতে পারেন।

তবে আপনি যদি বিষয়টি সম্পর্কে খুব গুরুতর হন তবে আপনি একজন নিখুঁতবাদী, অন্য উপায়টি মার্জ করার পদ্ধতিটি ব্যবহার করা উচিত, এটি (সম্ভবত) মানচিত্রটি কেবল একবার অনুসন্ধান করার জন্য (পূর্ববর্তী কোড স্নিপেটের চেয়ে বেশি দক্ষ): এই কোডটি প্রথম দর্শন থেকে স্পষ্ট নয়, এটি সংক্ষিপ্ত এবং পারফরম্যান্ট)

map.merge(key, 1, (a,b) -> a+b);

পরামর্শ: আপনার বেশিরভাগ সময় সম্পাদনের সামান্য পারফরম্যান্সের চেয়ে কোড পঠনযোগ্যতার বিষয়ে যত্ন নেওয়া উচিত। যদি প্রথম কোড স্নিপেট আপনার পক্ষে বুঝতে সহজ হয় তবে এটি ব্যবহার করুন। তবে আপনি যদি দ্বিতীয়টি জরিমানা বুঝতে সক্ষম হন তবে আপনি এটির জন্যও যেতে পারেন!


GetOfDefault পদ্ধতি জাভা 7 তে উপলব্ধ নয় J আমি কীভাবে জাভা 7 এ এটি অর্জন করতে পারি?
তানভি

1
আপনাকে তখন অন্য উত্তরের উপর নির্ভর করতে হতে পারে। এটি কেবল জাভা 8
99

1
সংশ্লেষ সমাধানের জন্য +1, এটি সর্বাধিক পারফর্মিং ফাংশন হবে কারণ আপনাকে কেবলমাত্র হ্যাশকোড গণনার জন্য 1 সময় দিতে হবে (ক্ষেত্রে যে মানচিত্রটি আপনি সঠিকভাবে পদ্ধতিটিতে ব্যবহার করছেন) এর পরিবর্তে সম্ভাব্যভাবে 3 প্রদান করার পরিবর্তে বার
ফেরিবিগ

2
পদ্ধতির অনুমিতি ব্যবহার করে: মানচিত্র.স্রোম (কী, 1, পূর্ণসংখ্যার :: যোগফল)
ইরানডাপ

25

এই জাতীয় জিনিসটির জন্য গুগল সংগ্রহ লাইব্রেরিটি দেখতে সর্বদা ভাল ধারণা । এই ক্ষেত্রে একটি মাল্টিসেট কৌশলটি করবে:

Multiset bag = Multisets.newHashMultiset();
String word = "foo";
bag.add(word);
bag.add(word);
System.out.println(bag.count(word)); // Prints 2

কী / এন্ট্রি ইত্যাদির উপর পুনরাবৃত্তি করার জন্য মানচিত্রের মতো পদ্ধতি রয়েছে Intern অভ্যন্তরীণভাবে বর্তমানে বাস্তবায়নটি একটি ব্যবহার করে HashMap<E, AtomicInteger>, তাই আপনার বক্সিংয়ের ব্যয় বহন করা হবে না।


উপরের উত্তরদাতাদের tovares প্রতিক্রিয়া প্রতিফলিত করা প্রয়োজন। এপিআই পোস্ট হওয়ার পরে পরিবর্তিত হয়েছে (3 বছর আগে :))
স্টিভ

count()মাল্টিসেটের পদ্ধতিটি কি ও (1) বা ও (এন) সময়ে (সবচেয়ে খারাপ) চালিত হয়? এই দস্তাবেজের দস্তাবেজগুলি অস্পষ্ট।
অ্যাডাম পার্কিন

এই ধরণের জিনিসের জন্য আমার অ্যালগরিদম: যদি (hasApacheLib (জিনিস)) ফিরে আসবে অ্যাপাচিবল; অন্যথায় যদি (hasOnGuava (জিনিস)) পেয়ারা ফিরে আসে। সাধারণত আমি এই দুটি ধাপটি পেরেছি না। :)
digao_mb

22

আপনার আসল প্রয়াসটি সম্পর্কে আপনার সচেতন হওয়া উচিত

int গণনা = map.containsKey (শব্দ)? map.get (শব্দ): 0;

একটি মানচিত্রে দুটি সম্ভাব্য ব্যয়বহুল ক্রিয়াকলাপ রয়েছে containsKeyএবং যথা get। প্রাক্তন পরবর্তীকালের মতো সম্ভাব্য বেশ সমান কোনও অপারেশন সম্পাদন করেন, সুতরাং আপনি একই কাজটি দু'বার করছেন !

আপনি যদি মানচিত্রের জন্য এপিআইয়ের দিকে নজর দেন, মানচিত্রে অনুরোধকৃত উপাদান না থাকলে getসাধারণত ক্রিয়াকলাপগুলি ফিরে nullআসে return

মনে রাখবেন এটি এর মতো একটি সমাধান তৈরি করবে

map.put (কী, ম্যাপ.জেট (কী) + 1);

বিপজ্জনক, যেহেতু এটি NullPointerExceptionগুলি পেতে পারে । আপনার nullপ্রথমটি পরীক্ষা করা উচিত ।

এছাড়াও মনে রাখবেন , এবং এই খুব গুরুত্বপূর্ণ, যে HashMapগুলি করতে থাকে nullsসংজ্ঞা দ্বারা। সুতরাং প্রত্যেকে প্রত্যাবর্তনকারীরা nullবলে না যে "এ জাতীয় কোনও উপাদান নেই"। এ ক্ষেত্রে, containsKeyআচরণ করবে ভিন্নভাবে থেকে getআসলে আপনি কহন মধ্যে কিনা এমন একটি উপাদান আছে। বিস্তারিত জানার জন্য এপিআই দেখুন।

আপনার ক্ষেত্রে, তবে, আপনি কোনও সঞ্চিত nullএবং "noSuchElement" এর মধ্যে পার্থক্য করতে চাইবেন না । আপনি যদি অনুমতি দিতে না চান তবে আপনি nullএকটি পছন্দ করতে পারেন Hashtable। ইতিমধ্যে অন্যান্য উত্তরে প্রস্তাবিত মোড়কের লাইব্রেরি ব্যবহার করা আপনার আবেদনের জটিলতার উপর নির্ভর করে ম্যানুয়াল চিকিত্সার আরও ভাল সমাধান হতে পারে।

এটা নেটিভ করছেন করার সর্বোত্তম উপায় উত্তর সম্পূর্ণ করার জন্য (এবং আমি সম্পাদনা ফাংশন প্রথমে লাগাতে যে, ধন্যবাদ ভুলে গেছি!), হয় getএকটি মধ্যে finalপরিবর্তনশীল, জন্য চেক nullএবং putএটা ফিরে সঙ্গে 1। পরিবর্তনশীল হওয়া উচিত finalকারণ এটি যাইহোক অপরিবর্তনীয়। সংকলকটির এই ইঙ্গিতটির প্রয়োজন হতে পারে না তবে এটি সেভাবে পরিষ্কার হয়।

চূড়ান্ত হ্যাশম্যাপ মানচিত্র = জেনারেটর্যান্ডম হ্যাশম্যাপ ();
চূড়ান্ত অবজেক্ট কী = fetchSomeKey ();
চূড়ান্ত পূর্ণসংখ্যা i = map.get (কী);
যদি (i! = নাল) {
    মানচিত্র.পুট (i + 1);
} অন্য {
    // কিছু কর
}

আপনি যদি অটোবক্সিংয়ের উপর নির্ভর করতে না চান তবে আপনার map.put(new Integer(1 + i.getValue()));পরিবর্তে এর মতো কিছু বলা উচিত ।


গ্রোভিতে প্রাথমিক আনম্যাপড / নাল মানগুলির সমস্যা এড়াতে আমি শেষ করছি: গণনা.পুট (কী, (কাউন্টারসেট (কী)?: 0) + 1) // অতিরিক্ত জটিল সংস্করণ ++
জো অ্যাটবার্গার

2
বা, সর্বাধিক সহজভাবে: গণনা = [:] ef সাথে ডিফল্ট {0} // ++ দূরে
জো অ্যাটবার্গার

18

অন্য উপায় হতে পারে একটি পরিবর্তনীয় পূর্ণসংখ্যা তৈরি:

class MutableInt {
  int value = 0;
  public void inc () { ++value; }
  public int get () { return value; }
}
...
Map<String,MutableInt> map = new HashMap<String,MutableInt> ();
MutableInt value = map.get (key);
if (value == null) {
  value = new MutableInt ();
  map.put (key, value);
} else {
  value.inc ();
}

অবশ্যই এটি বোঝায় একটি অতিরিক্ত বস্তু তৈরি করা কিন্তু একটি পূর্ণসংখ্যা তৈরির তুলনায় ওভারহেড (এমনকি পূর্ণসংখ্যা.ওলুওফ সহ) এত বেশি হওয়া উচিত নয়।


5
আপনি প্রথমবার মানচিত্রে এটি স্থাপন করার পরে মিউটেবলইন্টটি শুরু করতে চান না?
টম হাটিন -

5
অ্যাপাচি এর কমন্স-ল্যাং এর জন্য ইতিমধ্যে আপনার জন্য লিখিত একটি মিউটেবলআইন্ট রয়েছে।
একক শট

11

আপনি জাভা 8 তে সরবরাহ করা ইন্টারফেসে কম্পিউটিআইএফএবসেন্ট পদ্ধতিটি ব্যবহার করতে পারেন ।Map

final Map<String,AtomicLong> map = new ConcurrentHashMap<>();
map.computeIfAbsent("A", k->new AtomicLong(0)).incrementAndGet();
map.computeIfAbsent("B", k->new AtomicLong(0)).incrementAndGet();
map.computeIfAbsent("A", k->new AtomicLong(0)).incrementAndGet(); //[A=2, B=1]

পদ্ধতিটি computeIfAbsentযাচাই করে নিচ্ছে যে নির্দিষ্ট কীটি ইতিমধ্যে কোনও মানের সাথে যুক্ত কিনা? যদি কোনও সম্পর্কিত মান না থাকে তবে প্রদত্ত ম্যাপিং ফাংশনটি ব্যবহার করে এটির মানটি গণনা করার চেষ্টা করে। যে কোনও ক্ষেত্রে এটি নির্দিষ্ট কীটির সাথে সম্পর্কিত বর্তমান (বিদ্যমান বা গণিত) মান প্রদান করে, বা যদি গুণিত মানটি নাল হয় তবে নাল হয়।

সাইড নোটে আপনার যদি এমন পরিস্থিতি থাকে যেখানে একাধিক থ্রেডগুলি একটি সাধারণ যোগফল আপডেট করে আপনি লংএড্ডার শ্রেণিতে নজর রাখতে পারেন high উচ্চতর বিতর্ক, AtomicLongউচ্চতর স্থান গ্রহণের ব্যয়ে এই শ্রেণীর প্রত্যাশিত থ্রুপুট তুলনায় উল্লেখযোগ্য পরিমাণে বেশি ।


একত্রে হ্যাশম্যাপ এবং অ্যাটমিকলং কেন?
ealeon

7

এখানে মেমোরি রোটেশন একটি সমস্যা হতে পারে, যেহেতু 128 এর চেয়ে বড় বা সমান ইন্টের প্রতিটি বক্সিংয়ের ফলে একটি অবজেক্ট বরাদ্দ হয় (দেখুন পূর্ণসংখ্যা.ভালিউওফ (ইনট))। যদিও আবর্জনা সংগ্রহকারী খুব দক্ষতার সাথে স্বল্প-কালীন জিনিসগুলির সাথে ডিল করেন, পারফরম্যান্স কিছুটা হলেও ক্ষতিগ্রস্থ হবে।

আপনি যদি জানেন যে বর্ধিত সংখ্যাগুলি মূলত কীগুলির সংখ্যা (এই ক্ষেত্রে শব্দগুলির সংখ্যা) ছাড়িয়ে যাবে, পরিবর্তে কোনও int ধারক ব্যবহার করে বিবেচনা করুন। ফ্যাক্স ইতিমধ্যে এর জন্য কোড উপস্থাপন করেছে। এখানে এটি আবার দুটি পরিবর্তন সহ (ধারক শ্রেণীর তৈরি স্ট্যাটিক এবং প্রাথমিক মান 1 এ সেট করা হয়েছে):

static class MutableInt {
  int value = 1;
  void inc() { ++value; }
  int get() { return value; }
}
...
Map<String,MutableInt> map = new HashMap<String,MutableInt>();
MutableInt value = map.get(key);
if (value == null) {
  value = new MutableInt();
  map.put(key, value);
} else {
  value.inc();
}

আপনার যদি চূড়ান্ত পারফরম্যান্সের প্রয়োজন হয় তবে মানচিত্রের প্রয়োগের সন্ধান করুন যা আদিম মান ধরণের জন্য সরাসরি তৈরি করা হয়। জ্রুডলফ জিএনইউ ট্রভের কথা উল্লেখ করেছে ।

যাইহোক, এই বিষয়টির জন্য একটি ভাল অনুসন্ধান শব্দটি "হিস্টোগ্রাম"।


5

এতে থাকা কেকে () কল করার পরিবর্তে কেবল ম্যাপ.জেট কল করা দ্রুত হয় এবং ফিরে আসা মানটি নাল কিনা তা পরীক্ষা করে।

    Integer count = map.get(word);
    if(count == null){
        count = 0;
    }
    map.put(word, count + 1);

3

আপনি কি নিশ্চিত যে এটি একটি বাধা? আপনি কোন পারফরম্যান্স বিশ্লেষণ করেছেন?

হটস্পটগুলি দেখার জন্য নেটবিয়ানস প্রোফাইলার (এটি নিখরচায় এবং এনবি 6.1 এ অন্তর্নির্মিত) ব্যবহার করার চেষ্টা করুন।

অবশেষে, একটি জেভিএম আপগ্রেড (1.5-> 1.6 থেকে বলুন) প্রায়শই সস্তা পারফরম্যান্স বুস্টার হয়। এমনকি বিল্ড সংখ্যায় একটি আপগ্রেড ভাল পারফরম্যান্সের উত্সাহ সরবরাহ করতে পারে। আপনি যদি উইন্ডোয় চলমান থাকেন এবং এটি একটি সার্ভার ক্লাস অ্যাপ্লিকেশন, সার্ভার হটস্পট জেভিএম ব্যবহার করতে কমান্ড লাইনে-সার্ভারটি ব্যবহার করুন। লিনাক্স এবং সোলারিস মেশিনে এটি স্বয়ংক্রিয়ভাবে সন্ধান করা হয়।


3

কয়েকটি পন্থা রয়েছে:

  1. গুগল সংগ্রহগুলিতে থাকা সেটগুলির মতো একটি ব্যাগ অ্যালোরিয়াম ব্যবহার করুন।

  2. পরিবর্তনীয় পাত্রে তৈরি করুন যা আপনি মানচিত্রে ব্যবহার করতে পারেন:


    class My{
        String word;
        int count;
    }

এবং পুট ("শব্দ", নতুন আমার ("শব্দ")) ব্যবহার করুন; তারপরে আপনি এটি যাচাই করতে পারেন এবং যোগ করার সময় বাড়তি।

তালিকাগুলি ব্যবহার করে আপনার নিজস্ব সমাধান ঘূর্ণায়মান এড়িয়ে চলুন, কারণ আপনি যদি অন্তর্লুপ অনুসন্ধান এবং বাছাই করেন তবে আপনার কার্য সম্পাদন দুর্গন্ধযুক্ত হবে। প্রথম হ্যাশম্যাপ সমাধানটি আসলে বেশ দ্রুত, তবে গুগল সংগ্রহগুলিতে পাওয়া এর মতো একটি যথাযথটি সম্ভবত আরও ভাল।

গুগল সংগ্রহগুলি ব্যবহার করে শব্দ গণনা করা, এরকম কিছু দেখায়:



    HashMultiset s = new HashMultiset();
    s.add("word");
    s.add("word");
    System.out.println(""+s.count("word") );


হ্যাশমলিটসেট ব্যবহার করা বেশ স্বচ্ছল, কারণ একটি ব্যাগ-অ্যালগরিদম শব্দের গণনা করার সময় আপনার যা প্রয়োজন তা হ'ল।


3

আমি মনে করি আপনার সমাধানটি স্ট্যান্ডার্ড উপায় হবে, তবে - যেমন আপনি নিজেরাই উল্লেখ করেছেন - এটি সম্ভবত সবচেয়ে দ্রুততম উপায় নয়।

আপনি জিএনইউ ট্র্যাভের দিকে তাকিয়ে থাকতে পারেন । এটি একটি লাইব্রেরি যাতে সমস্ত ধরণের দ্রুত আদিম সংগ্রহ রয়েছে। আপনার উদাহরণটিতে একটি TOBjectIntHashMap ব্যবহার করা হবে যার একটি পদ্ধতি অ্যাডজাস্টআরপুটভ্যালু রয়েছে যা আপনি যা চান ঠিক তা করে does


TObjectIntHashMap এর লিঙ্কটি নষ্ট হয়ে গেছে। এটি সঠিক লিঙ্কটি: trove4j.sourceforge.net/javadocs/gnu/trove/map/…
সেগাল-হালেভি

ধন্যবাদ, এরেল, আমি লিঙ্কটি ঠিক করেছি।
জুনুডল্ফ

3

MutableInt পদ্ধতির একটি প্রকরণ যা আরও দ্রুত হতে পারে, যদি কিছুটা হ্যাক হয় তবে একটি একক উপাদান ইন্ট অ্যারে ব্যবহার করা হয়:

Map<String,int[]> map = new HashMap<String,int[]>();
...
int[] value = map.get(key);
if (value == null) 
  map.put(key, new int[]{1} );
else
  ++value[0];

আপনি যদি এই প্রকরণের সাথে আপনার পারফরম্যান্স পরীক্ষাগুলি পুনরায় চালু করতে পারেন তবে এটি আকর্ষণীয় হবে। এটি সবচেয়ে দ্রুত হতে পারে।


সম্পাদনা করুন: উপরের প্যাটার্নটি আমার পক্ষে ভাল কাজ করেছে তবে শেষ পর্যন্ত আমি তৈরি করা কয়েকটি খুব বড় মানচিত্রে মেমরির আকার হ্রাস করতে ট্রভের সংগ্রহগুলি ব্যবহার করতে পরিবর্তিত হয়েছি - এবং বোনাস হিসাবে এটিও দ্রুত ছিল।

সত্যিই একটি দুর্দান্ত বৈশিষ্ট্য হ'ল এই TObjectIntHashMapক্লাসটির একটি একক adjustOrPutValueকল রয়েছে যা সেই চাবিতে ইতিমধ্যে কোনও মান রয়েছে কিনা তার উপর নির্ভর করে হয় একটি প্রাথমিক মান রাখবে বা বিদ্যমান মান বাড়িয়ে দেবে। এটি বৃদ্ধি করার জন্য উপযুক্ত:

TObjectIntHashMap<String> map = new TObjectIntHashMap<String>();
...
map.adjustOrPutValue(key, 1, 1);

3

গুগল সংগ্রহ হ্যাশমুলিটসেট:
- ব্যবহারে বেশ মার্জিত
- তবে সিপিইউ এবং মেমরি গ্রাস করে

সেরা একটি পদ্ধতি যেমন হবে: Entry<K,V> getOrPut(K); (মার্জিত এবং কম ব্যয়)

এই জাতীয় পদ্ধতিটি কেবল একবার হ্যাশ এবং সূচি গণনা করবে এবং তারপরে আমরা এন্ট্রি সহ যা চাই তা করতে পারি (হয় মানটি প্রতিস্থাপন বা আপডেট করুন)।

আরও মার্জিত:
- একটি নিন HashSet<Entry>
- এটি প্রসারিত করুন যাতে get(K)প্রয়োজনে একটি নতুন এন্ট্রি রাখে
- এন্ট্রি আপনার নিজস্ব অবজেক্ট হতে পারে।
->(new MyHashSet()).get(k).increment();


3

বেশ সহজ, শুধুমাত্র বিল্ট-ইন ফাংশন ব্যবহার Map.javaহিসাবে অনুসৃত

map.put(key, map.getOrDefault(key, 0) + 1);

এটি মান বৃদ্ধি করে না, এটি কেবল বর্তমান মান বা 0 নির্ধারণ করে যদি কোনও মান কীতে বরাদ্দ না করা হয়।
সিগি

আপনি ++... ওএমজি দ্বারা মান বৃদ্ধি করতে পারেন , এটি এত সহজ। @ সিগি
সুডোজ

রেকর্ডের জন্য: ++এই অভিব্যক্তিটির কোথাও কাজ করে না কারণ একটি ভেরিয়েবল এর অপারেন্ড হিসাবে প্রয়োজন তবে কেবল মান রয়েছে। আপনার + 1কাজের যোগ যদিও। এখন আপনার সমাধানটি অফ৯৯৫৫৫ এর উত্তরের মতোই
সিইগি 26'19

2

"নকল কীটি নিশ্চিত করার জন্য" "দরকার" পেতে "রাখুন"।
সুতরাং সরাসরি একটি "পুট" করুন,
এবং যদি পূর্ববর্তী মান ছিল তবে একটি সংযোজন করুন:

Map map = new HashMap ();

MutableInt newValue = new MutableInt (1); // default = inc
MutableInt oldValue = map.put (key, newValue);
if (oldValue != null) {
  newValue.add(oldValue); // old + inc
}

যদি গণনা 0 থেকে শুরু হয়, তবে 1 যুক্ত করুন: (বা অন্য কোনও মান ...)

Map map = new HashMap ();

MutableInt newValue = new MutableInt (0); // default
MutableInt oldValue = map.put (key, newValue);
if (oldValue != null) {
  newValue.setValue(oldValue + 1); // old + inc
}

বিজ্ঞপ্তি: এই কোডটি থ্রেড নিরাপদ নয়। এটি নির্মাণের জন্য ব্যবহার করুন তারপর মানচিত্রটি ব্যবহার করুন, একযোগে এটি আপডেট করার জন্য নয়।

অপ্টিমাইজেশন: একটি লুপে, পরবর্তী লুপের নতুন মান হয়ে উঠতে পুরানো মানটি রাখুন।

Map map = new HashMap ();
final int defaut = 0;
final int inc = 1;

MutableInt oldValue = new MutableInt (default);
while(true) {
  MutableInt newValue = oldValue;

  oldValue = map.put (key, newValue); // insert or...
  if (oldValue != null) {
    newValue.setValue(oldValue + inc); // ...update

    oldValue.setValue(default); // reuse
  } else
    oldValue = new MutableInt (default); // renew
  }
}

1

বিভিন্ন আদিম চাদরে, যেমন, Integerঅপরিবর্তনীয় তাই সত্যিই আরও সংক্ষিপ্ত উপায় কি বলছি না এর দ্বারা যদি না তোমার মত কিছু দিয়ে এটা করতে পারেন AtomicLong । আমি এক মিনিট এবং আপডেটে যেতে পারেন। BTW, hashtable হয় একটি অংশ সংগ্রহ ফ্রেমওয়ার্ক


1

আমি অ্যাপাচি সংগ্রহগুলি অলস মানচিত্রটি ব্যবহার করব (0 থেকে মানগুলি আরম্ভ করার জন্য) এবং সেই মানচিত্রে মান হিসাবে অ্যাপাচি ল্যাং থেকে মিউটেবলআইন্টিজার ব্যবহার করব।

সবচেয়ে বড় ব্যয় আপনার পদ্ধতিতে দুইবার মানচিত্রটি সের্যাচ করতে হবে। আমার মধ্যে আপনি এটি একবার করতে হবে। কেবলমাত্র মানটি পান (এটি অনুপস্থিত থাকলে এটি সূচনা হবে) এবং এটি বৃদ্ধি করবে।


1

প্রায়োগিক জাভা লাইব্রেরির TreeMapdatastructure একটি হয়েছে updateসর্বশেষ ট্রাঙ্ক মাথায় পদ্ধতি:

public TreeMap<K, V> update(final K k, final F<V, V> f)

ব্যবহারের উদাহরণ:

import static fj.data.TreeMap.empty;
import static fj.function.Integers.add;
import static fj.pre.Ord.stringOrd;
import fj.data.TreeMap;

public class TreeMap_Update
  {public static void main(String[] a)
    {TreeMap<String, Integer> map = empty(stringOrd);
     map = map.set("foo", 1);
     map = map.update("foo", add.f(1));
     System.out.println(map.get("foo").some());}}

এই প্রোগ্রামটি "2" মুদ্রণ করে।


1

@ ভিলমান্টাস বড়ানউস্কাস: এই উত্তর সম্পর্কে আমি মন্তব্য করতে চাই যদি আমার কাছে রেপ পয়েন্ট থাকে তবে আমি তা না করি। আমি লক্ষ করতে চেয়েছিলাম যে কাউন্টার ক্লাসটি সংজ্ঞায়িত হয়েছে যে থ্রেড-নিরাপদ নেই কারণ এটি মান () সিঙ্ক্রোনাইজ না করে কেবল ইনক () সিঙ্ক্রোনাইজ করার পক্ষে যথেষ্ট নয়। আপডেটের সাথে ঘটনার আগে সম্পর্ক স্থাপন না করা হলে অন্যান্য থ্রেড কলিং মান () মানটি দেখার গ্যারান্টিযুক্ত নয়।


আপনি যদি কারও উত্তরটি উল্লেখ করতে চান তবে উপরে @ [ব্যবহারকারীর নাম] ব্যবহার করুন, উদাহরণস্বরূপ, @ ভিলমান্টাস বড়ানউস্কাস <সামগ্রী এখানে যায়>
হ্যাঙ্ক গে

এটি পরিষ্কার করার জন্য আমি সেই পরিবর্তন করেছি।
অ্যালেক্স মিলার

1

আমি জানি না এটি কতটা দক্ষ তবে নীচের কোডটিও কাজ করে You আপনাকে BiFunctionশুরুতে একটি সংজ্ঞায়িত করতে হবে । এছাড়াও, আপনি এই পদ্ধতিতে কেবল ইনক্রিমেন্টের চেয়ে আরও বেশি কিছু করতে পারেন।

public static Map<String, Integer> strInt = new HashMap<String, Integer>();

public static void main(String[] args) {
    BiFunction<Integer, Integer, Integer> bi = (x,y) -> {
        if(x == null)
            return y;
        return x+y;
    };
    strInt.put("abc", 0);


    strInt.merge("abc", 1, bi);
    strInt.merge("abc", 1, bi);
    strInt.merge("abc", 1, bi);
    strInt.merge("abcd", 1, bi);

    System.out.println(strInt.get("abc"));
    System.out.println(strInt.get("abcd"));
}

আউটপুট হয়

3
1

1

আপনি যদি গ্রিপস সংগ্রহগুলি ব্যবহার করছেন তবে আপনি একটি ব্যবহার করতে পারেন HashBag। এটি মেমরির ব্যবহারের ক্ষেত্রে সবচেয়ে কার্যকর পদ্ধতি হবে এবং এটি কার্যকর করার গতির ক্ষেত্রেও ভাল পারফর্ম করবে।

HashBagএমন একটি দ্বারা সমর্থিত MutableObjectIntMapযা Counterবস্তুর পরিবর্তে আদিম কৌতুক সঞ্চয় করে। এটি মেমরির ওভারহেড হ্রাস করে এবং সম্পাদনের গতি উন্নত করে।

HashBag আপনার যে API টি দরকার তা সরবরাহ করে এটি একটি Collection আপনাকে কোনও আইটেমের উপস্থিতির সংখ্যার জন্য জিজ্ঞাসা করতে সহায়তা করে allows

এখানে গ্রহগ্রহের সংগ্রহ কাটা থেকে একটি উদাহরণ ।

MutableBag<String> bag =
  HashBag.newBagWith("one", "two", "two", "three", "three", "three");

Assert.assertEquals(3, bag.occurrencesOf("three"));

bag.add("one");
Assert.assertEquals(2, bag.occurrencesOf("one"));

bag.addOccurrences("one", 4);
Assert.assertEquals(6, bag.occurrencesOf("one"));

দ্রষ্টব্য: আমি গ্রহগ্রাহ সংগ্রহের প্রতিশ্রুতিবদ্ধ।


1

আমি জাভা 8 মানচিত্র :: গণনা () ব্যবহার করার পরামর্শ দিই। কীটি উপস্থিত না থাকায় এটি কেস বিবেচনা করে।

Map.compute(num, (k, v) -> (v == null) ? 1 : v + 1);

mymap.merge(key, 1, Integer::sum)?
Det

-2

যেহেতু প্রচুর লোক গ্রোভির উত্তরের জন্য জাভা বিষয়গুলি অনুসন্ধান করে, আপনি গ্রোভিতে কীভাবে এটি করতে পারেন তা এখানে:

dev map = new HashMap<String, Integer>()
map.put("key1", 3)

map.merge("key1", 1) {a, b -> a + b}
map.merge("key2", 1) {a, b -> a + b}


-3

আশা করি আমি আপনার প্রশ্নটি সঠিকভাবে বুঝতে পেরেছি, আমি পাইথন থেকে জাভাতে আসছি যাতে আপনার সংগ্রামের সাথে আমি সহানুভূতি জানাতে পারি।

যদি তোমার থাকে

map.put(key, 1)

আপনি করতে হবে

map.put(key, map.get(key) + 1)

আশাকরি এটা সাহায্য করবে!

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