আমি কীভাবে নিরাপদে সংগ্রহগুলি অনুলিপি করতে পারি?


9

অতীতে, আমি সংগ্রহটি নিরাপদে কিছু অনুলিপি করতে বলেছিলাম যেমন কিছু করুন:

public static void doThing(List<String> strs) {
    List<String> newStrs = new ArrayList<>(strs);

অথবা

public static void doThing(NavigableSet<String> strs) {
    NavigableSet<String> newStrs = new TreeSet<>(strs);

তবে এই "অনুলিপি" নির্মাতারা, অনুরূপ স্থিতিশীল তৈরির পদ্ধতি এবং স্ট্রিমগুলি কি সত্যই নিরাপদ এবং নিয়মগুলি কোথায় নির্দিষ্ট করা হয়েছে? নিরাপদে আমার অর্থ জাভা ভাষায় প্রদত্ত বেসিক সিমেটিক অখণ্ডতা গ্যারান্টি হ'ল এবং কোনও ক্ষতিকারক কলারের বিরুদ্ধে প্রয়োগ করা হয়েছে, যুক্তিসঙ্গতভাবে ব্যাক আপ গ্রহণ করে SecurityManagerএবং কোনও ত্রুটি নেই।

আমি পদ্ধতি নিক্ষেপ সঙ্গে খুশি ConcurrentModificationException, NullPointerException, IllegalArgumentException, ClassCastExceptionইত্যাদি অথবা সম্ভবত এমনকি ঝুলন্ত।

আমি Stringঅপরিবর্তনীয় ধরণের যুক্তির উদাহরণ হিসাবে বেছে নিয়েছি । এই প্রশ্নের জন্য, আমি পরিবর্তনশীল ধরণের সংগ্রহের গভীর কপিগুলিতে আগ্রহী নই যার নিজস্ব গটছ রয়েছে।

(স্পষ্টরূপে, আমি ওপেনজেডিকে উত্স কোডটি দেখেছি এবং এর জন্য ArrayListএবং এর একরকম উত্তর পেয়েছি TreeSet))


2
আপনি নিরাপদ মানে কি ? সংগ্রহের কাঠামোতে সাধারণত ক্লাসের কথা বলতে জাভাদোকগুলিতে নির্দিষ্ট ব্যতিক্রমগুলি একইভাবে কাজ করতে থাকে। অনুলিপি নির্মাণকারীরা অন্য নির্মাণকারীর মতোই "নিরাপদ"। আপনার মনে যে কোনও বিশেষ জিনিস রয়েছে, কারণ কোনও সংগ্রহের অনুলিপি নির্মাণকারী নিরাপদ কিনা তা জিজ্ঞাসা করা খুব সুনির্দিষ্ট মনে হচ্ছে?
কায়মন

1
ভাল, NavigableSetএবং অন্যান্য Comparableভিত্তিক সংগ্রহগুলি কখনও কখনও সনাক্ত করতে পারে যে কোনও শ্রেণি compareTo()সঠিকভাবে প্রয়োগ করে না এবং একটি ব্যতিক্রম ছুঁড়ে দেয়। অবিশ্বস্ত যুক্তি দ্বারা আপনি কী বোঝাতে চাইছেন এটি কিছুটা অস্পষ্ট। আপনি বলতে চাইছেন যে একজন দুষ্টু লোক খারাপ স্ট্রিংয়ের একটি সংগ্রহশালা তৈরি করে এবং আপনি যখন সেগুলি আপনার সংগ্রহে অনুলিপি করেন তখন কোনও খারাপ ঘটনা ঘটে? না, সংগ্রহের কাঠামোটি বেশ শক্ত, এটি প্রায় 1.2 এর কাছাকাছি।
কায়মন

1
@JesseWilson আপনি তাদের অভ্যন্তরীণ, হ্যাকিং ছাড়া মান সংগ্রহ অনেকটা আপোস করতে পারে HashSet(এবং সাধারণভাবে অন্যান্য সকল হ্যাশ সংগ্রহ) শুদ্ধি / অখণ্ডতা উপর নির্ভর hashCodeউপাদানের প্রয়োগ, TreeSetএবং PriorityQueueউপর নির্ভর করে Comparator(এবং আপনি এমনকি করতে পারেন না কাস্টম তুলনাকারী না থাকলে একটি সমতুল্য অনুলিপি তৈরি করুন), EnumSetনির্দিষ্ট enumধরণের অখণ্ডতার উপর ভরসা করে যা সংকলনের পরে কখনই যাচাই করা হয় না, সুতরাং কোনও শ্রেণি ফাইল, উত্পন্ন javacবা হস্তশিল্প দ্বারা উত্পাদিত নয় , এটি বিকৃত করতে পারে।
হলগার

1
আপনার উদাহরণে, আপনি new TreeSet<>(strs)যেখানে strsএকটি হল NavigableSet। এটি একটি বাল্ক অনুলিপি নয়, ফলস্বরূপ TreeSetউত্সের তুলকটি ব্যবহার করা হবে, যা শব্দার্থবিজ্ঞান ধরে রাখতে এমনকি প্রয়োজনীয়। যদি আপনি কেবল অন্তর্ভুক্ত উপাদানগুলিকে প্রক্রিয়াকরণ দিয়ে ঠিক থাকেন toArray()তবে যাবার উপায়; এমনকি এটি পুনরাবৃত্তির ক্রমটি রাখবে। আপনি যখন "উপাদান নিন, উপাদানটি কার্যকর করুন, উপাদানটি ব্যবহার করুন" দিয়ে ভাল থাকেন, আপনার এমনকি অনুলিপি তৈরি করার দরকার নেই। আপনি যখন সমস্ত উপাদান ব্যবহার করে সমস্ত উপাদান যাচাই করতে চান তখন সমস্যা শুরু হয় all তারপরে, আপনি কোনও TreeSetঅনুলিপি কাস্টম তুলককে বিশ্বাস করতে পারবেন না
হলগার

1
একমাত্র বাল্ক অনুলিপি অপারেশন checkcastপ্রতিটি উপাদান জন্য একটি এর প্রভাব আছে , toArrayএকটি নির্দিষ্ট ধরনের সঙ্গে হয়। আমরা সবসময় এটি শেষ করছি। জেনেরিক সংগ্রহগুলি এমনকি তাদের আসল উপাদানটির প্রকারটি জানে না, তাই তাদের অনুলিপি নির্মাণকারীরা অনুরূপ কার্যকারিতা সরবরাহ করতে পারে না। অবশ্যই, আপনি পূর্বের ব্যবহারের জন্য কোনও পরীক্ষা স্থগিত করতে পারেন, তবে তারপরে, আপনার প্রশ্নগুলি কী লক্ষ্য করছে তা আমি জানি না। উপাদানগুলির ব্যবহার করার আগে অবিলম্বে চেক করা এবং ব্যর্থ হওয়াতে আপনি যখন ভাল থাকেন তখন আপনার "সিমেটিক অখণ্ডতা" লাগবে না।
হলগার

উত্তর:


12

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

সহজেই প্রদর্শিত হতে পারে:

public static void main(String[] args) throws InterruptedException {
    Object[] array = { "foo", "bar", "baz", "and", "another", "string" };
    array[array.length - 1] = new Object() {
        @Override
        public String toString() {
            Collections.shuffle(Arrays.asList(array));
            return "string";
        }
    };
    doThing(new ArrayList<String>() {
        @Override public Object[] toArray() {
            return array;
        }
    });
}

public static void doThing(List<String> strs) {
    List<String> newStrs = new ArrayList<>(strs);

    System.out.println("made a safe copy " + newStrs);
    for(int i = 0; i < 10; i++) {
        System.out.println(newStrs);
    }
}
made a safe copy [foo, bar, baz, and, another, string]
[bar, and, string, string, another, foo]
[and, baz, bar, string, string, string]
[another, baz, and, foo, bar, string]
[another, bar, and, foo, string, and]
[another, baz, string, another, and, foo]
[string, and, another, foo, string, foo]
[baz, string, foo, and, baz, string]
[bar, another, string, and, another, baz]
[bar, string, foo, string, baz, and]
[bar, string, bar, another, and, foo]

যেমন আপনি দেখতে পাচ্ছেন, প্রত্যাশা করা List<String>গ্যারান্টিটি আসলে Stringউদাহরণগুলির একটি তালিকা পাবে না । মুছে ফেলা এবং কাঁচা ধরণের প্রকারের কারণে, তালিকা বাস্তবায়ন পক্ষের পক্ষে এমনকি কোনও ঠিক করা সম্ভব হয়নি।

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

সাধারণত, প্রতিটি কোণার চারপাশে ইচ্ছাকৃতভাবে দূষিত কোড অনুমান করে কোড লেখার চেষ্টা করার কোনও মানে নেই। সবকিছুর বিরুদ্ধে রক্ষা করার জন্য এটি অনেক কিছু করতে পারে। এই জাতীয় সুরক্ষা কেবল কোডের জন্য কার্যকর যা কোনও ক্রিয়াকে সত্যই কার্যকর করে যা কোনও দূষিত কলারকে কিছুতে অ্যাক্সেস দিতে পারে, এই কোডটি ছাড়াই এটি ইতিমধ্যে অ্যাক্সেস করতে পারে না।

আপনার যদি কোনও নির্দিষ্ট কোডের সুরক্ষা প্রয়োজন হয় তবে ব্যবহার করুন

public static void doThing(List<String> strs) {
    String[] content = strs.toArray(new String[0]);
    List<String> newStrs = new ArrayList<>(Arrays.asList(content));

    System.out.println("made a safe copy " + newStrs);
    for(int i = 0; i < 10; i++) {
        System.out.println(newStrs);
    }
}

তারপরে, আপনি নিশ্চিত হতে পারেন যে newStrsএতে কেবল স্ট্রিং রয়েছে এবং এটি নির্মাণের পরে অন্য কোড দ্বারা সংশোধিত হতে পারে না।

বা List<String> newStrs = List.of(strs.toArray(new String[0]));জাভা 9 বা আরও নতুন ব্যবহার করুন
নোট করুন যে জাভা 10 এর List.copyOf(strs)একই কাজ করে তবে এর ডকুমেন্টেশনে উল্লেখ করা হয় না যে এটি আগত সংগ্রহের toArrayপদ্ধতিতে বিশ্বাস না করার নিশ্চয়তা দেওয়া হয়েছে । সুতরাং কলিং List.of(…), এটি অবশ্যই একটি অনুলিপি তৈরি করবে যদি এটি অ্যারে ভিত্তিক তালিকাটি ফেরত দেয় তবে এটি নিরাপদ।

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

সুতরাং নির্দিষ্ট উপাদানটি অ্যারে থেকে সংগ্রহ করার পরে বা সামগ্রিকভাবে ফলাফল সংগ্রহের পরে কোনও ধারাবাহিকতা পরীক্ষা করা উচিত।


2
জাভাটির সুরক্ষা মডেল স্ট্যাকের সমস্ত কোডের অনুমতি সেটগুলিকে ছেদ দেওয়ার কোডটি দিয়ে কাজ করে, সুতরাং যখন আপনার কোডের কলকারী আপনার কোডকে অনিচ্ছাকৃত কাজ করতে বাধ্য করে, তখনও এটি প্রথমে এর চেয়ে বেশি অনুমতি পায় না। সুতরাং এটি কেবল আপনার কোডটি এমন কিছু কাজ করে যা দূষিত কোডটি আপনার কোড ছাড়াও করতে পারে। আপনাকে কেবলমাত্র AccessController.doPrivileged(…)ইত্যাদি দ্বারা উন্নত সুবিধাগুলি দিয়ে চালানোর কোডটি কঠোর করতে হবে তবে অ্যাপলেট সুরক্ষা সম্পর্কিত বাগগুলির দীর্ঘ তালিকা আমাদের এই ইঙ্গিত দেয় যে কেন এই প্রযুক্তিটি পরিত্যক্ত করা হয়েছে ...
হোলার

1
তবে আমার "কালেকশন এপিআই এর মতো সাধারণ এপিআইগুলিতে" ”োকানো উচিত ছিল, যেমনটি আমি উত্তরে ফোকাস করছিলাম।
হোলার

2
দূষিত সংগ্রহের প্রয়োগ বাস্তবায়নের পিছনে প্রবেশের অনুমতি দেয় এমন অধিকারী কোডের বিরুদ্ধে আপনার কোডটি কেন স্পষ্টভাবে সুরক্ষা প্রাসঙ্গিক নয়? সেই কাল্পনিক কলারটি এখনও আপনার কোড কল করার আগে এবং পরে দূষিত আচরণের সাপেক্ষে থাকবে। এমনকি আপনার খেয়ালও করা হবে না যে আপনার কোডটি সঠিকভাবে আচরণ করছে। new ArrayList<>(…)অনুলিপি নির্মাতা হিসাবে ব্যবহার করা সঠিক সংগ্রহের বাস্তবায়ন অনুমান করে is ইতিমধ্যে যখন খুব দেরি হয়ে গেছে তখন সুরক্ষা সমস্যা সমাধান করা আপনার দায়িত্ব নয়। আপোস করা হার্ডওয়্যার সম্পর্কে কী? অপারেটিং সিস্টেম? মাল্টি থ্রেডিং সম্পর্কে কীভাবে?
হোলার

2
আমি সত্যের পরে কোনও ভাঙ্গা পরিবেশ ঠিক করার চেষ্টা করার পরিবর্তে, "কোনও সুরক্ষা নয়", তবে সঠিক জায়গাগুলিতে সুরক্ষা দেওয়ার পক্ষে নিচ্ছি। এটি একটি আকর্ষণীয় দাবি যে " এখানে অনেকগুলি সংগ্রহ রয়েছে যা তাদের সুপারটাইপগুলি সঠিকভাবে প্রয়োগ করে না " তবে এটি আরও বেশি প্রসারিত হয়ে প্রমাণ করার জন্য জিজ্ঞাসা করার জন্য অনেক আগে থেকেই চলে গিয়েছিল। মূল প্রশ্নের সম্পূর্ণ উত্তর দেওয়া হয়েছে; আপনি এখন যে পয়েন্টগুলি নিয়ে আসছেন তা কখনই এর অংশ ছিল না। যেমনটি বলা হয়েছে, List.copyOf(strs)সুস্পষ্ট মূল্যে সেই বিষয়ে আগত সংগ্রহের নির্ভুলতার উপর নির্ভর করবে না। ArrayListএকটি যুক্তিসঙ্গত-প্রতিদিনের সমঝোতা।
হলগার

4
এটি পরিষ্কারভাবে বলেছে যে সমস্ত "অনুরূপ স্থিতিশীল তৈরির পদ্ধতি এবং স্ট্রিম" এর জন্য এ জাতীয় কোনও স্পেসিফিকেশন নেই। তাই আপনি যদি একেবারে নিরাপদ চান, আপনি ফোন করতে হবে toArray(), নিজেকে কারণ অ্যারে উপেক্ষিত আচরণ, মত বিন্যাস একটি সংগ্রহ অনুলিপি তৈরি, দ্বারা অনুসরণ থাকতে পারে না new ArrayList<>(Arrays.asList( strs.toArray(new String[0])))বা List.of(strs.toArray(new String[0]))। উভয়েরও উপাদান ধরণের প্রয়োগের পার্শ্ব প্রতিক্রিয়া রয়েছে। আমি ব্যক্তিগতভাবে মনে করি না যে তারা কখনও copyOfঅপরিবর্তনীয় সংগ্রহগুলি নিয়ে আপস করার অনুমতি দেবে , তবে উত্তরটিতে বিকল্প রয়েছে।
হলগার

1

আমি বরং এই তথ্যটি মন্তব্যে রেখে দিয়েছি, তবে আমার যথেষ্ট খ্যাতি নেই, দুঃখিত :) আমি যতটা পারি তারপরে এটি যথাসম্ভব ব্যাখ্যা করার চেষ্টা করব।

constসদস্য ফাংশনগুলি চিহ্নিত করার জন্য সি ++-তে ব্যবহৃত কিছু পরিবর্তকের মতো পরিবর্তে যা বস্তুর বিষয়বস্তুগুলিকে সংশোধন করার কথা নয়, জাভাতে মূলত "অপরিবর্তনযোগ্যতা" ধারণাটি ব্যবহৃত হয়েছিল। এনক্যাপসুলেশন (বা ওসিপি, ওপেন-ক্লোজড প্রিন্সিপাল) কোনও বস্তুর কোনও অপ্রত্যাশিত পরিবর্তন (পরিবর্তন) থেকে রক্ষা করার কথা ছিল। অবশ্যই প্রতিচ্ছবি API এ ঘুরে বেড়ায়; সরাসরি মেমরি অ্যাক্সেস একই কাজ করে; যে নিজের পা শুটিং সম্পর্কে আরও বেশি :)

java.util.Collectionনিজেই পরিবর্তনীয় ইন্টারফেস: এটির addপদ্ধতি রয়েছে যা সংগ্রহটি পরিবর্তন করতে পারে। অবশ্যই প্রোগ্রামার এমন কিছুতে সংগ্রহটি গুটিয়ে রাখতে পারে যা ছোঁড়াবে ... এবং সমস্ত রানটাইম ব্যতিক্রম ঘটবে কারণ অন্য কোনও প্রোগ্রামার জাভাদোক পড়তে সক্ষম হননি যা স্পষ্টভাবে বলে যে সংগ্রহটি অপরিবর্তনীয়।

আমি java.util.Iterableআমার ইন্টারফেসে অপরিবর্তনীয় সংগ্রহ প্রকাশের জন্য প্রকারটি ব্যবহার করার সিদ্ধান্ত নিয়েছি । শব্দার্থকভাবে Iterable"মিউটেবিলিটি" হিসাবে সংগ্রহের তেমন বৈশিষ্ট্য নেই। তবুও আপনি (সম্ভবত সম্ভবত) স্ট্রিমগুলির মাধ্যমে অন্তর্নিহিত সংগ্রহগুলি সংশোধন করতে সক্ষম হবেন।


জেআইসি, অচল পদ্ধতিতে মানচিত্র উন্মোচন java.util.Function<K,V>করতে ব্যবহার করা যেতে পারে (মানচিত্রের getপদ্ধতিটি এই সংজ্ঞায় ফিট করে)


কেবল পঠনযোগ্য ইন্টারফেস এবং অপরিবর্তনীয়তার ধারণাগুলি অরথোগোনাল। সি ++ এবং সি এর মুল বক্তব্যটি তারা শব্দার্থক অখণ্ডতা সমর্থন করে না । এছাড়াও অবজেক্ট / স্ট্রাক্ট আর্গুমেন্টগুলি অনুলিপি করুন - কনস্ট এবং এটির জন্য একটি ছদ্মবেশী অপ্টিমাইজেশন। আপনি যদি কোনও পাস করতে পারেন Iteratorতবে এটি কার্যতঃ একটি এলিমিটাইজড কপি জোর করে, তবে এটি খুব সুন্দর নয়। forEachRemaining/ ব্যবহার forEachকরা স্পষ্টতই একটি সম্পূর্ণ বিপর্যয় হতে চলেছে। (আমারও Iteratorএর একটি removeপদ্ধতি আছে তা উল্লেখ করতে হবে ))
টম হাটিন - ১১

স্কালা সংগ্রহ লাইব্রেরিতে যদি তাকান তবে পরিবর্তনীয় এবং অপরিবর্তনীয় ইন্টারফেসের মধ্যে কঠোর পার্থক্য রয়েছে। যদিও (আমি মনে করি) এটি সম্পূর্ণ ভিন্ন কারণে তৈরি হয়েছিল তবে এখনও কীভাবে সুরক্ষা পাওয়া যায় তার একটি প্রদর্শনী। কেবলমাত্র পঠন ইন্টারফেসটি শব্দার্থভাবে অপরিবর্তনীয়তা ধরে নিয়েছে, এটাই আমি বলার চেষ্টা করছি। (আমি Iterableপ্রকৃতপক্ষে অপরিবর্তনীয় নয় এমন বিষয়ে সম্মত হই তবে এর সাথে কোনও সমস্যা দেখি না forEach*)
আলেকজান্ডার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.