JavaRush /وبلاگ جاوا /Random-FA /رشته ها در جاوا (کلاس java.lang.String)
Viacheslav
مرحله

رشته ها در جاوا (کلاس java.lang.String)

در گروه منتشر شد

معرفی

مسیر یک برنامه نویس یک فرآیند پیچیده و طولانی است. و در بیشتر موارد با برنامه ای شروع می شود که Hello World را روی صفحه نمایش می دهد. جاوا نیز از این قاعده مستثنی نیست (به درس: برنامه "Hello World!" مراجعه کنید ). همانطور که می بینیم، پیام خروجی با استفاده System.out.println("Hello World!"); از API جاوا می شود، متد System.out.println String را به عنوان پارامتر ورودی می گیرد . این نوع داده ها مورد بحث قرار خواهد گرفت.

رشته به عنوان دنباله ای از کاراکترها

در واقع، رشته ترجمه شده از انگلیسی یک رشته است. درست است، نوع String نشان دهنده یک رشته متنی است. رشته متن چیست؟ رشته متن نوعی توالی مرتب از کاراکترها است که به دنبال یکدیگر می آیند. نماد کاراکتر است. دنباله - دنباله. بنابراین بله، کاملا درست است، String یک پیاده سازی از java.lang.CharSequence. و اگر به داخل خود کلاس String نگاه کنید، در داخل آن چیزی بیش از یک آرایه از کاراکترها وجود ندارد: یک قرارداد نسبتاً ساده private final char value[]; دارد :java.lang.CharSequence
رشته ها در جاوا (کلاس java.lang.String) - 1
ما یک روش برای بدست آوردن تعداد عناصر، گرفتن یک عنصر خاص و گرفتن مجموعه ای از عناصر + خود متد toString داریم که این را برمی گرداند) جالب تر است که متدهایی را که در جاوا 8 به دست ما آمده است درک کنید، و این : chars()و codePoints() از آموزش Oracle " Primitive Data" Types " را به خاطر بیاورید که char است single 16-bit Unicode character. یعنی در اصل char فقط یک نوع است به اندازه نصف یک int (32 بیت) که اعداد 0 تا 65535 را نشان می دهد (مقادیر اعشاری را ببینید در جدول ASCII ). یعنی در صورت تمایل می توانیم char را به صورت int نمایش دهیم. و جاوا 8 از این مزیت استفاده کرد. با شروع نسخه 8 جاوا، ما IntStream را داریم - یک جریان برای کار با int های اولیه. بنابراین، در charSequence می توان یک IntStream به دست آورد که نشان دهنده نویسه ها یا CodePoints باشد. قبل از اینکه به سراغ آنها برویم، مثالی برای نشان دادن راحتی این رویکرد خواهیم دید. بیایید از کامپایلر جاوا آنلاین Tutorialspoint استفاده کنیم و کد را اجرا کنیم:
public static void main(String []args){
        String line = "aaabccdddc";
        System.out.println( line.chars().distinct().count() );
}
اکنون می توانید تعدادی نماد منحصر به فرد را به این روش ساده دریافت کنید.

CodePoints

بنابراین، ما در مورد کاراکترها دیدیم. اکنون مشخص نیست که این کدها چه نوع نقاطی هستند. مفهوم codePoint به این دلیل ظاهر شد که وقتی جاوا ظاهر شد، 16 بیت (نیم int) برای رمزگذاری یک کاراکتر کافی بود. بنابراین، char در جاوا با فرمت UTF-16 نمایش داده می شود (مشخصات "یونیکد 88"). بعداً Unicode 2.0 ظاهر شد که مفهوم آن نشان دادن یک شخصیت به عنوان یک جفت جانشین (2 کاراکتر) بود. این به ما امکان داد تا محدوده مقادیر ممکن را به مقدار int گسترش دهیم. برای جزئیات بیشتر، به stackoverflow مراجعه کنید: " مقایسه یک کاراکتر با یک نقطه کد؟ " UTF-16 همچنین در JavaDoc for Character ذکر شده است . در جاوا داک آمده است که: 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 از چنین کاراکترهایی در متن پشتیبانی نمی کند. بنابراین، در مثال زیر باید مقداری را در String وارد کنید. بنابراین، اکنون یک تست ساده را درک خواهیم کرد:
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
}
همانطور که می بینید، در این مورد 1 کد پوینت برای 2 کاراکتر می رود. این جادو است.

شخصیت

همانطور که در بالا دیدیم، رشته ها در جاوا از 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()(به عنوان مثال، براکت. '(' یک تصویر آینه ای دارد ')').

استخر رشته

رشته ها در جاوا تغییرناپذیر هستند، یعنی ثابت هستند. این در JavaDoc خود کلاس java.lang.String نیز نشان داده شده است . دوم، و همچنین بسیار مهم، رشته ها را می توان به صورت لفظی مشخص کرد:
String literalString = "Hello, World!";
String literalString = "Hello, World!";
یعنی هر رشته نقل قول شده، همانطور که در بالا گفته شد، در واقع یک شی است. و این سؤال را ایجاد می کند - اگر ما اغلب از رشته ها استفاده می کنیم و اغلب می توانند یکسان باشند (به عنوان مثال، متن "خطا" یا "با موفقیت")، آیا راهی وجود دارد که مطمئن شویم رشته ها هر بار ایجاد نمی شوند؟ به هر حال، ما هنوز Maps را داریم که کلید آن می تواند یک رشته باشد. پس قطعاً نمی‌توانیم رشته‌های یکسانی داشته باشیم که اشیاء متفاوت باشند، در غیر این صورت نمی‌توانیم شی را از Map دریافت کنیم. توسعه دهندگان جاوا فکر کردند، فکر کردند و String Pool را ارائه کردند. این مکانی است که رشته ها در آن ذخیره می شوند، می توانید آن را کش رشته نامید. تمام خطوط به خودی خود به آنجا ختم نمی شوند، بلکه فقط خطوطی که در کد با یک کلمه مشخص شده اند. می توانید خودتان یک خط به استخر اضافه کنید، اما بعداً در مورد آن بیشتر توضیح دهید. بنابراین، در حافظه ما این کش را در جایی داریم. یک سوال منصفانه: این استخر در کجا قرار دارد؟ پاسخ به این را می‌توان در stackoverflow پیدا کرد: « استخر ثابت رشته جاوا کجا زندگی می‌کند، پشته یا پشته؟ " در حافظه Heap، در یک منطقه حوضچه ثابت زمان اجرا ویژه قرار دارد. مخزن ثابت Runtime زمانی تخصیص داده می شود که یک کلاس یا رابط توسط ماشین مجازی از ناحیه متد ایجاد شود - یک ناحیه خاص در Heap که تمام رشته های داخل ماشین مجازی جاوا به آن دسترسی دارند. استخر رشته به ما چه می دهد؟ این چند مزیت دارد:
  • اشیاء از همان نوع ایجاد نمی شوند
  • مقایسه با مرجع سریعتر از مقایسه کاراکتر به کاراکتر از طریق برابر است
اما اگر بخواهیم شی ایجاد شده را در این کش قرار دهیم چه؟ سپس یک متد خاص داریم: String.intern این متد یک رشته به String Pool اضافه می کند. شایان ذکر است که این فقط نوعی کش به شکل آرایه نیست (همانطور که برای اعداد صحیح). روش کارآموزی به عنوان "بومی" مشخص شده است. این بدان معنی است که خود متد در زبان دیگری (بیشتر C++) پیاده سازی می شود. در مورد روش های پایه جاوا، بهینه سازی های مختلف دیگری را می توان در سطح 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);
}
فقط توجه داشته باشید که ما همچنان String جدید خواهیم ساخت. یعنی اینترن یک رشته را از حافظه پنهان به ما برمی گرداند، اما رشته اصلی که در حافظه پنهان جستجو کردیم برای تمیز کردن پرت می شود، زیرا هیچ کس دیگری در مورد او نمی داند این به وضوح یک مصرف غیر ضروری از منابع است =( بنابراین، شما همیشه باید رشته ها را با استفاده از مساوی مقایسه کنید تا تا حد امکان از تشخیص خطاهای ناگهانی و دشوار جلوگیری کنید.
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test.equals(test2));
}
Equals یک مقایسه رشته ای کاراکتر به نویسه را انجام می دهد.

الحاق

همانطور که به یاد داریم، خطوط را می توان اضافه کرد. و همانطور که به یاد داریم، رشته های ما تغییر ناپذیر هستند. پس چگونه کار می کند؟ درست است، یک خط جدید ایجاد می شود که شامل نمادهایی از اشیاء اضافه شده است. میلیون ها نسخه از نحوه کار الحاق plus وجود دارد. برخی از مردم فکر می کنند که هر بار یک شی جدید وجود خواهد داشت، برخی دیگر فکر می کنند که چیز دیگری وجود خواهد داشت. اما ممکن است فقط یک نفر حق داشته باشد. و آن شخص کامپایلر جاواک است. بیایید از سرویس کامپایلر آنلاین استفاده کنیم و اجرا کنیم:
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 قبل از چرخه ایجاد می شود و در خود چرخه فقط اضافه می شود. به هر حال، یک چیز جالب دیگر در اینجا وجود دارد. یک مقاله عالی وجود دارد: " پردازش رشته در جاوا. بخش اول: رشته، StringBuffer، StringBuilder ." بسیاری از اطلاعات مفید در نظرات. به عنوان مثال، مشخص شده است که هنگام الحاق یک نما، new StringBuilder().append()...toString()بهینه سازی ذاتی اعمال می شود که توسط گزینه -XX:+OptimizeStringConcat تنظیم می شود که به طور پیش فرض فعال است. ذاتی - به عنوان "داخلی" ترجمه شده است. JVM چنین مواردی را به روشی خاص مدیریت می کند و آنها را به عنوان Native پردازش می کند، تنها بدون هزینه های اضافی JNI. بیشتر بخوانید: " روش های درونی در HotSpot VM ".

StringBuilder و StringBuffer

همانطور که در بالا دیدیم، StringBuilder یک ابزار بسیار مفید است. رشته ها تغییر ناپذیر هستند، یعنی. تغییرناپذیر و من می خواهم آن را تا کنم. بنابراین، 2 کلاس برای کمک به ما داده می شود: StringBuilder و StringBuffer. تفاوت اصلی بین این دو این است که StringBuffer در JDK1.0 معرفی شد، در حالی که StringBuilder در جاوا 1.5 به عنوان یک نسخه غیر همگام از StringBuffer برای حذف افزایش هزینه های همگام سازی روش های غیرضروری عرضه شد. هر دوی این کلاس‌ها پیاده‌سازی‌های کلاس انتزاعی AbstractStringBuilder - دنباله‌ای قابل تغییر از کاراکترها هستند. آرایه ای از charms در داخل ذخیره می شود که طبق قانون بزرگ می شود: value.length * 2 + 2. به طور پیش فرض، اندازه (ظرفیت) StringBuilder 16 است.

قابل مقایسه

رشته ها قابل مقایسه هستند، یعنی. روش compareTo را پیاده سازی کنید. این کار با استفاده از مقایسه شخصیت به کاراکتر انجام می شود. جالب اینجاست که حداقل طول از دو رشته انتخاب شده و یک حلقه روی آن اجرا می شود. بنابراین، compareTo یا تفاوت بین مقادیر int اولین کاراکترهای بی‌همتا را تا کوچک‌ترین طول رشته برمی‌گرداند، یا اگر همه کاراکترها با حداقل طول رشته مطابقت داشته باشند، تفاوت بین طول‌های رشته را برمی‌گرداند. این مقایسه «لغت‌شناسی» نامیده می‌شود.

کار با رشته های جاوا

رشته روش های مفید زیادی دارد:
رشته ها در جاوا (کلاس java.lang.String) - 4
کارهای زیادی برای کار با رشته ها وجود دارد. به عنوان مثال، در Coding Bat . همچنین یک دوره آموزشی در مورد coursera وجود دارد: " الگوریتم های رشته ها ".

نتیجه

حتی یک نمای کلی از این کلاس فضای قابل توجهی را اشغال می کند. و این تمام نیست. من به شدت توصیه می کنم گزارش JPoint 2015 را تماشا کنید: Alexey Shipilev - Catechism java.lang.String
#ویاچسلاو
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION