sonyps4.ru

Вопрос: Максимальная длина строки в Java - метод length (). Последовательность выполнения операторов

Описание основных методов типа данных String.
Для работы со строками в Java используется тип данных String.
Инициализация
Инициализируется этот тип данных также как и остальные примитивные типы.
String a;
Создаст новый объект типа String со значением по умолчанию равным null.
Команда
a = "Строка";
Запишет в переменную a слово Строка.
String и char
Строку в Java можно рассматривать как некий массив символов типа char. Поэтому в Java есть простые способы получения каких-либо символов из строки. Пусть имеется переменная b типа char.
Команда
int I = a.indexOf(b);
Запишет в I номер первого вхождения символа b в встречаемой строке. Обратите внимание, что нумерация символов начинается не с 1 а с 0, поэтому если в приведенном выше примере символ b будет равен букве C, то в переменную I будет записано значение 0. В случае если искомый символ не встречается в строке, в переменную I будет записано значение -1.

Оператор lastIndexOf работает по тому же принципу, но возвращает номер последнего вхождения символа в строку.

Пусть имеется int i.
Команда
char c = a.charAt(i);
Запишет в переменную c символ стоящий на i-м месте в строке a. Здесь тоже не стоит забывать про особенности нумерации в Java. Если же длина строки меньше чем значение i, то программа рассмотрит такую попытку обращения как ошибку.
Получить длину строки(количество символов в ней) можно следующим образом:
Команда
Int I = a.length();
Запишет в переменную I длину строки a.

Также можно заменить все вхождения какого-либо символа в строку на другой символ. Пусть у нас есть строка a2, и нам нужно заменить все символы b1 на символ b2 и записать полученный результат в строку a1 . Это осуществляется с помощью следующей команды:

a1 = a2.replace(b1, b2);
Стоит заметить что если нужно заменить все элементы в исходной строке, без создания новой, то можно написать
a1 = a1.replace(b1, b2);
В Java также существует легкий способ переводить все символы в строке из нижнего регистра в верхний, и наоборот.
Команда
a = a.toLowerCase();
Заменит все символы верхнего регистра на символы нижнего регистра. Например, если мы применим этот оператор к строке из первого примера, то на выходе получим слово “строка”.

Оператор toUpperCase делает обратное, то есть в случае приведённого выше примера значение строки a станет “СТРОКА”.

Работа со строками
Также мы можем складывать строки друг с другом, сравнивать их, и получать подстроку данной строки.

Складывать строки можно также как и остальные примитивные типы данных. Пусть имеются строки а1 и а2, и их значения — “Стр” и “ока” соответственно.

Команда
a = a1 + a2;
Запишет в строку а1 слово “Строка”, являющееся “суммой” строк a1 и a2. Тот же самый результат будет при использовании команды
a = a1.concat(a2);
Команда
Int I = a1.compareTo(a2);
Запишет в переменную I значение меньше нуля в случае если лексикографически строка a1 меньше строки a2, и вернет значение большее нуля в обратном случае. Если же a1 лексикографически эквивалентна a2 то в I будет записано значение 0.
Пусть имеется строка a и число i.
Команда
String a1 = a.substring(i);
Запишет в строку a1 всю ту часть строки a которая начинается с символа с номером i. Также можно огранить подстроку и с другой стороны. Пусть у нас также имеется число j.
В таком случае команда
String a1 = a.substring(i, j);
Запишет в строку a1 часть строки a начинающуюся с символа с номером I и идущую до символа с номером j(не включая сам этот символ).

8 ответов

Позвольте мне вначале выделить три разных способа для аналогичной цели.

length - массивы (int , double , String) - знать длину массивов

length() - Связанный с строкой объект (String , StringBuilder и т.д.) - знать длину строки

size() - Объект коллекции (ArrayList , Set и т.д.) - знать размер коллекции

Теперь забудьте о length() рассмотреть только length и size() .

length не является методом, поэтому вполне понятно, что он не будет работать на объектах. Он работает только на массивах.
size() его имя описывает его лучше, и поскольку это метод, он будет использоваться в случае тех объектов, которые работают с коллекцией (фреймворками коллекции), как я уже сказал там.

Теперь переходите к length() :
Строка не является примитивным массивом (поэтому мы не можем использовать.length), а также не коллекцию (поэтому мы не можем использовать.size()), поэтому нам также нужен другой, который является length() (сохраняйте различия и подавайте цель).

Как ответ на Почему?
Я считаю, что это полезно, легко запомнить и использовать и дружелюбно.

Немного упрощенная, вы можете думать об этом как о массивах, которые являются особым случаем, а не обычными классами (немного похожими на примитивы, но нет). Строка и все коллекции являются классами, поэтому методы получения размера, длины или аналогичных вещей.

Я предполагаю, что причиной на момент разработки была производительность, если они создали ее сегодня, они, вероятно, придумали что-то вроде массивов, основанных на массивах.

Если кому-то интересно, вот небольшой фрагмент кода, иллюстрирующий разницу между двумя в сгенерированном коде, сначала источник:

Public class LengthTest { public static void main(String args) { int array = {12,1,4}; String string = "Hoo"; System.out.println(array.length); System.out.println(string.length()); } }

Вырезая способ не столь важной части байтового кода, запуск javap -c в классе приводит к следующим двум двум строкам:

20: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream; 23: aload_1 24: arraylength 25: invokevirtual #4; //Method java/io/PrintStream.println:(I)V 28: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream; 31: aload_2 32: invokevirtual #5; //Method java/lang/String.length:()I 35: invokevirtual #4; //Method java/io/PrintStream.println:(I)V

В первом случае (20-25) код просто запрашивает JVM для размера массива (в JNI это было бы вызовом GetArrayLength()), тогда как в случае String (28-35) ему нужно для вызова метода для получения длины.

В середине 90-х годов, без хороших JIT и т.д., он бы полностью уничтожил производительность, чтобы иметь только java.util.Vector(или что-то подобное), а не конструкцию языка, которая на самом деле не вела себя как класс, но была быстро. Разумеется, они могли маскировать свойство как вызов метода и обрабатывать его в компиляторе, но я думаю, что было бы еще более запутанным иметь метод для чего-то, что не является реальным классом.

Int myArray = new int; String myString = "hello world!"; List myList = new ArrayList(); myArray.length //gives the length of the array myString.length() //gives the length of the string myList.size() //gives the length of the list

Скорее всего, строки и массивы были спроектированы в разное время и, следовательно, были использованы в разных соглашениях. Одно из обоснований заключается в том, что, поскольку в Strings используются внутренние массивы, метод length() использовался во избежание дублирования одной и той же информации. Другим является то, что использование метода length() помогает подчеркнуть неизменность строк, даже если размер массива также неизменен.

В конечном счете это просто непоследовательно, что эволюционировало, что определенно было бы исправлено, если бы язык был перепроектирован с нуля. Насколько я знаю, никакие другие языки (С#, python, scala и т.д.) Не делают то же самое, так что это скорее всего небольшой недостаток, который оказался частью языка.

Вы получите сообщение об ошибке, если в любом случае вы используете неверный.: P: D

В Java массив хранит свою длину отдельно от структуры, которая фактически хранит данные. Когда вы создаете массив, вы указываете его длину и становится определяющим атрибутом массива. Независимо от того, что вы делаете с массивом длины N (значения изменения, нулевые значения и т.д.), Он всегда будет массивом длины N.

Длина строки является случайной; это не атрибут String, а побочный продукт. Хотя Java-строки на самом деле неизменяемы, если бы можно было изменить их содержимое, вы могли бы изменить их длину. Сбрасывание последнего символа (если это было возможно) уменьшило бы длину.

Я понимаю, что это прекрасное различие, и я могу проголосовать за него, но это правда. Если я делаю массив длиной 4, эта длина 4 является определяющей характеристикой массива и истинна независимо от того, что удерживается внутри. Если я создаю строку, содержащую "собак", эта строка имеет длину 4, потому что она содержит четыре символа.

Я рассматриваю это как оправдание для того, чтобы сделать одно с атрибутом, а другое - с помощью метода. По правде говоря, это может быть непреднамеренная несогласованность, но это всегда имело смысл для меня, и это всегда так, как я думал об этом.

Мне научили, что для массивов длина не извлекается методом из-за следующего страха: программисты просто назначили длину локальной переменной перед входом в цикл (подумайте о цикле for, где условное использует длину массива.) Программист предположительно сделает это, чтобы обрезать вызовы функций (и тем самым повысить производительность). Проблема в том, что длина может измениться во время цикла, а переменная не будет.

Всякий раз, когда создается массив, его размер задается. Таким образом, длину можно рассматривать как атрибут конструкции. Для String это по существу массив char. Длина является свойством массива char. Нет необходимости ставить длину как поле, потому что не все нуждается в этом поле. http://www.programcreek.com/2013/11/start-from-length-length-in-java/

Я согласен с Fredrik, что лучший выбор для оптимизатора компилятора. Это также решит проблему: даже если вы используете свойство для массивов, вы не решили проблему для строк и других (неизменяемых) типов коллекций, потому что, например, string основывается на массиве char , поскольку вы можно увидеть в определении класса string:

Public final class String implements java.io.Serializable, Comparable, CharSequence { private final char value; // ...

И я не согласен с тем, что это будет еще более запутанным, потому что массив наследует все методы из java.lang.Object .

Как инженер, мне действительно не нравится ответ "Потому что это всегда было так". и пожелал бы лучшего ответа. Но в этом случае это похоже.

По-моему, это дизайнерский недостаток Java и не должен реализовываться таким образом.

В этой главе обсуждаются средства языка Java для работы со строками. В язы­ках С и C++ отсутствует встроенная поддержка такого объекта, как строка. В них при необхо­димости передается адрес последовательности байтов, содержимое которых трактуется как символы до тех пор, пока не будет встречен нулевой байт, отмечающий конец строки. В пакет java.lang встроен класс, инкапсулирующий структуру данных, соответ­ствующую строке. Этот класс, называемый String , не что иное, как объ­ектное представление неизменяемого символьного массива.В этом классе есть методы, которые позволяют сравнивать строки, осуществлять в них поиск и извлекать определенные символы и подстроки . Кл асс StringBuffer исп ользуе тся тогда, когда строку после создания требу­ется изменять.

ВНИМАНИЕ

И String, и StringBuffer объявлены final, что означает, что ни от одного из этих классов нельзя производить подклассы. Это было сделано для того, чтобы можно было применить некоторые виды оптимизации по­зволяющие увеличить производительность при выполнении операций обработки строк.

Конструкторы

Как и в случае любого другого класса, вы можете создавать объекты типа String с помощью оператора new. Для создания пустой строки ис­пользуется конструктор без параметров:

String s = new String():

П риведенный ниже фрагмент кода создает объект s типа String иници­ализируя его строкой из трех символов, переданных конструктору в ка­честве параметра в символьном массиве.

char chars = { "а", "b", "с" }:

String s = new String(chars);

System.out.println(s):

Этот фрагмент кода выводит строку «abc». Итак, у этого конструктора - 3 параметра:

String(char chars, int начальныйИндекс, int числоСимволов);

Используем такой способ инициализации в нашем очередном примере:

char chars = { "a", "b", "с", "d", "e", "f" }:

String s = new String(chars,2,3);

System.out.println(s);

Этот фрагмент выведет «cde».

Специальный синтаксис для работы со строками

В Java включено несколько приятных синтаксических дополнений, цель которых - помочь программистам в выполнении операций со строками. В числе таких операций создание объектов типа String слияние нескольких строк и преобразование других типов данных в символьное представление.

Создание строк

Java включает в себя стандартное сокращение для этой опера­ции - запись в виде литерала, в которой содержимое строки заключа­ется в пару двойных кавычек. Приводимый ниже фрагмент кода экви­валентен одному из предыдущих, в котором строка инициализировалась массивом типа char.

String s = "abc";

System.out.println(s);

Один из общих методов, используемых с объектами String - метод length, возвращающий число символов в строке. Очередной фрагмент вы­водит число 3, поскольку в используемой в нем строке - 3 символа.

String s = "abc";

System.out.println(s.length);

В Java интересно то, что для каждой строки-литерала создается свой представитель класса String, так что вы можете вызывать методы этого класса непосредственно со строками-литералами, а не только со ссылоч­ными переменными. Очередной пример также выводит число 3.

System.out.println("abc".Length () );

Слияние строк

С троку

String s = «Не is » + age + " years old.";

в которой с помощью оператора + три строки объединяются в одну, про­честь и понять безусловно легче, чем ее эквивалент, записанный с яв­ными вызовами тех самых методов, которые неявно были использованы в первом примере:

String s = new StringBuffer("He is ").append(age);

s.append(" years old.").toString();

По определению каждый объект класса String не может изменять­ся. Нельзя ни вставить новые символы в уже существующую строку, ни поменять в ней одни символы на другие. И добавить одну строку в конец другой тоже нельзя. Поэтому транслятор Java преобразует опера­ции, выглядящие, как модификация объектов String, в операции с род­ственным классом StringBuffer.

ЗАМЕЧАНИЕ

Все это может показаться вам необоснованно сложным. А почему нельзя обойтись одним классом String, позволив ему вести себя при­мерно так же, как StringBuffer? Все дело в производительности. Тот факт, что объекты типа String в Java неизменны, позволяет транслято­ру применять к операциям с ними различные способы оптимизации.

Последовательность выполнения операторов

Давайте еще раз обратимся к нашему последнему примеру:

String s = "Не is " + age + " years old.";

В том случае, когда age - не String, а переменная, скажем, типа int, в этой строке кода заключено еще больше магии транслятора. Целое значение переменной int передается совмещенному методу append класса StringBuffer, который преобразует его в текстовый вид и добавляет в конец содержащейся в объекте строки. Вам нужно быть вниматель­ным при совместном использовании целых выражений и слияния строк, в противном случае результат может получиться совсем не тот, который вы ждали. Взгляните на следующую строку:

String s = "four: " + 2 + 2;

Быть может, вы надеетесь, что в s будет записана строка «four: 4»? Не угадали - с вами сыграла злую шутку последовательность выпол­нения операторов. Так что в результате получа­ется "four: 22".

Для того, чтобы первым выполнилось сложение целых чисел, нужно использовать скобки:

String s = "four: " + (2 + 2);

Преобразование строк

В каждом классе String есть метод toString - либо своя собственная реализация, либо вариант по умолчанию, наследуемый от класса Object. Класс в нашем очередном примере замещает наследуемый метод toStrring своим собственным, что позволяет ему выводить значения переменных объекта.

class Point {

int х, у;

Point(int x, int у) {

this.x = х;

this.у = у;

}

public String toString() {

return "Point[" + x + ", " + у + "]";

} }

class toStringDemo {

Point p = new Point(10, 20);

System.out.println("p = " + p);

} }

Ниже приведен результат, полученный при запуске этого примера.

С:\> Java toStringDemo
p = Point

Извлечение символов

Для того, чтобы извлечь одиночный символ из строки, вы можете со­слаться непосредственно на индекс символа в строке с помощью метода charAt. Если вы хотите в один прием извлечь несколько символов, можете воспользоваться методом getChars. В приведенном ниже фрагменте показано, как следует извлекать массив символов из объекта типа String.

class getCharsDemo {

public static void main(String args) {

String s = "This is a demo of the getChars method.";

int start = 10;

int end = 14;

char buf = new char;

s.getChars(start, end, buf, 0);

System.out.println(buf);

} }

Обратите внимание - метод getChars не включает в выходной буфер символ с индексом end. Это хорошо видно из вывода нашего примера - выводимая строка состоит из 4 символов.

С:\> java getCharsDemo

demo

Для удобства работы в String есть еще одна функция - toCharArray, которая возвращает в выходном массиве типа char всю строку.Альтернативная форма того же самого механизма позволяет записать содержимое строки в массив типа byte, при этом значения старших бай­тов в 16-битных символах отбрасываются. Соответствующий метод на­зывается getBytes, и его параметры имеют тот же смысл, что и пара­метры getChars, но с единственной разницей - в качестве третьего параметра надо использовать массив типа byte.

Сравнение

Если вы хотите узнать, одинаковы ли две строки, вам следует воспользоваться методом equals класса String. Альтернативная форма этого метода называется equalsIgnoreCase, при ее использовании различие ре­гистров букв в сравнении не учитывается. Ниже приведен пример, иллюстрирующий использование обоих методов:

class equalDemo {

public static void main(String args) {

String s1 = "Hello";

String s2 = "Hello";

String s3 = "Good-bye";

String s4 = "HELLO";

System.out.println(s1 + " equals " + s2 + " -> " + s1.equals(s2));

System.out.println(s1 + " equals " + s3 + " -> " + s1.equals(s3));

System.out.println(s1 + " equals " + s4 + " -> " + s1.equals(s4));

System.out.println(s1 + " equalsIgnoreCase " + s4 + " -> " +

s1.equalsIgnoreCase(s4));

} }

Результат запуска этого примера:

С:\> java equalsDemo

Hello equals Hello -> true

Hello equals Good-bye -> false

Hello equals HELLO -> false

Hello equalsIgnoreCase HELLO -> true

В классе String реализована группа сервисных методов, являющихся специализированными версиями метода equals. М етод regionMatches используется для сравнения подстроки в исходной строке с подстрокой в строке-параметре. М етод startsWith проверяет, начинается ли данная подстрока фрагментом, переданным методу в качестве параметра. М етод endsWith проверяет совпадает ли с параметром конец строки.

Равенство

Метод equals и оператор == выполняют две совершенно различных проверки. Если метод equal сравнивает символы внутри строк, то опе­ратор == сравнивает две переменные-ссылки на объекты и проверяет, указывают ли они на разные объекты или на один и тот же. В очеред­ном нашем примере это хорошо видно - содержимое двух строк оди­наково, но, тем не менее, это - различные объекты, так что equals и == дают разные результаты.

class EqualsNotEqualTo {

public static void main(String args) {

String s1 = "Hello";

String s2 = new String(s1);

System.out.println(s1 + " equals " + s2 + " -> " + s1.equals(s2));

System.out.println(s1 + " == " + s2 + ", -> " + (s1 == s2));

} }

Вот результат запуска этого примера:

C:\> java EqualsNotEqualTo

Hello equals Hello -> true
Hello == Hello -> false

Упорядочение

Зачастую бывает недостаточно просто знать, являются ли две строки идентичными. Для приложений, в которых требуется сортировка, нужно знать, какая из двух строк меньше другой. Для ответа на этот вопрос нужно воспользоваться методом compareTo класса String. Если целое значение, возвращенное методом, отрицательно, то строка, с которой был вызван метод, меньше строки-параметра, если положительно - больше. Если же метод compareTo вернул значение 0, строки идентичны. Ниже приведена программа, в которой выполняется пузырьковая сорти­ровка массива строк, а для сравнения строк используется метод compareTo.Эта программа выдает отсортированный в алфавитном порядке список строк.

class SortString {

static String arr = {"Now", "is", "the", "time", "for", "all",

"good", "men", "to", "come", "to", "the",

"aid", "of", "their", "country" };

public static void main(String args) {

for (int j = 0; i < arr.length; j ++) {

for (int i = j + 1; i < arr.length; i++) {

if (arr[i].compareTo(arr[j]) < 0) {

String t = arr[j];

arr[j] = arr[i];

arr[i] = t;

}

}

System.out.println(arr[j]);

}

} }

indexOf и lastIndexOf

В класс String включена поддержка поиска определенного символа или подстроки, для этого в нем имеются два метода - indexOf и lastIndexOf. Каждый из этих методов возвращает индекс того символа, который вы хотели найти, либо индекс начала ис­комой подстроки. В любом случае, если поиск оказался неудачным ме­тоды возвращают значение -1. В очередном примере показано, как пользоватьсяразличнымивариантами этих методов поиска.

class indexOfDemo {

public static void main(String args) {

String s = "Now is the time for all good men " +

"to come to the aid of their country " +

"and pay their due taxes.";

System.out.println(s);

System.out.println("indexOf(t) = " + s.indexOf("f’));

System.out.println("lastlndexOf(t) = " + s.lastlndexOf("f’));

System.out.println("indexOf(the) = " + s.indexOf("the"));

System.out.println("lastlndexOf(the) = " + s.lastlndexOf("the"));

System.out.println("indexOf(t, 10) = " + s.indexOf("f’ , 10));

System.out.println("lastlndexOf(t, 50) = " + s.lastlndexOf("f’ , 50));

System.out.println("indexOf(the, 10) = " + s.indexOf("the", 10));

System.out.println("lastlndexOf(the, 50) = " + s.lastlndexOf("the", 50));

} }

Ниже приведен результат работы этой программы.Обратите внимание на то, что индексы в строках начинаются с нуля.

С:> java indexOfDemo

Now is the time for all good men to come to the aid of their country

and pay their due taxes.

indexOf(t) = 7

lastlndexOf(t) = 87

indexOf(the) = 7

lastlndexOf(the) = 77

index0f(t, 10) = 11

lastlndex0f(t, 50) = 44

index0f(the, 10) = 44

lastlndex0f(the, 50) = 44

Модификация строк при копировании

Поскольку объекты класса String нельзя изменять, всякий раз, когда вам захочется модифицировать строку, придется либо копировать ее в объект типа StringBuffer, либо использовать один из описываемых ниже методов класса String, которые создают новую копию строки, внося в нее ваши изменения.

substring

Вы можете извлечь подстроку из объекта String, используя метод sub­string . Этот метод создает новую копию символов из того диапазона ин­дексов оригинальной строки, который вы указали при вызове. Можно указать только индекс первого символа нужной подстроки - тогда будут скопированы все символы, начиная с указанного и до конца строки. Также можно указать и начальный, и конечный индексы - при этом в новую строку будут скопированы все символы, начиная с первого ука­занного, и до (но не включая его) символа, заданного конечным индек­сом.

"Hello World".substring(6) -> "World"

"Hello World".substring(3,8) -> "lo Wo"

Слияние, или конкатенация строк выполняется с помощью метода concat. Этот метод создает новый объект String, копируя в него содер­жимое исходной строки и добавляя в ее конец строку, указанную в параметре метода.

"Hello".concat(" World") -> "Hello World"

Методу replace в качестве параметров задаются два символа. Все сим­волы, совпадающие с первым, заменяются в новой копии строки на вто­рой символ.

"Hello".replace("l" , "w") -> "Hewwo"

toLowerCase и toUpperCase

Эта пара методов преобразует все символы исходной строки в нижний и верхний регистр, соответственно.

"Hello".toLowerCase() -> "hello"

"Hello".toUpperCase() -> "HELLO"

И, наконец, метод trim убирает из исходной строки все ведущие и замыкающие пробелы.

“Hello World“.trirn() -> "Hello World"

valueOf

Если вы имеете дело с каким-либо типом данных и хотите вывести значение этого типа в удобочитаемом виде, сначала придется преобразо­вать это значение в текстовую строку. Для этого существует метод val­ueOf. Такой статический метод определен для любого существующего в Java типа данных (все эти методы совмещены, то есть используют одно и то же имя). Благодаря этому не составляет труда преобразовать в стро­ку значение любого типа.

StringBuffer

StringBuffer - близнец класса String, предоставляющий многое из того, что обычно требуется при работе со строками. Объекты класса String представляют собой строки фиксированной длины, которые нельзя изме­нять. Объекты типа StringBuffer представляют собой последовательности символов, которые могут расширяться и модифицироваться. Java активно ис­пользует оба класса, но многие программисты предпочитают работать только с объектами типа String, используя оператор +. При этом Java вы­полняет всю необходимую работу со StringBuffer за сценой.

Конструкторы

Объект StringBuffer можно создать без параметров, при этом в нем будет зарезервировано место для размещения 16 символов без возмож­ности изменения длины строки. Вы также можете передать конструкто­ру целое число, для того чтобы явно задать требуемый размер буфера. И, наконец, вы можете передать конструктору строку, при этом она будет скопирована в объект и дополнительно к этому в нем будет заре­зервировано место еще для 16 символов. Текущую длину StringBuffer можно определить, вызвав метод length , а для определения всего места, зарезервированного под строку в объекте StringBuffer нужно воспользоваться методом capacity . Ниже приведен пример, поясняющий это:

class StringBufferDemo {

public static void main(String args) {

System.out.println("buffer = " + sb);

System.out.println("length = " + sb.length());

System.out. println("capacity = " + sb.capacity());

} }

Вот вывод этой программы, из которого видно, что в объекте String-Buffer для манипуляций со строкой зарезервировано дополнительное место.

С:\> java StringBufferDemo

buffer = Hello

length = 5

capacity = 21

ensureCapacity

Если вы после создания объекта StringBuffer захотите зарезервировать в нем место для определенного количества символов, вы можете для установки размера буфера воспользоваться методом ensureCapacity . Это бывает полезно, когда вы заранее знаете, что вам придется добавлять к буферу много небольших строк.

setLength

Если вам вдруг понадобится в явном виде установить длину строки в буфере, воспользуйтесь методом setLength. Если вы зададите значение, большее чем длина содержащейся в объекте строки, этот метод заполнит ко нец новой, расширенной строки символами с кодом нуль. В приводимой чуть дальше программе setCharDemo метод sstLength используется для укорачивания буфера.

charAt и setCharAt

Одиночный символ может быть извлечен из объекта StringBuffer с помощью метода charAt . Другой метод setCharAt позволяет записать в заданную позицию строки нужный символ. Использование обоих этих методов проиллюстрировано в примере:

class setCharAtDemo {

public static void main(String args) {

StringBuffer sb = new StringBuffer("Hello");

System.out.println("buffer before = " + sb);

System.out.println("charAt(1) before = " + sb.charAt(1));

sb.setCharAt(1, "i");

sb.setLength(2);

System.out.println("buffer after = " + sb);

System.out.println("charAt(1) after = " + sb.charAt(1));

} }

Вот вывод, полученный при запуске этой программы.

C:\> java setCharAtDemo

buffer before = Hello

charAt(1) before = e

buffer after = Hi

charAt(1) after = i

append

Метод append класса StringBuffer обычно вызывается неявно при ис­пользовании оператора + в выражениях со строками. Для каждого параметра вызывается метод String.valueOf и его результат до­бавляется к текущему объекту StringBuffer. К тому же при каждом вы­зове метод append возвращает ссылку на объект StringBuffer, с которым он был вызван. Это позволяет выстраивать в цепочку последовательные вызовы метода, как это показано в очередном примере.

class appendDemo {

public static void main(String args) {

String s;

int a = 42;

StringBuffer sb = new StringBuffer(40);

s = sb.append("a = ").append(a).append("!").toString();

System.out.println(s);

} }

Вот вывод этого примера:

С:\> Java appendDemo

а = 42!

insert

Метод insert идентичен методу append в том смысле, что для каждого возможного типа данных существует своя совмещенная версия этого ме­тода. Правда, в отличие от append, он не добавляет символы, возвра­щаемые методом String.valueOf, в конец объекта StringBuffer, а встав­ляет их в определенное место в буфере, задаваемое первым его параметром. В очередном нашем примере строка "there" вставляется между "hello" и "world!".

class insertDemo {

public static voidmain(String args) {

StringBuffer sb = new StringBuffer("hello world !");

sb.insert(6,"there ");

System.out.println(sb);

} }

При запуске эта программа выводит следующуюстроку:

С:\> java insertDemo

hello there world!

Без строк не обойдешься

Почти любой аспект программирования в Java на каком либо этапе подразумевает использование классов String и StringBuffer. Они понадо­бятся и при отладке, и при работе с текстом, и при указании имен фай­лов и адресов URL в качестве параметров методам. Каждый второй байт большинства строк в Java - нулевой (Unicode пока используется редко ). То, что строки в Java требуют вдвое больше памяти, чем обыч­ные ASCII, не очень пугает, пока вам для эффективной работы с текстом в редакторах и других подобных приложениях не придется напрямую работать с огромным массивом типа char.

Привычным способом Java сравнения строк является лексикографический способ, реализованный в методе compareTo() класса String . Но иногда нужно сравнить строки по их длине. По умолчанию нельзя использовать для этого метод compareTo() . Вместо этого нужно написать собственный метод, который вычисляет длину строк с помощью метода length() и сравнивает их. Такой метод должен возвращать положительное значение, если первая String имеет длину больше второй; отрицательное значение, если длина первой String меньше длины второй и ноль, если обе String имеют одинаковую длину.

Можно определить такой одноразовый метод, используя анонимный внутренний класс, и далее использовать его для сортировки списка строк по длине. Это метод хорошо работает в Java 6 и 7 , но в Java 8 он работает еще лучше из-за меньшего количества помех благодаря новой функции, называемой лямбда-выражения.

Вот как можно сделать это в более ранних версиях Java :

public int compare(String s1, String s2) { return s1.length() - s2.length(); }

Сравнение длины строк arraylist в Java 8 можно сделать еще проще, используя лямбда-выражение и новые методы по умолчанию, добавленные в класс java.util.Comparator :

Comparator strlenComp = (a, b) -> Integer.compare(a.length(), b.length());

a и b — это два объекта String. Integer.compare() — это новый метод, добавленный в Java 8 . Поскольку мы используем компаратор для строк, компилятор в состоянии выводить типы a и b , которые являются String .

Ниже приведен пример использования этого компаратора длины строк для сортировки списка строк по их длине. В этом примере мы продемонстрировали программы на JDK 6 и 7 с использованием анонимного класса, и новый способ с использованием лямбда-выражений, который доступен в Java 8 . Он занимает всего одну строку и его намного проще понять, если вы знакомы с синтаксисом лямбда-выражений.

Как отсортировать список строк по их длине в Java 7 и 8

Java 6, 7

Import java.util.ArrayList; Import java.util.Arrays; Import java.util.Collections; Import java.util.Comparator; Import java.util.List; / * * Программа Java для сортировки списка строк по их длине * / public class StringComparisonByLength{ Public static void main (String args) { List books = new ArrayList<>(Arrays.asList("Effective Java", "Algorithms", "Refactoring")); System.out.println("Sorting List of String by length in JDK 7 ======"); System.out.println("The original list without sorting"); System.out.println(books); Comparator byLength = new Comparator(){ @Override Public int compare (String s1, String s2) { Return s1.length () - s2.length (); } }; Collections.sort(books, byLength); System.out.println("The same list after sorting string by length"); System.out.println(books); } }

Результат

Sorting List of String by length in JDK 7 ====== The original list without sorting The same list after sorting string by length

Java 8

Import java.util.ArrayList; Import java.util.Arrays; Import java.util.Comparator; Import java.util.List; / * * Программа Java для сортировки списка строк по их длине в JDK 8 * / public class SortingListOfStringByLength{ public static void main(String args) { // В Java 8 System.out.println("Sorting List of String by length in Java 8 ======"); List cities = new ArrayList<>(Arrays.asList("London", "Tokyo", "NewYork")); System.out.println("The original list without sorting"); System.out.println(cities); cities.sort((first, second) -> Integer.compare(first.length(), second.length())); System.out.println("The same list after sorting string by length"); System.out.println(cities); } }

Результат

Sorting List of String by length in Java 8 ====== The original list without sorting The same list after sorting string by length

Это все, что касается сравнения длины строк Java и сортировки списка String по длине в Java 7 и Java 8 . В последней версии языка делать это намного удобнее, поскольку можно создать пользовательский компаратор только одной строкой, используя лямбда-выражение. API JDK 8 содержит множество подобных методов, которые облегчают еще более сложные сравнения. Например, используя метод thenComparing() , можно связать несколько компараторов.

Перевод статьи «How to compare String by their length in Java 7 and 8 » дружной командой проекта

Знаете сколько в памяти занимает строка? Каких только я не слышал ответов на этот вопрос, начиная от «не знаю» до «2 байта * количество символов в строке». А сколько тогда занимает пустая строка? А знаете сколько занимает объект класса Integer? А сколько будет занимать Ваш собственный объект класса с тремя Integer полями? Забавно, но ни один мой знакомый Java программист не смог ответить на эти вопросы… Да, большинству из нас это вообще не нужно и никто в реальных java проектах не будет об этом думать. Но это, ведь, как не знать объем двигателя машины на которой Вы ездите. Вы можете быть прекрасным водителем и даже не подозревать о том, что значат цифры 2.4 или 1.6 на вашей машине. Но я уверен, что найдется мало людей, которые не знакомы со значением этих цифр. Так почему же java программисты так мало знают об этой части своего инструмента?

Integer vs int
Все мы знаем, что в java - everything is an object. Кроме, пожалуй, примитивов и ссылок на сами объекты. Давайте рассмотрим две типичных ситуации:
//первый случай int a = 300; //второй случай Integer b = 301;
В этих простых строках разница просто огромна, как для JVM так и для ООП. В первом случае, все что у нас есть - это 4-х байтная переменная, которая содержит значение из стека. Во втором случае у нас есть ссылочная переменная и сам объект, на который эта переменная ссылается. Следовательно, если в первом случае мы определено знаем, что занимаемый размер равен:
sizeOf(int)
то во втором:
sizeOf(reference) + sizeOf(Integer)
Забегая вперед скажу - во втором случае количество потребляемой памяти приблизительно в 5 раз больше и зависит от JVM. А теперь давайте разберемся, почему разница настолько огромна.
Из чего же состоит объект?
Прежде чем определять объем потребляемой памяти, следует разобраться, что же JVM хранит для каждого объекта:
  • Заголовок объекта;
  • Память для примитивных типов;
  • Память для ссылочных типов;
  • Смещение/выравнивание - по сути, это несколько неиспользуемых байт, что размещаются после данных самого объекта. Это сделано для того, чтобы адрес в памяти всегда был кратным машинному слову, для ускорения чтения из памяти + уменьшения количества бит для указателя на объект + предположительно для уменьшения фрагментации памяти. Стоит также отметить, что в java размер любого объекта кратен 8 байтам!
Структура заголовка объекта
Каждый экземпляр класса содержит заголовок. Каждый заголовок для большинства JVM(Hotspot, openJVM) состоит из двух машинных слов. Если речь идет о 32-х разрядной системе, то размер заголовка - 8 байт, если речь о 64-х разрядной системе, то соответственно - 16 байт. Каждый заголовок может содержать следующую информацию:
  • Маркировочное слово (mark word) - к сожалению мне так и не удалось найти назначение этой информации, подозреваю что это просто зарезервированная на будущее часть заголовка.
  • Hash Code - каждый объект имеет хеш код. По умолчанию результат вызова метода Object.hashCode() вернет адрес объекта в памяти, тем не менее некоторые сборщики мусора могут перемещать объекты в памяти, но хеш код всегда остается одним и тем же, так как место в заголовке объекта как раз может быть использовано для хранения оригинального значения хеш кода.
  • Garbage Collection Information - каждый java объект содержит информацию нужную для системы управления памятью. Зачастую это один или два бита-флага, но также это может быть, например, некая комбинация битов для хранения количества ссылок на объект.
  • Type Information Block Pointer - содержит информацию о типе объекта. Этот блок включает информацию о таблице виртуальных методов, указатель на объект, который представляет тип и указатели на некоторые дополнительные структуры, для более эффективных вызовов интерфейсов и динамической проверки типов.
  • Lock - каждый объект содержит информацию о состоянии блокировки. Это может быть указатель на объект блокировки или прямое представление блокировки.
  • Array Length - если объект - массив, то заголовок расширяется 4 байтами для хранения длины массива.
Спецификация Java
Известно, что примитивные типы в Java имеют предопределенный размер, этого требует спецификация для переносимости кода. Поэтому не будем останавливаться на примитивах, так как все прекрасно описано по ссылке выше. А что же говорит спецификация для объектов? Ничего, кроме того, что у каждого объекта есть заголовок. Иными словами, размеры экземпляров Ваших классов могут отличатся от одной JVM к другой. Собственно, для простоты изложения я буду приводить примеры на 32-х разрядной Oracle HotSpot JVM. А теперь давайте разберем самые используемые классы Integer и String.
Integer и String
Итак, давайте попробуем подсчитать сколько же будет занимать объект класса Integer в нашей 32-х разрядной HotSpot JVM. Для этого нужно будет заглянуть в сам класс, нам интересны все поля, которые не объявлены как static. Из таких видим только одно - int value. Теперь исходя из информации выше получаем:
Заголовок: 8 байт Поле int: 4 байта Выравнивание для кратности 8: 4 байта Итого: 16 байт
Теперь заглянем в класс строки:
private final char value; private final int offset; private final int count; private int hash;
И подсчитаем размер:
Заголовок: 8 байт Поля int: 4 байта * 3 == 12 байт Ссылочная переменная на объект массива: 4 байта Итого: 24 байта
Ну и это еще не все… Так как строка содержит ссылку на массив символов, то, по сути, мы имеем дело с двумя разными объектами - объектом класса String и самим массивом, который хранит строку. Это, как бы, верно с точки зрения ООП, но если посмотреть на это со стороны памяти, то к полученному размеру нужно добавить и размер выделенного для символов массива. А это еще 12 байт на сам объект массива + 2 байта на каждый символ строки. Ну и, конечно же, не забываем добавлять выравнивание для кратности 8 байтам. Итого в конечном итоге простая, казалось бы, строка new String(«a») выливается в:
new String() Заголовок: 8 байт Поля int: 4 байта * 3 == 12 байт Ссылочная переменная на объект массива: 4 байта Итого: 24 байта new char Заголовок: 8 байт + 4 байта на длину массива == 12 байт Примитивы char: 2 байта * 1 == 2 байта Выравнивание для кратности 8: 2 байта Итого: 16 байта Итого, new String("a") == 40 байт
Важно отметить, что new String(«a») и new String(«aa») будут занимать одинаковое количество памяти. Это важно понимать. Типичный пример использования этого факта в свою пользу - поле hash в классе String. Если бы его не было, то объект строки так или иначе занимал бы 24 байта, за счет выравнивания. А так получается что для этих 4-х байтов нашлось очень достойное применение. Гениальное решение, не правда ли?
Размер ссылки
Немножко хотел бы оговорится о ссылочных переменных. В принципе, размер ссылки в JVM зависит от ее разрядности, подозреваю, что для оптимизации. Поэтому в 32-х разрядных JVM размер ссылки обычно 4 байта, а в 64-х разрядных - 8 байт. Хотя это условие и не обязательно.
Группировка полей
Следует также отметить, что JVM проводит предварительную группировку полей объекта. Это значит, что все поля класса размещаются в памяти в определенном порядке, а не так как объявлены. Порядок группировки выглядит так:
  • 1. 8-ми байтовые типы(double и long)
  • 2. 4-х байтовые типы(int и float)
  • 3. 2-х байтовые типы(short и char)
  • 4. Одно байтовые типы(boolean и byte)
  • 5. Ссылочные переменные
Зачем все это?
Иногда возникает ситуация в которой Вам необходимо прикинуть приблизительный объем памяти для хранения тех или иных объектов, например словаря, эта маленькая справка поможет быстро сориентироваться. Также, это потенциально возможный способ оптимизации, особенно в том окружении, где доступ к его настройкам не доступен.
Выводы
Тема памяти в java очень интересна и обширна, когда я начинал писать эту статью, то думал что уложусь в пару примеров с выводами. Но чем дальше и глубже копаешь, тем больше и интересней становится. Вообще, знать как выделяется память для объектов очень полезная вещь, так как поможет Вам сэкономить память, предотвратить

Загрузка...