জাভা লিঙ্কডহ্যাশম্যাপ প্রথম বা শেষ এন্ট্রি পান


139

আমি ব্যবহার করেছি LinkedHashMapকারণ মানচিত্রে কীগুলি প্রবেশ করানো হয়েছে সেটি ক্রম গুরুত্বপূর্ণ।

তবে এখন আমি প্রথম স্থানে (প্রথম প্রবেশ করা প্রবেশ) বা সর্বশেষে কীটির মান পেতে চাই।

মত একটি পদ্ধতি হতে হবে first()এবং last()বা ওই জাতীয় কিছু?

প্রথম কি প্রবেশের জন্য আমার কি পুনরুক্তি হওয়া দরকার? এজন্যই আমি ব্যবহার করেছি LinkedHashMap!

ধন্যবাদ!


3
পরিস্থিতি আসলেই দুর্ভাগ্যজনক। এখানে (স্বল্প-অগ্রাধিকার) বৈশিষ্ট্য অনুরোধ যা আপনার যা প্রয়োজন তা প্রদান করবে: বাগসসসন.ভিউ_বগ.ডো? বুগ_আইডি = 6266354
কেভিন

stackoverflow.com/a/30984049/1808829 আপনাকে সাহায্য করতে পারে
আয়াজ Alifov

উত্তর:


159

এর শব্দার্থবিদ্যা LinkedHashMapএখনও একটি ম্যাপ যারা, বদলে যে হয় LinkedList। এটি সন্নিবেশ ক্রমটি ধরে রাখে, হ্যাঁ, তবে এটি একটি ইন্টারফেসের দিকের চেয়ে বরং বাস্তবায়ন বিশদ।

"প্রথম" প্রবেশের দ্রুততম উপায়টি এখনও রয়েছে entrySet().iterator().next()। "সর্বশেষ" এন্ট্রি পাওয়া সম্ভব, তবে .next()আপনি শেষ না হওয়া পর্যন্ত কল করে পুরো এন্ট্রি সেটটিতে পুনরাবৃত্তি করতে হবে । while (iterator.hasNext()) { lastElement = iterator.next() }

সম্পাদনা : তবে, আপনি যদি জাভাসি এপিআই ছাড়িয়ে যেতে ইচ্ছুক হন, তবে অ্যাপাচি কমন্স সংগ্রহগুলির নিজস্ব LinkedMapপ্রয়োগ রয়েছে, যার মতো পদ্ধতি রয়েছে firstKeyএবং lastKeyআপনি যা খুঁজছেন তা করেন। ইন্টারফেসটি যথেষ্ট সমৃদ্ধ।


আমার লিঙ্কযুক্ত মানচিত্রে প্রথম এন্ট্রি হিসাবে আমাকে প্রবেশ (কী, মান) toোকাতে হবে; বা সূচক ভিত্তিক সন্নিবেশ জাতীয় কিছু। অ্যাপাচি সংগ্রহের মাধ্যমে কি তা সম্ভব?
কানগাভেলু সুগুমার

2
"লিঙ্কডম্যাপ," "ফার্স্টকি," এবং "লাস্টকি" লিঙ্কগুলি বাসি, ফাই।
জোশ

দেখা যাচ্ছে আপনি সম্ভবত কমন্স লিংকডম্যাপকে বিপরীত ক্রমে অনুসরণ করতে ব্যবহার করতে পারেন, লাস্টকি পেয়ে তারপরে পূর্ববর্তী কী ব্যবহার করে পিছিয়ে যাওয়ার জন্য ... চমৎকার।
রজারডপ্যাক

দেখা যাচ্ছে আপনি সম্ভবত কমন্স লিংকডম্যাপকে বিপরীত ক্রমে অনুসরণ করতে ব্যবহার করতে পারেন, লাস্টকি পেয়ে তারপরে পূর্ববর্তী কী ব্যবহার করে পিছিয়ে যাওয়ার জন্য ... চমৎকার। এর পরে আপনি এমনকি আপনার নিজের Iterable লিখতে পারে যদি আপনি এটি ব্যবহার করতে চেয়েছিলেন foreach loops, উদা: stackoverflow.com/a/1098153/32453
rogerdpack

1
@ স্কাফম্যান, আপনি কি দয়া করে সহায়তা করতে পারেন: mylinkedmap.entrySet().iterator().next()সময়ের জটিলতা কী? এটি ও (1)?
tkrishtop

25

আপনি কি (শেষ এন্ট্রি পেতে) এর মতো কিছু করার চেষ্টা করতে পারেন:

linkedHashMap.entrySet().toArray()[linkedHashMap.size() -1];

5
এটি পুনরাবৃত্তি করা এবং শেষ আইটেমটি রাখা এখনও দ্রুত। T last = null ; for( T item : linkedHashMap.values() ) last = item; বা এমন কিছু। এটি সময়ে ও (এন) তবে স্মৃতিতে ও (1)।
ফ্লোরিয়ান এফ

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

1
@ স্কিনি_জোনস: অ্যারের সমাধানটি দ্রুত হবে কেন? এটি এখনও পুরো মানচিত্রের উপরে পুনরাবৃত্তি জড়িত, এটি কেবল এখন পুনরাবৃত্তিটি একটি JDK পদ্ধতির অভ্যন্তরে স্পষ্ট করার পরিবর্তে।
রুখ 21

17

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

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

সম্ভাব্য বিকল্প

অ্যারে পদ্ধতির ব্যবহার

অনুসরণটি তুলনা করতে আমি পূর্ববর্তী উত্তর থেকে এটি নিয়েছি। এই সমাধানটি @feresr এর অন্তর্গত।

  public static String FindLasstEntryWithArrayMethod() {
        return String.valueOf(linkedmap.entrySet().toArray()[linkedmap.size() - 1]);
    }

অ্যারেলিস্ট পদ্ধতির ব্যবহার

কিছুটা আলাদা পারফরম্যান্স সহ প্রথম সমাধানের মতো

public static String FindLasstEntryWithArrayListMethod() {
        List<Entry<Integer, String>> entryList = new ArrayList<Map.Entry<Integer, String>>(linkedmap.entrySet());
        return entryList.get(entryList.size() - 1).getValue();
    }

পদ্ধতি হ্রাস করুন

এই পদ্ধতিটি স্ট্রিমের শেষ উপাদানটি না পাওয়া পর্যন্ত উপাদানগুলির সেটকে হ্রাস করবে। তদতিরিক্ত, এটি কেবল নির্বিচার ফলাফলগুলিই ফিরিয়ে দেবে

public static String FindLasstEntryWithReduceMethod() {
        return linkedmap.entrySet().stream().reduce((first, second) -> second).orElse(null).getValue();
    }

এড়িয়ে যান ফাংশন পদ্ধতি

এই পদ্ধতিটি স্ট্রিমের সর্বশেষ উপাদানটি কেবলমাত্র তার আগে সমস্ত উপাদান এড়িয়ে চলে যাবে

public static String FindLasstEntryWithSkipFunctionMethod() {
        final long count = linkedmap.entrySet().stream().count();
        return linkedmap.entrySet().stream().skip(count - 1).findFirst().get().getValue();
    }

বিকল্প বিকল্প

Iterables.get গুগল পেয়ারা থেকে সর্বশেষ। এটি তালিকাগুলি এবং সোর্টার্ডসেটের জন্যও কিছু অপ্টিমাইজেশন রয়েছে

public static String FindLasstEntryWithGuavaIterable() {
        return Iterables.getLast(linkedmap.entrySet()).getValue();
    }

এখানে সম্পূর্ণ উত্স কোড

import com.google.common.collect.Iterables;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class PerformanceTest {

    private static long startTime;
    private static long endTime;
    private static LinkedHashMap<Integer, String> linkedmap;

    public static void main(String[] args) {
        linkedmap = new LinkedHashMap<Integer, String>();

        linkedmap.put(12, "Chaitanya");
        linkedmap.put(2, "Rahul");
        linkedmap.put(7, "Singh");
        linkedmap.put(49, "Ajeet");
        linkedmap.put(76, "Anuj");

        //call a useless action  so that the caching occurs before the jobs starts.
        linkedmap.entrySet().forEach(x -> {});



        startTime = System.nanoTime();
        FindLasstEntryWithArrayListMethod();
        endTime = System.nanoTime();
        System.out.println("FindLasstEntryWithArrayListMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");


         startTime = System.nanoTime();
        FindLasstEntryWithArrayMethod();
        endTime = System.nanoTime();
        System.out.println("FindLasstEntryWithArrayMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");

        startTime = System.nanoTime();
        FindLasstEntryWithReduceMethod();
        endTime = System.nanoTime();

        System.out.println("FindLasstEntryWithReduceMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");

        startTime = System.nanoTime();
        FindLasstEntryWithSkipFunctionMethod();
        endTime = System.nanoTime();

        System.out.println("FindLasstEntryWithSkipFunctionMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");

        startTime = System.currentTimeMillis();
        FindLasstEntryWithGuavaIterable();
        endTime = System.currentTimeMillis();
        System.out.println("FindLasstEntryWithGuavaIterable : " + "took " + (endTime - startTime) + " milliseconds");


    }

    public static String FindLasstEntryWithReduceMethod() {
        return linkedmap.entrySet().stream().reduce((first, second) -> second).orElse(null).getValue();
    }

    public static String FindLasstEntryWithSkipFunctionMethod() {
        final long count = linkedmap.entrySet().stream().count();
        return linkedmap.entrySet().stream().skip(count - 1).findFirst().get().getValue();
    }

    public static String FindLasstEntryWithGuavaIterable() {
        return Iterables.getLast(linkedmap.entrySet()).getValue();
    }

    public static String FindLasstEntryWithArrayListMethod() {
        List<Entry<Integer, String>> entryList = new ArrayList<Map.Entry<Integer, String>>(linkedmap.entrySet());
        return entryList.get(entryList.size() - 1).getValue();
    }

    public static String FindLasstEntryWithArrayMethod() {
        return String.valueOf(linkedmap.entrySet().toArray()[linkedmap.size() - 1]);
    }
}

প্রতিটি পদ্ধতির পারফরম্যান্স সহ আউটপুট এখানে

FindLasstEntryWithArrayListMethod : took 0.162 milliseconds
FindLasstEntryWithArrayMethod : took 0.025 milliseconds
FindLasstEntryWithReduceMethod : took 2.776 milliseconds
FindLasstEntryWithSkipFunctionMethod : took 3.396 milliseconds
FindLasstEntryWithGuavaIterable : took 11 milliseconds

11

LinkedHashMapবর্তমান প্রয়োগ (জাভা 8) এর লেজ ট্র্যাক করে। কর্মক্ষমতা যদি উদ্বেগজনক হয় এবং / অথবা মানচিত্রটি আকারে বড় হয় তবে আপনি সেই ক্ষেত্রটি প্রতিবিম্বের মাধ্যমে অ্যাক্সেস করতে পারেন।

কারণ বাস্তবায়ন বদলে যেতে পারে সম্ভবত ফ্যালব্যাক কৌশলও রাখা ভাল ধারণা। যদি কোনও ব্যতিক্রম ছুঁড়ে দেওয়া হয় তবে আপনি কিছু লগইন করতে পারেন যাতে আপনি জানেন যে বাস্তবায়ন পরিবর্তিত হয়েছে।

এটি দেখতে দেখতে পারে:

public static <K, V> Entry<K, V> getFirst(Map<K, V> map) {
  if (map.isEmpty()) return null;
  return map.entrySet().iterator().next();
}

public static <K, V> Entry<K, V> getLast(Map<K, V> map) {
  try {
    if (map instanceof LinkedHashMap) return getLastViaReflection(map);
  } catch (Exception ignore) { }
  return getLastByIterating(map);
}

private static <K, V> Entry<K, V> getLastByIterating(Map<K, V> map) {
  Entry<K, V> last = null;
  for (Entry<K, V> e : map.entrySet()) last = e;
  return last;
}

private static <K, V> Entry<K, V> getLastViaReflection(Map<K, V> map) throws NoSuchFieldException, IllegalAccessException {
  Field tail = map.getClass().getDeclaredField("tail");
  tail.setAccessible(true);
  return (Entry<K, V>) tail.get(map);
}

1
আমি মনে করি আমি যোগ চাই ClassCastExceptionথেকে catchক্ষেত্রে শুধু tailএকটি নয় Entryএকটি উপশ্রেণী (অথবা ভবিষ্যতে বাস্তবায়ন) এ।
পল বোডিংটন

@ পলবডিংটন আমি এটিকে সমস্ত ক্যাচ দিয়ে প্রতিস্থাপন করেছি - সাধারণত প্রস্তাবিত নয় তবে সম্ভবত এখানে উপযুক্ত।
assylias

7
আহ

6

লিংকডহ্যাশম্যাপের প্রথম এবং শেষ প্রবেশের আরও একটি উপায় হ'ল সেট ইন্টারফেসের "টু অ্যারে" পদ্ধতিটি ব্যবহার করা।

তবে আমি মনে করি এন্ট্রি সেটে প্রবেশের উপরে পুনরাবৃত্তি করা এবং প্রথম এবং শেষ এন্ট্রি পাওয়া আরও ভাল পদ্ধতির।

অ্যারে পদ্ধতিগুলির ব্যবহারের ফলে "... ... অনুসারে আনচেক করা রূপান্তর প্রয়োজন ..." যা ফিক্স করা যায় না [তবে এটি কেবলমাত্র মন্তব্য @ সাপ্লাইস ওয়ার্নিংস ("চেক না করা")] ব্যবহার করে চাপা দেওয়া যেতে পারে] of

"টু অ্যারে" পদ্ধতির ব্যবহার প্রদর্শন করার জন্য এখানে একটি ছোট উদাহরণ রয়েছে:

public static void main(final String[] args) {
    final Map<Integer,String> orderMap = new LinkedHashMap<Integer,String>();
    orderMap.put(6, "Six");
    orderMap.put(7, "Seven");
    orderMap.put(3, "Three");
    orderMap.put(100, "Hundered");
    orderMap.put(10, "Ten");

    final Set<Entry<Integer, String>> mapValues = orderMap.entrySet();
    final int maplength = mapValues.size();
    final Entry<Integer,String>[] test = new Entry[maplength];
    mapValues.toArray(test);

    System.out.print("First Key:"+test[0].getKey());
    System.out.println(" First Value:"+test[0].getValue());

    System.out.print("Last Key:"+test[maplength-1].getKey());
    System.out.println(" Last Value:"+test[maplength-1].getValue());
}

// the output geneated is :
First Key:6 First Value:Six
Last Key:10 Last Value:Ten


4
টুআরাই পদ্ধতিটি নিজেই আবার হ্যাশম্যাপে পুনরাবৃত্তি করবে :) সুতরাং অ্যারে তৈরিতে অতিরিক্ত অতিরিক্ত চক্র অপচয় এবং অ্যারের জন্য বরাদ্দকৃত স্থানের কারণে এটি বরং আরও অদক্ষ।
ডুরিন

5

এটি কিছুটা নোংরা, তবে আপনি removeEldestEntryলিঙ্কডহ্যাশম্যাপের পদ্ধতিটি ওভাররাইড করতে পারেন , এটি আপনার ব্যক্তিগত অনামী সদস্য হিসাবে করা উপযুক্ত হতে পারে:

private Splat eldest = null;
private LinkedHashMap<Integer, Splat> pastFutures = new LinkedHashMap<Integer, Splat>() {

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Splat> eldest) {

        eldest = eldest.getValue();
        return false;
    }
};

সুতরাং আপনি সর্বদা আপনার eldestসদস্যের প্রথম এন্ট্রি পেতে সক্ষম হবেন । এটি প্রতিবার আপনি যখন সম্পাদন করবেন তখন আপডেট হবে put

ওভাররাইড putএবং সেট করাও সহজ হওয়া উচিত youngest...

    @Override
    public Splat put(Integer key, Splat value) {

        youngest = value;
        return super.put(key, value);
    }

আপনি যদিও এন্ট্রি অপসারণ শুরু করবেন তখন এটি ভেঙে যায়; এটিকে নষ্ট করার উপায় বের করেনি।

এটি খুব বিরক্তিকর যে আপনি অন্যথায় বুদ্ধিমান উপায়ে মাথা বা লেজ অ্যাক্সেস পেতে পারবেন না ...


ভাল চেষ্টা করুন, তবে মানচিত্র.জেটের অনুরোধ করা হলে সবচেয়ে বড় এন্ট্রি আপডেট করা হয়। সুতরাং জেনারেল এন্ট্রি সেই ক্ষেত্রে আপডেট করা হবে না, যদি না তার পরে বলা হয়।
বিশশ

নির্বাচিত অনুরোধ করা হলে @ বিশার জ্যেষ্ঠ এন্ট্রি আপডেট হয়। (অ্যাক্সেস অর্ডার
লিংকডহ্যাশম্যাপের

2

সম্ভবত এরকম কিছু:

LinkedHashMap<Integer, String> myMap;

public String getFirstKey() {
  String out = null;
  for (int key : myMap.keySet()) {
    out = myMap.get(key);
    break;
  }
  return out;
}

public String getLastKey() {
  String out = null;
  for (int key : myMap.keySet()) {
    out = myMap.get(key);
  }
  return out;
}

2

প্রস্তাবনা:

map.remove(map.keySet().iterator().next());

এটি মানচিত্রে প্রথম keyোকানো কীটি দেয়। প্রথম কীটি সন্ধান করা হবে ও (1) এ। এখানে সমস্যাটি সর্বশেষ sertedোকানো কীটি খুঁজে পেতে ও (1) এর উপায় খুঁজে পাচ্ছে।
এমাদ অগাহেই

1

আমি কনক্র্যান্টস্কিপলিস্টম্যাপ ব্যবহার করার পরামর্শ দিচ্ছি যা রয়েছে firstKey()এবং lastKey()পদ্ধতি রয়েছে


1
সাম্প্রতিকস্কিপলিস্টম্যাপের জন্য একটি তুলনাকারী (বা প্রাকৃতিক তুলনাকারী) প্রয়োজন, যাতে প্রবেশের ক্রমগুলি সংরক্ষণ করার জন্য আপনাকে অতিরিক্ত কাজ করতে হবে।
এলিকএলজিন-কিলাকা

হ্যাশম্যাপ এবং বিশেষত লিংকডহ্যাশম্যাপে ও (1) এর গড় হারে অ্যাক্সেস সরবরাহ করে - স্কিপলিস্টের বিপরীতে, যা ও (লগন) -এর গড় অ্যাক্সেস সরবরাহ করে।
অ্যালিকেলজিন-কিলাকা

"মানচিত্রটি এর কীগুলির প্রাকৃতিক ক্রম অনুসারে বাছাই করা হয়েছে"

1

প্রথম উপাদানটির জন্য entrySet().iterator().next()এবং 1 পুনরাবৃত্তির পরে পুনরাবৃত্তি বন্ধ করুন। শেষের জন্য সবচেয়ে সহজ উপায় হ'ল যখনই আপনি কোনও মানচিত্র.পুট করবেন তখন চলকটিতে কীটি সংরক্ষণ করা।


0

যদিও লিংকড হ্যাশম্যাপ প্রথম, শেষ বা কোনও নির্দিষ্ট অবজেক্ট পাওয়ার কোনও পদ্ধতি সরবরাহ করে না।

তবে এটি পেতে বেশ তুচ্ছ

  • মানচিত্রের অর্ডারম্যাপ = নতুন লিংকডহ্যাশম্যাপ ();
    Al = অর্ডারম্যাপ.কিসেট () সেট করুন;

এখন একটি বস্তুতে পুনরুক্তি ব্যবহার করে; আপনি যে কোনও বস্তু পেতে পারেন।


0

হ্যাঁ আমি একই সমস্যাটি পেরিয়ে এসেছি তবে ভাগ্যক্রমে আমার কেবল প্রথম উপাদানটির প্রয়োজন ... - এটিই আমি এর জন্য করেছি।

private String getDefaultPlayerType()
{
    String defaultPlayerType = "";
    for(LinkedHashMap.Entry<String,Integer> entry : getLeagueByName(currentLeague).getStatisticsOrder().entrySet())
    {
        defaultPlayerType = entry.getKey();
        break;
    }
    return defaultPlayerType;
}

আপনারও যদি সর্বশেষ উপাদানটির প্রয়োজন হয় - আমি কীভাবে আপনার মানচিত্রের ক্রমটি বিপরীত করতে হবে তা সন্ধান করব - এটি একটি অস্থায়ী ভেরিয়েবলে সংরক্ষণ করুন, বিপরীত মানচিত্রে প্রথম উপাদানটি অ্যাক্সেস করুন (সুতরাং এটি আপনার শেষ উপাদান হবে), মেরে ফেলুন টেম্প ভেরিয়েবল

হ্যাশম্যাপকে কীভাবে বিপরীত করতে হবে সে সম্পর্কে এখানে কয়েকটি ভাল উত্তর রয়েছে:

জাভাতে বিপরীত ক্রমে হ্যাশম্যাপ পুনরুক্তি করা যায় কীভাবে

আপনি যদি উপরের লিঙ্কটি থেকে সহায়তা ব্যবহার করেন, দয়া করে তাদের আপ-ভোট দিন :) আশা করি এটি কারওর পক্ষে সহায়তা করতে পারে।


0

ডান, আপনি লিঙ্কযুক্ত তালিকার শেষ পর্যন্ত কীসেটটি ম্যানুয়ালি অঙ্কিত করতে হবে, তারপরে কী দ্বারা এন্ট্রিটি পুনরুদ্ধার করুন এবং এই এন্ট্রিটি ফিরে পাবেন return


0
public static List<Fragment> pullToBackStack() {
    List<Fragment> fragments = new ArrayList<>();
    List<Map.Entry<String, Fragment>> entryList = new ArrayList<>(backMap.entrySet());
    int size = entryList.size();
    if (size > 0) {
        for (int i = size - 1; i >= 0; i--) {// last Fragments
            fragments.add(entryList.get(i).getValue());
            backMap.remove(entryList.get(i).getKey());
        }
        return fragments;
    }
    return null;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.