На одном уровне навыки программирования не зависят от языка. Программирование - это в основном решение проблем. Вам необходимо знать, как определить проблему, проанализировать ее и разбить на ее основные компоненты и разработать решение (будь то с помощью алгоритмов, структур данных или, чаще всего, обоих). Каждый язык - это просто набор инструментов, который вы используете для построения решения. Некоторые из них, такие как Java и C #, проще в использовании, но имеют более простые инструменты, поэтому вы редко можете точно настроить решение так, как вам нужно. C - самый гибкий и мощный язык (вне ассемблера). Это дает вам возможность и свободу делать в точности то, что вы хотите сделать, включая грубое злоупотребление (конечно, преднамеренное) нарушением его (неисполненных) правил. Но его набор инструментов заполнен многими очень острыми инструментами, и для его безопасного использования требуется гораздо больше знаний в области программирования (что делает его таким хорошим мостом между, скажем, Java и машинным кодом, который в конечном итоге выполняется процессором (ами)). .
Есть одна вещь, которая беспокоит меня об этом разговоре, это огромное количество людей, которые программируют только на сильно абстрагированных языках, таких как Java, C # и любой из скриптовых веб-языков (Python сразу приходит на ум). Я программировал на двух хороших языках (если не больше) за 37 лет программирования.
Это правда, что каждый новый язык имеет свою кривую обучения, разбирающуюся по синтаксису, семантике, библиотекам и особенностям. Но есть еще одна проблема, которую я здесь не вижу.
Сегодня большинство программистов обучаются исключительно на высокоуровневых, сильно абстрагированных языках и не имеют ни малейшего представления о том, что их код на самом деле собирается делать под прикрытием. Вы общаетесь с совершенно чуждым «мозгом», который одновременно очень глуп, и, благодаря возможности собирать миллионы и даже миллиарды простых операций и запускать их с почти невообразимо высокой скоростью, может имитировать виртуальный блеск.
Программисты, с которыми я работаю, которые никогда не были основаны на языках ассемблера, C или даже C ++, но которые думают о программировании только на уровне Java, C # и т. Д., В конечном итоге не понимают, что такое красивый код. они пишут, что на самом деле собираются делать, и каковы преимущества и (часто DIRE) последствия их выбора в том, как они владеют языком.
Вам нужен некоторый опыт работы с языком, который может привести вас к низкому уровню - особенно с командной строкой DOS или Unix без слоев GUI - скажем, до того, как операционные системы в защищенном режиме изолировали программиста от кодирования до чистого металла и работы с ним. порты, адреса памяти, таблицы адресов прерываний, регистры, а также основные коды операций и операнды, которые фактически выполняет машина.
Я приведу вам пример того, о чем я говорю. Сколько Java-программистов когда-либо инициализировали StringBuilder или StringBuffer?
StringBuffer strBuf = new StringBuffer();
а затем приступил к выполнению дюжины .append () операций над ним? Особенно в цикле, который выполняется тысячи, даже миллионы раз. Обычный Java-программист не знает, что конструктор по умолчанию создает внутренний буфер в 16 байтов. Как каждый strBuf.append (str); увеличивает длину содержимого StringBuffer, он вынужден выделять новый, больший блок памяти, копировать существующее содержимое в память, добавлять новую строку и затем отмечать старый буфер для gc. Это приводит к огромному количеству ненужных выделений, копий и фрагментированной памяти вне области видимости, которую необходимо скопировать.
Если вы определяете StringBuffer непосредственно за пределами вашего цикла и используете конструктор, который дает ему начальный размер буфера, который более чем достаточно велик для обработки любой ожидаемой предельной длины, вы выделяете один блок памяти и никогда не должны выполнять все эти тысячи или миллионы ненужных перераспределений, копий и сборок.
StringBuffer fixedStrBuf = new StringBuffer(1024);
Затем каждый .append () в вашем цикле просто копирует новую строку в существующую в том же буфере. Когда вы закончите и получите содержимое с помощью fixedStrBuf.toString (), просто вызовите fixedStrBuf.setLength (0); который оставляет сам буфер нетронутым, но повторно инициализирует его для использования снова. Вы также можете выполнить вызов setLength (0) в верхней части каждого цикла. Просто убедитесь, что объект StringBuffer выйдет из области видимости, как только вы выйдете из цикла обработки, который непрерывно использует его, или, по крайней мере, присвойте ему значение NULL, когда вы закончите с ним, чтобы его можно было больше не нужно.
Кстати, StringBuilder работает быстрее, чем StringBuffer, и предпочтителен, если вам не нужно делать ваш код поточно-ориентированным. Кроме того, вы можете получить еще большую скорость и гибкость (и большую защиту от неожиданных нулей), используя метод Apache Commons:
org.apache.commons.lang.text.StrBuilder;
Как и StringBuilder, он не синхронизирован, поэтому используйте его осторожно, но у него есть как минимум дюжина других полезных методов, включая .clear (), который делает то же самое, что и .setLength (0), но создает код немного читабельнее.
Пару лет назад я был вызван крупной транспортной компанией после того, как их звездный программист на Java (лучшие 10% его класса в Penn, и прошедший тест Brainbench) написал программу ETL (datamining), которая должна была взять данные, поступившие в одночасье, выполнить некоторые важные преобразования в них и создать временный информационный магазин, из которого старшие инженеры могли бы вызывать графики, таблицы и отчеты, необходимые для их повседневной работы. К сожалению, его программа работала от 300 до 360 минут (от 5 до 6 часов), и инженеры отстали на полдня, потому что они не могли получить данные для поддержки принятия решений до обеда. Я потратил две недели на анализ и рефакторинг программы и без какого-либо серьезного переписывания сократил время выполнения до 15-20 минут. В этой программе, среди многих других неправильных употреблений языка (все правильно с точки зрения синтаксиса и семантики), программист фактически использовал вышеупомянутый StringBuffer () с примерно дюжиной операций добавления каждого прохода в цикле, который выполнялся более миллиона раз, создавая до пятнадцати миллионов ненужных выделений, копий и удалений. И это была всего одна строка кода. Я мог бы написать книгу о всех способах, которыми программист не знал о низкоуровневых последствиях своего красивого, но кошмарно неэффективного кода Java.
Итак, мой совет, вместо того, чтобы пытаться добавить Ruby или Python или какой-либо новый горячий абстрагированный язык в свой репертуар, уделите несколько месяцев своего свободного времени, изучая C (или C99 или даже старше - не беспокоиться о том, чтобы попытаться догнать новый стандарт C11, который почти никто еще не использует), и получить книгу под названием «Cracking the Coding Interview» от Gayle Laakmann, в которой, среди прочего, есть 150 небольших упражнений по кодированию типа Вас могут попросить написать код на доске во время технического интервью. Все они могут быть решены в C, C ++ и Java (он дает ответы в конце книги по Java, потому что это более широко понимается, даже несмотря на то, что C предоставил бы намного более простые и более элегантные решения). Проработайте их в C, пока вы учитесь, и вы узнаете огромное количество знаний о том, что такое хорошее Java-программирование и Bad Java-программирование. Постскриптум найдите пару хороших C-праймеров - пытаться выучить C из оригинального руководства K & R (Kernighan and Ritchie) - это все равно, что выдержать двухдневный корневой канал. Если вы читаете K & R, найдите хотя бы второе издание. H & S (Harbison & Steel) - более понятное учебное пособие.
Кстати, JVM, которые выполняют ваш p-код Java, написаны на C или C ++.