StringBuilder vs Конкатенация строк в toString () в Java

Учитывая приведенные ниже toString() 2 toString() , какая из них предпочтительна:

 public String toString(){ return "{a:"+ a + ", b:" + b + ", c: " + c +"}"; } 

или

 public String toString(){ StringBuilder sb = new StringBuilder(100); return sb.append("{a:").append(a) .append(", b:").append(b) .append(", c:").append(c) .append("}") .toString(); } 

?

Что еще более важно, учитывая, что у нас есть только 3 свойства, это может не повлиять, но в какой момент вы переключитесь с + concat на StringBuilder ?

Версия 1 предпочтительнее, потому что она короче, и компилятор фактически превратит ее в версию 2 – никаких различий в производительности.

Что еще более важно, у нас есть только 3 свойства, это может не иметь значения, но в какой момент вы переключаетесь с concat на builder?

В точке, где вы конкатенируете в цикле – это обычно, когда компилятор не может самостоятельно заменить StringBuilder .

Ключ заключается в том, пишете ли вы одну конкатенацию в одном месте или накапливаете ее с течением времени.

В примере, который вы указали, нет смысла явно использовать StringBuilder. (Посмотрите на скомпилированный код для вашего первого случая.)

Но если вы строите строку, например внутри цикла, используйте StringBuilder.

Чтобы уточнить, предполагая, что arrayArray содержит тысячи строк, код выглядит следующим образом:

 ... String result = ""; for (String s : hugeArray) { result = result + s; } 

очень расточительно по времени и памяти по сравнению с:

 ... StringBuilder sb = new StringBuilder(); for (String s : hugeArray) { sb.append(s); } String result = sb.toString(); 

Я предпочитаю:

 String.format( "{a: %s, b: %s, c: %s}", a, b, c ); 

… потому что он короткий и читаемый.

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

Я согласен, что если вам нужно вывести множество параметров, эта форма может запутаться (как говорится в одном из комментариев). В этом случае я бы переключился на более читаемую форму (возможно, используя ToStringBuilder из apache-commons – взятый из ответа матового b) и снова проигнорировал производительность.

В большинстве случаев вы не увидите фактической разницы между этими двумя подходами, но легко построить сценарий наихудшего случая, такой как этот:

 public class Main { public static void main(String[] args) { long now = System.currentTimeMillis(); slow(); System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms"); now = System.currentTimeMillis(); fast(); System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms"); } private static void fast() { StringBuilder s = new StringBuilder(); for(int i=0;i<100000;i++) s.append("*"); } private static void slow() { String s = ""; for(int i=0;i<100000;i++) s+="*"; } } 

Выход:

 slow elapsed 11741 ms fast elapsed 7 ms 

Проблема в том, что to + = append к строке восстанавливает новую строку, поэтому она стоит что-то линейное по длине ваших строк (сумма обоих).

Итак - на ваш вопрос:

Второй подход будет более быстрым, но он менее читабельным и сложнее поддерживать. Как я уже сказал, в вашем конкретном случае вы, вероятно, не увидите разницы.

Я также столкнулся с моим боссом о том, следует ли использовать append или +. Поскольку они используют Append (я все еще не могу понять, как они говорят каждый раз, когда создается новый объект). Поэтому я подумал сделать R & D. Хотя я люблю объяснение Майкла Боргвардта, но просто хотел показать объяснение, если кому-то действительно нужно знать в будущем.

 /** * * @author Perilbrain */ public class Appc { public Appc() { String x = "no name"; x += "I have Added a name" + "We May need few more names" + Appc.this; x.concat(x); // x+=x.toString(); --It creates new StringBuilder object before concatenation so avoid if possible //System.out.println(x); } public void Sb() { StringBuilder sbb = new StringBuilder("no name"); sbb.append("I have Added a name"); sbb.append("We May need few more names"); sbb.append(Appc.this); sbb.append(sbb.toString()); // System.out.println(sbb.toString()); } } 

и parsingка выше classа выдается как

  .method public ()V //public Appc() .limit stack 2 .limit locals 2 met001_begin: ; DATA XREF: met001_slot000i .line 12 aload_0 ; met001_slot000 invokespecial java/lang/Object.()V .line 13 ldc "no name" astore_1 ; met001_slot001 .line 14 met001_7: ; DATA XREF: met001_slot001i new java/lang/StringBuilder //1st object of SB dup invokespecial java/lang/StringBuilder.()V aload_1 ; met001_slot001 invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\ g/StringBuilder; ldc "I have Added a nameWe May need few more names" invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\ g/StringBuilder; aload_0 ; met001_slot000 invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\ g/StringBuilder; invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String; astore_1 ; met001_slot001 .line 15 aload_1 ; met001_slot001 aload_1 ; met001_slot001 invokevirtual java/lang/String.concat(Ljava/lang/String;)Ljava/lang/Strin\ g; pop .line 18 return //no more SB created met001_end: ; DATA XREF: met001_slot000i ... ; =========================================================================== ;met001_slot000 ; DATA XREF: r ... .var 0 is this LAppc; from met001_begin to met001_end ;met001_slot001 ; DATA XREF: +6w ... .var 1 is x Ljava/lang/String; from met001_7 to met001_end .end method ;44-1=44 ; --------------------------------------------------------------------------- ; Segment type: Pure code .method public Sb()V //public void Sb .limit stack 3 .limit locals 2 met002_begin: ; DATA XREF: met002_slot000i .line 21 new java/lang/StringBuilder dup ldc "no name" invokespecial java/lang/StringBuilder.(Ljava/lang/String;)V astore_1 ; met002_slot001 .line 22 met002_10: ; DATA XREF: met002_slot001i aload_1 ; met002_slot001 ldc "I have Added a name" invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\ g/StringBuilder; pop .line 23 aload_1 ; met002_slot001 ldc "We May need few more names" invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\ g/StringBuilder; pop .line 24 aload_1 ; met002_slot001 aload_0 ; met002_slot000 invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\ g/StringBuilder; pop .line 25 aload_1 ; met002_slot001 aload_1 ; met002_slot001 invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String; invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\ g/StringBuilder; pop .line 28 return met002_end: ; DATA XREF: met002_slot000i ... ;met002_slot000 ; DATA XREF: Sb+25r .var 0 is this LAppc; from met002_begin to met002_end ;met002_slot001 ; DATA XREF: Sb+9w ... .var 1 is sbb Ljava/lang/StringBuilder; from met002_10 to met002_end .end method ;96-49=48 ; --------------------------------------------------------------------------- 

Из приведенных выше двух кодов вы можете видеть, что Майкл прав. В каждом случае создается только один объект SB.

Так как Java 1.5, простая конкатенация одной строки с «+» и StringBuilder.append () генерирует точно такой же байт-код.

Поэтому для удобства чтения кода используйте «+».

2 исключения:

  • многопоточная среда: StringBuffer
  • конкатенация в циклах: StringBuilder / StringBuffer

Используя последнюю версию Java (1.8), дизассемблер ( javap -c ) показывает оптимизацию, представленную компилятором. + а sb.append() будет генерировать очень похожий код. Тем не менее, будет полезно проверить поведение, если мы используем + в цикле for.

Добавление строк с использованием + в цикле for

Ява:

 public String myCatPlus(String[] vals) { String result = ""; for (String val : vals) { result = result + val; } return result; } 

ByteCode 🙁 for fragmentа цикла)

 12: iload 5 14: iload 4 16: if_icmpge 51 19: aload_3 20: iload 5 22: aaload 23: astore 6 25: new #3 // class java/lang/StringBuilder 28: dup 29: invokespecial #4 // Method java/lang/StringBuilder."":()V 32: aload_2 33: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 36: aload 6 38: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 41: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 44: astore_2 45: iinc 5, 1 48: goto 12 

Добавление строк с помощью stringbuilder.append

Ява:

 public String myCatSb(String[] vals) { StringBuilder sb = new StringBuilder(); for(String val : vals) { sb.append(val); } return sb.toString(); } 

ByteCdoe: ( for fragmentа цикла)

 17: iload 5 19: iload 4 21: if_icmpge 43 24: aload_3 25: iload 5 27: aaload 28: astore 6 30: aload_2 31: aload 6 33: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 36: pop 37: iinc 5, 1 40: goto 17 43: aload_2 

Однако есть немного ослепительная разница . В первом случае, когда был использован + , для каждого цикла цикла создается новый StringBuilder и сгенерированный результат сохраняется, выполняя вызов toString() (с 29 по 41). Таким образом, вы генерируете промежуточные строки, которые вам действительно не нужны при использовании цикла for + for .

Apache Commons-Lang имеет class ToStringBuilder, который очень прост в использовании. Он отлично справляется с обработкой append-logic, а также с форматированием того, как вы хотите, чтобы ваша toString выглядела.

 public void toString() { ToStringBuilder tsb = new ToStringBuilder(this); tsb.append("a", a); tsb.append("b", b) return tsb.toString(); } 

Будет возвращать вывод, который выглядит как [email protected][a=whatever, b=foo] .

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

 public void toString() { return new ToStringBuilder(this).append("a", a).append("b", b").toString(); } 

Или если вы хотите использовать reflection для включения каждого поля classа:

 public String toString() { return ToStringBuilder.reflectionToString(this); } 

Вы также можете настроить стиль ToString, если хотите.

По соображениям производительности использование += ( String concatenation) не рекомендуется. Причина в том, что: Java String неизменна, каждый раз, когда выполняется новая конкатенация, создается новая String (у новой есть отпечаток пальца от более старого, уже существующего в пуле String ). Создание новых строк оказывает давление на GC и замедляет работу программы: создание объекта дорого.

Ниже код должен сделать его более практичным и понятным одновременно.

 public static void main(String[] args) { // warming up for(int i = 0; i < 100; i++) RandomStringUtils.randomAlphanumeric(1024); final StringBuilder appender = new StringBuilder(); for(int i = 0; i < 100; i++) appender.append(RandomStringUtils.randomAlphanumeric(i)); // testing for(int i = 1; i <= 10000; i*=10) test(i); } public static void test(final int howMany) { List samples = new ArrayList<>(howMany); for(int i = 0; i < howMany; i++) samples.add(RandomStringUtils.randomAlphabetic(128)); final StringBuilder builder = new StringBuilder(); long start = System.nanoTime(); for(String sample: samples) builder.append(sample); builder.toString(); long elapsed = System.nanoTime() - start; System.out.printf("builder - %d - elapsed: %dus\n", howMany, elapsed / 1000); String accumulator = ""; start = System.nanoTime(); for(String sample: samples) accumulator += sample; elapsed = System.nanoTime() - start; System.out.printf("concatenation - %d - elapsed: %dus\n", howMany, elapsed / (int) 1e3); start = System.nanoTime(); String newOne = null; for(String sample: samples) newOne = new String(sample); elapsed = System.nanoTime() - start; System.out.printf("creation - %d - elapsed: %dus\n\n", howMany, elapsed / 1000); } 

Результаты для запуска приведены ниже.

 builder - 1 - elapsed: 132us concatenation - 1 - elapsed: 4us creation - 1 - elapsed: 5us builder - 10 - elapsed: 9us concatenation - 10 - elapsed: 26us creation - 10 - elapsed: 5us builder - 100 - elapsed: 77us concatenation - 100 - elapsed: 1669us creation - 100 - elapsed: 43us builder - 1000 - elapsed: 511us concatenation - 1000 - elapsed: 111504us creation - 1000 - elapsed: 282us builder - 10000 - elapsed: 3364us concatenation - 10000 - elapsed: 5709793us creation - 10000 - elapsed: 972us 

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

Уроки, извлеченные из этого очень быстрого эксперимента (легко воспроизводимого с помощью вышеприведенного кода): никогда не используйте += для объединения строк вместе, даже в очень простых случаях, когда требуется несколько конкатенаций (как сказано, создание новых строк в любом случае является дорогостоящим и оказывает давление на GC).

В Java 9 версия 1 должна быть быстрее, потому что она преобразуется в invokedynamic call. Более подробную информацию можно найти в JEP-280 :

Идея состоит в том, чтобы заменить весь танк добавления StringBuilder простым вызовом invokedynamic на java.lang.invoke.StringConcatFactory, который будет принимать значения в необходимости конкатенации.

Сделайте метод toString доступным для чтения, насколько возможно.

Единственным исключением из этого в моей книге является то, что вы можете доказать мне, что он потребляет значительные ресурсы 🙂 (Да, это означает профилирование)

Также обратите внимание, что компилятор Java 5 генерирует более быстрый код, чем рукописный подход «StringBuffer», используемый в более ранних версиях Java. Если вы используете «+», это и будущие улучшения предоставляются бесплатно.

Похоже, некоторые споры о том, что использование StringBuilder по-прежнему необходимо для текущих компиляторов. Поэтому я подумал, что дам 2 цента опыта.

У меня есть результирующий набор JDBC из 10 тыс. Записей (да, мне все они нужны в одной партии). Использование оператора + занимает около 5 минут на моей машине с Java 1.8 . Использование stringBuilder.append("") занимает менее секунды для одного и того же запроса.

Так что разница огромная. Внутри цикла StringBuilder выполняется намного быстрее.

См. Пример ниже:

 //java8 static void main(String[] args) { case1();//str.concat case2();//+= case3();//StringBuilder } static void case1() { List savedTimes = new ArrayList(); long startTimeAll = System.currentTimeMillis(); String str = ""; for (int i = 0; i < MAX_ITERATIONS; i++) { long startTime = System.currentTimeMillis(); str = str.concat(UUID.randomUUID()+"---"); saveTime(savedTimes, startTime); } System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms"); } static void case2() { List savedTimes = new ArrayList(); long startTimeAll = System.currentTimeMillis(); String str = ""; for (int i = 0; i < MAX_ITERATIONS; i++) { long startTime = System.currentTimeMillis(); str+=UUID.randomUUID()+"---"; saveTime(savedTimes, startTime); } System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms"); } static void case3() { List savedTimes = new ArrayList(); long startTimeAll = System.currentTimeMillis(); StringBuilder str = new StringBuilder(""); for (int i = 0; i < MAX_ITERATIONS; i++) { long startTime = System.currentTimeMillis(); str.append(UUID.randomUUID()+"---"); saveTime(savedTimes, startTime); } System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms"); } static void saveTime(List executionTimes, long startTime) { executionTimes.add(System.currentTimeMillis()-startTime); if(executionTimes.size()%CALC_AVG_EVERY == 0) { out.println("average time for "+executionTimes.size()+" concatenations: "+ NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(()->0))+ " ms avg"); executionTimes.clear(); } } время //java8 static void main(String[] args) { case1();//str.concat case2();//+= case3();//StringBuilder } static void case1() { List savedTimes = new ArrayList(); long startTimeAll = System.currentTimeMillis(); String str = ""; for (int i = 0; i < MAX_ITERATIONS; i++) { long startTime = System.currentTimeMillis(); str = str.concat(UUID.randomUUID()+"---"); saveTime(savedTimes, startTime); } System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms"); } static void case2() { List savedTimes = new ArrayList(); long startTimeAll = System.currentTimeMillis(); String str = ""; for (int i = 0; i < MAX_ITERATIONS; i++) { long startTime = System.currentTimeMillis(); str+=UUID.randomUUID()+"---"; saveTime(savedTimes, startTime); } System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms"); } static void case3() { List savedTimes = new ArrayList(); long startTimeAll = System.currentTimeMillis(); StringBuilder str = new StringBuilder(""); for (int i = 0; i < MAX_ITERATIONS; i++) { long startTime = System.currentTimeMillis(); str.append(UUID.randomUUID()+"---"); saveTime(savedTimes, startTime); } System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms"); } static void saveTime(List executionTimes, long startTime) { executionTimes.add(System.currentTimeMillis()-startTime); if(executionTimes.size()%CALC_AVG_EVERY == 0) { out.println("average time for "+executionTimes.size()+" concatenations: "+ NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(()->0))+ " ms avg"); executionTimes.clear(); } } 

Вывод:

среднее время для 10000 конкатенаций: 0,096 мс
среднее время для 10000 конкатенаций: 0,185 мс
среднее время для 10000 конкатенаций: 0,327 мс
среднее время для 10000 конкатенаций: 0,501 мс
среднее время для 10000 конкатенаций: 0,666 мс
Созданная строка длины: 1950000 в 17745 мс
среднее время для 10000 конкатенаций: 0,21 мс
среднее время для 10000 конкатенаций: 0,652 мс
среднее время для 10000 конкатенаций: 1.129 мс
среднее время для 10000 конкатенаций: 1,727 мс
среднее время для 10000 конкатенаций: 2.302 мс
Созданная строка длины: 1950000 в 60279 мс
среднее время для 10000 конкатенаций: 0,002 мс
среднее время для 10000 конкатенаций: 0,002 мс
среднее время для 10000 конкатенаций: 0,002 мс
среднее время для 10000 конкатенаций: 0,002 мс
среднее время для 10000 конкатенаций: 0,002 мс
Созданная строка длины: 1950000 в 100 мс

По мере увеличения длины строки также увеличивается время конкатенации.
Именно здесь определенная необходимость в StringBuilder .
Как вы видите, конкатенация: UUID.randomUUID()+"---" , на самом деле не влияет на время.

PS: Я не думаю, что когда использовать StringBuilder в Java, это действительно дубликат.
Этот вопрос говорит о toString() который в большинстве случаев не выполняет конкатенации огромных строк.

Могу ли я указать, что если вы собираетесь перебирать коллекцию и использовать StringBuilder, вы можете проверить Apache Commons Lang и StringUtils.join () (в разных вариантах)?

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

Я сравнил четыре разных подхода, чтобы сравнить производительность. Я точно не знаю, что происходит с gc, но для меня важно время. Компилятор – важный фактор здесь. Я использовал jdk1.8.0_45 под платформой window8.1.

 concatWithPlusOperator = 8 concatWithBuilder = 130 concatWithConcat = 127 concatStringFormat = 3737 concatWithBuilder2 = 46 

 public class StringConcatenationBenchmark { private static final int MAX_LOOP_COUNT = 1000000; public static void main(String[] args) { int loopCount = 0; long t1 = System.currentTimeMillis(); while (loopCount < MAX_LOOP_COUNT) { concatWithPlusOperator(); loopCount++; } long t2 = System.currentTimeMillis(); System.out.println("concatWithPlusOperator = " + (t2 - t1)); long t3 = System.currentTimeMillis(); loopCount = 0; while (loopCount < MAX_LOOP_COUNT) { concatWithBuilder(); loopCount++; } long t4 = System.currentTimeMillis(); System.out.println("concatWithBuilder = " + (t4 - t3)); long t5 = System.currentTimeMillis(); loopCount = 0; while (loopCount < MAX_LOOP_COUNT) { concatWithConcat(); loopCount++; } long t6 = System.currentTimeMillis(); System.out.println("concatWithConcat = " + (t6 - t5)); long t7 = System.currentTimeMillis(); loopCount = 0; while (loopCount < MAX_LOOP_COUNT) { concatStringFormat(); loopCount++; } long t8 = System.currentTimeMillis(); System.out.println("concatStringFormat = " + (t8 - t7)); long t9 = System.currentTimeMillis(); loopCount = 0; while (loopCount < MAX_LOOP_COUNT) { concatWithBuilder2(); loopCount++; } long t10 = System.currentTimeMillis(); System.out.println("concatWithBuilder2 = " + (t10 - t9)); } private static void concatStringFormat() { String s = String.format("%s %s %s %s ", "String", "String", "String", "String"); } private static void concatWithConcat() { String s = "String".concat("String").concat("String").concat("String"); } private static void concatWithBuilder() { StringBuilder builder=new StringBuilder("String"); builder.append("String").append("String").append("String"); String s = builder.toString(); } private static void concatWithBuilder2() { String s = new StringBuilder("String").append("String").append("String").append("String").toString(); } private static void concatWithPlusOperator() { String s = "String" + "String" + "String" + "String"; } } 

Консолидация строк по умолчанию с использованием «+» является более дорогостоящей, поскольку она должна сделать совершенно новую копию String, поскольку строки не изменяются в java. Это играет особую роль, если конкатенация очень частая, например: внутри цикла. Вот что предлагает моя ИДЕЯ, когда я пытаюсь сделать такое:

введите описание изображения здесь

Основные правила:

  • В пределах одного присваивания строк использование конкатенации String прекрасное.
  • Если вы зацикливаете, чтобы создать большой блок символьных данных, перейдите к StringBuffer.
  • Использование + = на String всегда будет менее эффективным, чем использование StringBuffer, поэтому оно должно вызывать предупреждающие сигналы, но в некоторых случаях оптимизация будет незначительной по сравнению с проблемами читаемости, поэтому используйте здравый смысл.

Вот хороший блог Джона Скита по этой теме.

Для простых строк, которые я предпочитаю использовать

 "string".concat("string").concat("string"); 

В порядке, я бы сказал, предпочтительным методом построения строки является использование StringBuilder, String # concat (), а затем перегруженный + оператор. StringBuilder – значительное увеличение производительности при работе с большими строками, так же как и с использованием оператора +, – это значительное снижение производительности (экспоненциально большое уменьшение по мере увеличения размера строки). Одна проблема с использованием .concat () заключается в том, что он может вызывать NullPointerExceptions.

Я думаю, мы должны пойти с подходом StringBuilder append. Причина в том, что

  1. Конкатенация String будет каждый раз создавать новый строковый объект (As String – неизменяемый объект), поэтому он создаст 3 объекта.

  2. При построении String будет создан только один объект [StringBuilder is muttable], и ​​к нему добавляется дополнительная строка.

Interesting Posts

Как использовать Dropbox с Truecrypt?

Внешний жесткий диск вызывает второй диск, который нераспределяется (исчезает)

Не удалось загрузить файл или сборку «Microsoft.CodeAnalysis, version = 1.3.1.0, Culture = neutral, PublicKeyToken = 31bf3856ad364e35» или один из его зависимостей

Получение FILE * из std :: fstream

Как обновить или перезагрузить Windows 8 без раздела System Reserved?

Как загрузить Xcode DMG или XIP-файл?

Ссылка на компьютеры по их имени между ОС X и операционными системами Windows

Как сбросить пароль защищенной функции на Brother MFC-9560-CDW

javax.net.ssl.SSLException: Ошибка чтения: ssl = 0x9524b800: ошибка ввода-вывода во время системного вызова, сброс соединения с помощью одноранговой сети

В чем разница между архитектурой Intel (CISC) и ARM (RISC)?

SSH-туннель для VNC не работает

Как получить внешний IP-адрес (через NAT) из командной строки Windows?

Объясните, почему инжектор конструктора лучше, чем другие параметры

Как я могу туннелировать весь свой сетевой трафик через SSH?

Как заполнить / создать экземпляр массива C # с одним значением?

Давайте будем гением компьютера.