JavaRush /مدونة جافا /Random-AR /السلاسل في Java (فئة java.lang.String)
Viacheslav
مستوى

السلاسل في Java (فئة java.lang.String)

نشرت في المجموعة

مقدمة

مسار المبرمج عملية معقدة وطويلة. وفي معظم الحالات يبدأ الأمر ببرنامج يعرض Hello World على الشاشة. Java ليست استثناءً (راجع الدرس: تطبيق "Hello World!" ). كما نرى، يتم إخراج الرسالة باستخدام System.out.println("Hello World!"); Java API، فستجد أن الأسلوب System.out.println يأخذ السلسلة كمعلمة إدخال . سيتم مناقشة هذا النوع من البيانات.

سلسلة كسلسلة من الأحرف

في الواقع، السلسلة المترجمة من الإنجليزية هي سلسلة. هذا صحيح، نوع السلسلة يمثل سلسلة نصية. ما هي السلسلة النصية؟ السلسلة النصية هي نوع من التسلسل المرتب للأحرف التي تتبع بعضها البعض. الرمز هو شار. تسلسل – تسلسل. لذا نعم، هذا صحيح تمامًا، String عبارة عن تطبيق لـ java.lang.CharSequence. وإذا نظرت داخل فئة السلسلة نفسها، فلن تجد بداخلها سوى مجموعة من الأحرف: private final char value[]; فهي تحتوي على java.lang.CharSequenceعقد بسيط إلى حد ما:
السلاسل في Java (فئة java.lang.String) - 1
لدينا طريقة للحصول على عدد العناصر، والحصول على عنصر محدد والحصول على مجموعة من العناصر + طريقة toString نفسها، والتي ستعيد هذا) من المثير للاهتمام فهم الأساليب التي جاءت إلينا في Java 8، وهذا هو : chars()وتذكر codePoints() من البرنامج التعليمي لأنواع "البيانات الأولية" من Oracle أن char هو single 16-bit Unicode character. أي أن char في الأساس هو مجرد نوع نصف حجم int (32 بت) يمثل أرقامًا من 0 إلى 65535 (انظر القيم العشرية في جدول ASCII ). وهذا يعني، إذا أردنا، يمكننا تمثيل char كـ int. وقد استفاد Java 8 من هذا. بدءًا من الإصدار 8 من Java، لدينا IntStream - وهو تيار للعمل مع ints البدائية. لذلك، في charSequence من الممكن الحصول على IntStream الذي يمثل إما chars أو codePoints. وقبل أن ننتقل إليهم، سنرى مثالاً يوضح مدى ملاءمة هذا النهج. دعنا نستخدم برنامج Tutorialspoint online java compiler وننفذ الكود:
public static void main(String []args){
        String line = "aaabccdddc";
        System.out.println( line.chars().distinct().count() );
}
يمكنك الآن الحصول على عدد من الرموز الفريدة بهذه الطريقة البسيطة.

نقاط كود بوينتس

لذلك، رأينا حول الأحرف. الآن ليس من الواضح ما هو نوع نقاط التعليمات البرمجية هذه. ظهر مفهوم codePoint لأنه عندما ظهرت Java، كانت 16 بت (نصف int) كافية لترميز الحرف. لذلك، يتم تمثيل char في Java بتنسيق UTF-16 (مواصفات "Unicode 88"). لاحقًا، ظهر Unicode 2.0، وكان مفهومه هو تمثيل الشخصية كزوج بديل (حرفين). هذا سمح لنا بتوسيع نطاق القيم الممكنة إلى قيمة int. لمزيد من التفاصيل، راجع تدفق المكدس: " مقارنة حرف بنقطة رمز؟ " تم ذكر UTF-16 أيضًا في JavaDoc for Character . هناك، في JavaDoc، يُقال ما يلي: من In this representation, supplementary characters are represented as a pair of char values, the first from the high-surrogates range, (\uD800-\uDBFF), the second from the low-surrogates range (\uDC00-\uDFFF). الصعب جدًا (وربما من المستحيل) إعادة إنتاج ذلك بالأبجدية القياسية. لكن الرموز لا تنتهي بالحروف والأرقام. في اليابان، توصلوا إلى شيء يصعب ترميزه مثل الرموز التعبيرية، وهي لغة الرموز التعبيرية والرموز التعبيرية. هناك مقالة مثيرة للاهتمام حول هذا الموضوع على ويكيبيديا: " Emoji ". لنجد مثالاً على أحد الرموز التعبيرية، على سبيل المثال هذا: " Emoji Ghost ". كما نرى، تمت الإشارة إلى نفس codePoint هناك (القيمة = U+1F47B). يشار إليه بالتنسيق الست عشري. إذا قمنا بالتحويل إلى رقم عشري، نحصل على 128123. وهذا يسمح بأكثر من 16 بت (أي أكثر من 65535). دعنا ننسخها:
سلاسل في جافا (فئة java.lang.String) - 2
لسوء الحظ، لا يدعم نظام JavaRush مثل هذه الأحرف في النص. ولذلك، في المثال أدناه سوف تحتاج إلى إدراج قيمة في السلسلة. لذلك، الآن سوف نفهم اختبار بسيط:
public static void main(String []args){
	    String emojiString = "Вставте сюда эмоджи через ctrl+v";
	    //На один emojiString приходится 2 чара (т.к. не влезает в 16 бит)
	    System.out.println(emojiString.codePoints().count()); //1
	    System.out.println(emojiString.chars().count()); //2
}
كما ترون، في هذه الحالة، يتم استخدام نقطة كود واحدة لحرفين. هذا هو السحر.

شخصية

كما رأينا أعلاه، تتكون السلاسل النصية في Java من char. يسمح لك النوع البدائي بتخزين قيمة، لكن الغلاف java.lang.Characterفوق النوع البدائي يسمح لك بعمل الكثير من الأشياء المفيدة باستخدام هذا الرمز. على سبيل المثال، يمكننا تحويل سلسلة إلى أحرف كبيرة:
public static void main(String[] args) {
    String line = "организация объединённых наций";
    char[] chars = line.toCharArray();
    for (int i = 0; i < chars.length; i++) {
        if (i == 0 || chars[i - 1] == ' ') {
            chars[i] = Character.toUpperCase(chars[i]);
        }
    }
    System.out.println(new String(chars));
}
حسنًا، هناك العديد من الأشياء المثيرة للاهتمام: isAlphabetic()، isLetter()، isSpaceChar()، ، isDigit()، isUpperCase()، ، isMirrored()(على سبيل المثال، الأقواس. '(' لها صورة معكوسة ')').

تجمع السلسلة

السلاسل النصية في Java غير قابلة للتغيير، أي ثابتة. تتم الإشارة إلى هذا أيضًا في JavaDoc للفئة java.lang.String نفسها . ثانيًا، وهو أيضًا مهم جدًا، يمكن تحديد السلاسل النصية كنصوص حرفية:
String literalString = "Hello, World!";
String literalString = "Hello, World!";
أي أن أي سلسلة نصية مقتبسة، كما هو مذكور أعلاه، هي في الواقع كائن. وهذا يطرح السؤال - إذا كنا نستخدم السلاسل كثيرًا ويمكن أن تكون هي نفسها في كثير من الأحيان (على سبيل المثال، النص "خطأ" أو "بنجاح")، فهل هناك أي طريقة للتأكد من عدم إنشاء السلاسل في كل مرة؟ بالمناسبة، لا يزال لدينا خرائط، حيث يمكن أن يكون المفتاح عبارة عن سلسلة. ومن ثم، فمن المؤكد أنه لا يمكننا الحصول على نفس السلاسل لكائنات مختلفة، وإلا فلن نتمكن من الحصول على الكائن من الخريطة. لقد فكر مطورو Java وفكروا وتوصلوا إلى String Pool. هذا هو المكان الذي يتم فيه تخزين السلاسل، ويمكنك تسميته بذاكرة التخزين المؤقت للسلسلة. لا تنتهي جميع الأسطر نفسها هناك، ولكن فقط الأسطر المحددة في الكود حرفيًا. يمكنك إضافة سطر إلى التجمع بنفسك، ولكن المزيد عن ذلك لاحقًا. لذلك، في الذاكرة لدينا هذا ذاكرة التخزين المؤقت في مكان ما. سؤال وجيه: أين يقع هذا المسبح؟ يمكن العثور على الإجابة على هذا السؤال في تدفق المكدس: " أين يعيش تجمع Java الثابت للسلسلة، الكومة أم المكدس؟ " وهو موجود في ذاكرة الكومة، في منطقة تجمع ثابتة خاصة بوقت التشغيل. يتم تخصيص تجمع ثابت وقت التشغيل عندما يتم إنشاء فئة أو واجهة بواسطة الجهاز الظاهري من منطقة الطريقة - وهي منطقة خاصة في الكومة يمكن لجميع سلاسل الرسائل داخل Java Virtual Machine الوصول إليها. ماذا يقدم لنا تجمع السلسلة؟ وهذا له العديد من المزايا:
  • لن يتم إنشاء كائنات من نفس النوع
  • تعد المقارنة حسب المرجع أسرع من مقارنة كل حرف على حدة عبر المساواة
ولكن ماذا لو أردنا وضع الكائن الذي تم إنشاؤه في ذاكرة التخزين المؤقت هذه؟ ثم لدينا طريقة خاصة: String.intern تضيف هذه الطريقة سلسلة إلى String Pool. تجدر الإشارة إلى أن هذا ليس مجرد نوع من ذاكرة التخزين المؤقت في شكل مصفوفة (كما هو الحال مع الأعداد الصحيحة). تم تحديد طريقة المتدرب على أنها "أصلية". هذا يعني أن الطريقة نفسها يتم تنفيذها بلغة أخرى (غالبًا C++). في حالة أساليب Java الأساسية، يمكن تطبيق العديد من التحسينات الأخرى عليها على مستوى JVM. بشكل عام، سيحدث السحر هنا. من المثير للاهتمام قراءة المنشور التالي عن المتدرب: https://habr.com/post/79913/#comment_2345814 ويبدو أنها فكرة جيدة. ولكن كيف سيؤثر هذا علينا؟ ولكن سيكون له تأثير حقيقي)
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal");
    System.out.println(test == test2);
}
كما ترون، الخطوط هي نفسها، ولكن النتيجة ستكون خاطئة. وكل ذلك لأن == لا يُقارن بالقيمة، بل بالرجوع. وهذه هي الطريقة التي يعمل بها:
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test == test2);
}
لاحظ فقط أننا سنستمر في إنشاء سلسلة جديدة. أي أن المتدرب سيعيد لنا سلسلة من ذاكرة التخزين المؤقت، ولكن سيتم التخلص من السلسلة الأصلية التي بحثنا عنها في ذاكرة التخزين المؤقت للتنظيف، لأن لا أحد يعرف عنه. من الواضح أن هذا استهلاك غير ضروري للموارد =( لذلك، يجب عليك دائمًا مقارنة السلاسل باستخدام يساوي لتجنب الأخطاء المفاجئة والتي يصعب اكتشافها قدر الإمكان.
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test.equals(test2));
}
يقوم Equals بإجراء مقارنة سلسلة حرفًا بحرف.

سلسلة

وكما نتذكر، يمكن إضافة خطوط. وكما نتذكر، فإن خيوطنا غير قابلة للتغيير. فكيف يعمل بعد ذلك؟ هذا صحيح، يتم إنشاء سطر جديد، والذي يتكون من رموز الكائنات التي تتم إضافتها. هناك مليون نسخة لكيفية عمل التسلسل الإضافي. يعتقد بعض الناس أنه سيكون هناك شيء جديد في كل مرة، ويعتقد آخرون أنه سيكون هناك شيء آخر. لكن قد يكون شخص واحد فقط على حق. وهذا الشخص هو مترجم javac. دعنا نستخدم خدمة المترجم عبر الإنترنت ونقوم بتشغيل:
public class HelloWorld {

    public static void main(String[] args) {
        String helloMessage = "Hello, ";
        String target = "World";
        System.out.println(helloMessage + target);
    }

}
الآن دعونا نحفظ هذا كأرشيف مضغوط، ونستخرجه إلى دليل وننفذه: javap –c HelloWorld وهنا نكتشف كل شيء:
سلاسل في جافا (فئة java.lang.String) - 3
في الحلقة، بالطبع، من الأفضل إجراء التسلسل عبر StringBuilder بنفسك. وليس بسبب نوع من السحر، ولكن بحيث يتم إنشاء StringBuilder قبل الدورة، وفي الدورة نفسها يحدث الإلحاق فقط. بالمناسبة، هناك شيء آخر مثير للاهتمام هنا. هناك مقال ممتاز: " معالجة السلسلة في Java. الجزء الأول: سلسلة، StringBuffer، StringBuilder ." الكثير من المعلومات المفيدة في التعليقات. على سبيل المثال، تم تحديد أنه عند تسلسل عرض، new StringBuilder().append()...toString()يكون التحسين الجوهري ساري المفعول، ويتم تنظيمه بواسطة خيار -XX:+OptimizeStringConcat، والذي يتم تمكينه افتراضيًا. جوهري - يُترجم إلى "داخلي". يتعامل JVM مع مثل هذه الأشياء بطريقة خاصة، ويعالجها على أنها أصلية، فقط دون التكاليف الإضافية التي تتحملها JNI. اقرأ المزيد: " الطرق الجوهرية في HotSpot VM ".

StringBuilder وStringBuffer

كما رأينا أعلاه، StringBuilder هي أداة مفيدة جدًا. السلاسل غير قابلة للتغيير، أي. غير قابل للتغيير. وأريد طيها. لذلك، تم منحنا فئتين لمساعدتنا: StringBuilder وStringBuffer. الفرق الرئيسي بين الاثنين هو أن StringBuffer تم تقديمه في JDK1.0، بينما جاء StringBuilder في Java 1.5 كإصدار غير متزامن من StringBuffer للتخلص من الحمل الزائد لمزامنة الطريقة غير الضرورية. كلا هاتين الفئتين عبارة عن تطبيقات للفئة المجردة AbstractStringBuilder - تسلسل قابل للتغيير من الأحرف. يتم تخزين مجموعة من التعويذات بالداخل، والتي يتم توسيعها وفقًا للقاعدة: value.length * 2 + 2. افتراضيًا، يكون حجم (سعة) StringBuilder هو 16.

قابلة للمقارنة

السلاسل قابلة للمقارنة، أي. تنفيذ طريقة المقارنة. ويتم ذلك باستخدام المقارنة بين الأحرف. ومن المثير للاهتمام أنه يتم تحديد الحد الأدنى للطول من سلسلتين ويتم تنفيذ حلقة فوقه. لذلك، سيقوم CompareTo إما بإرجاع الفرق بين قيم int للأحرف الأولى غير المتطابقة حتى أصغر طول سلسلة، أو إرجاع الفرق بين أطوال السلسلة إذا كانت جميع الأحرف متطابقة ضمن الحد الأدنى لطول السلسلة. وتسمى هذه المقارنة "معجمية".

العمل مع سلاسل جافا

تحتوي السلسلة على العديد من الطرق المفيدة:
السلاسل في Java (فئة java.lang.String) - 4
هناك العديد من المهام للعمل مع السلاسل. على سبيل المثال، في Coding Bat . هناك أيضًا دورة تدريبية على كورسيرا: " خوارزميات السلاسل ".

خاتمة

حتى نظرة عامة قصيرة على هذا الفصل تشغل مساحة هائلة. وهذا ليس كل شيء. أوصي بشدة بمشاهدة التقرير من JPoint 2015: Alexey Shipilev - التعليم المسيحي java.lang.String
# فياتشيسلاف
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION