前段时间,我在 GitChat 上写了一门《算法应该怎么“玩”?》的畅销课,这门课选了三十多个简单且实用的算法实例,基本覆盖了各种算法比赛中经常出现的题目以及生活中常见的一些有趣的算法实现,在介绍每个算法实现时其侧重点会放在各种算法的设计方法和思想上,让读者拥有将具体问题抽象为数据模型的能力。
课程上线后,收到了读者的不少好评,也收到了一些反馈:“算法为什么用 C++,而不用 Java 写呢?”
我打算写一篇文章来比较一下用 C++ 和 Java 在写算法时的差异,通过对比,Java 程序员能快速理解我用 C++ 实现算法的例子,C++ 程序员也能看懂简单的 Java 算法代码,两种语言的对比会放在一起展示。
此外,C++ 语法层面使用的版本是 C++ 11 和 C++ 11 之后的版本,Java 使用的标准是 Java 6 和 Java 6 以后的版本。
为什么会有这篇文章无论是七、八年前开始写的 “ 算法博客 ”,还是三年前出版的图书,亦或是在 GitChat 平台上发布的《算法应该怎么“ 玩 ”?》畅销课,我介绍算法用的例子都是用 C++ 编写的,在我看来,选择何种语言并不是困扰,用最顺手的就好了。除非你要体验一行代码实现快速排序这样的丝般顺滑感觉(比如用 Perl),否则的话,用 C++ 还是用 Java,对于实现一个小算法而言,没有太大的差异。尽管博客留言里不乏抱怨之声,但是在《算法的乐趣》出版的时候,介绍算法实现的部分我依然使用了 C++。
我邀请一些朋友 review 我随书发放的例子代码,不少朋友也建议我改用 Java 做例子实现,可惜我没有听从建议。我的 “ 一意孤行 ” 终于招致读者愤怒的差评:“ 好好的一本算法书,为什么要用 C++?” 尽管我在微博里也吐槽过这个事情,我的很多使用 Java 的朋友们也都为我 “ 打抱不平 ”,但是在事实面前,我不得不承认,这个拖了很久的需求必须要实现了。
尽管学习 Java 了很长时间,但是因为工作的需要,很少用 Java 做过大型的项目,所以在公开算法实现的时候,我本能地选择最擅长的 C++ 语言。在我看来,把大型项目的代码转换另一种编程语言实现是不现实的,也是没有必要的,但是把小算法翻译一下,肯定是没有问题的。
事实上,我在编写精品课内容的过程中,参考资料里有不少 Java 实现的算法,也有 Python 实现,算法的原理都是相通的,用何种编程语言实现只是对外展示的一张皮而已。但是不可否认,无论我再精心地遣词造句,在很多情况下,自然语言的模糊性对于理解问题还是会存在困扰,如果有直接能看懂的代码做参考,则更容易理解某些细节。我曾经想过将所有例子代码翻译成 Java 版本提供给读者,但是拖拖拉拉,到现在也只完成了几个,照这个速度下去,估计完成的时间会非常感人。
作为一个 Java 程序员,经常会遇到 “ 丧心病狂 ” 的 C++ 算法代码,有时候没有更好的替代例子的情况下,还必须得啃一啃 C++ 代码。往好处想,虽然 C++ 涉及的内容广泛,不是一两天就能掌握,但是算法能用到的部分并不多,都是一些基本语言元素,用一两天的时间了解一下 C++ 相关的内容,能够看懂小段的 C++ 算法代码是完全有可能的。
接下来,我计划用 1 ~ 2 篇文章介绍一下怎么做这个事情,我会在算法涉及的层面上,比较一下 C++ 和 Java 在写算法上的差异,通过这些对比和比较,Java 程序员能快速理解我的博客和书里给出的算法实现的例子,C++ 程序员也能看懂简单的 Java 算法代码。两种语言的对比会放在一起展示,如不做特殊说明,上面的代码是 C++ 的实现方式,下面的是 Java 的实现方式。此外,C++ 语法层面使用的版本是 C++ 11 和 C++ 11 之后的版本,Java 使用的标准是 Java 6 和 Java 6 以后的版本。
C++ 和 Java 语法特性的相似性大约在上个世纪 90 年代中期,Sun 微系统公司的帕特里克 · 诺顿被自己写的 C 编译器弄得焦头烂额,大量的指针和难用的 API 接口,很可能毁掉 Sun 公司试图进入适用于小型家电设备的嵌入式设备领域的雄心壮志。作为当时 Sun 公司的首席科学家的比尔 · 乔伊果断地决定放弃使用 C 语言嵌入式系统,他们决定参考 C++ 的实现思想,但是放弃 C++ 从 C 语言继承来的指针(当然,他们对 C++ 的多继承也不感冒),实现一种新的编程语言。
于是 Sun 公司成立了一个名为 “ Green ” 的项目,参加这个项目的人还有詹姆斯 · 高斯林和麦克 · 舍林丹。牛人就是不一样,工具不好用就创造新工具,绝不妥协,于是 Oak(橡树)语言诞生了,命名为 Oak,是因为詹姆斯 · 高斯林看到了窗外的一棵橡树,有感而发,其他人也觉得甚好。后来在 Sun 公司的支持下,比尔 · 乔伊决定公开 Oak 的源代码,但是 Oak 的商标已经被注册,所以大家决定将其改名为 Java,这是一种他们常喝的咖啡的名字(Java 爪哇咖啡 ),这也是 Java 的图标是一杯冒着热气的咖啡的原因。
至此,你应该知道,因为历史原因,同为 C 语言家族的 Java 和 C++ 语言层面的相似性是有客观基础的。我通常是这样理解的:Java 是跨平台的 C++,是一种更好的 C++(是不是有点拉仇恨的感觉)。
基本数据类型C++ 的基本数据类型有:int、unsigned int、long、unsigned long、short、unsigned short、char、unsigned char、bool、float 和 double;
相应的,Java 也有 8 种基本数据类型,分别是:byte、short、int、long、float、double、char 和 boolean。
大部分情况下,两种语言的基本数据类型可以根据下表进行一对一的转换,但是也有差异,需要特别注意。
首先是 char、C++ 的 char 是 8 比特无符号整数,顺便表示了 ASCII 字符,C++ 程序员常用 char 型的数组存储和表示字符串。Java 的 char 是 16 比特,天生就可以表示宽字符集的字符。
另一个需要注意的是 long 类型,C++ 的 long 是不可移植类型,在不同的系统上其长度不一样,可能是 32 位,也可能是 64 位,所以 C++ 程序员应尽量避免使用 long,除非你觉得 C++ 还不够刺激。Java 的 long 比较单纯,无论是 32 位的系统还是 64 位的系统,它都表示 64 位整数。
C++ 数据类型Java 数据类型长度int/unsigned int
int32 比特short/unsigned short
short16 比特,无论是 C++ 还是 Java,尽量少用shortunsigned char
byte8 比特,数据范围 -128 ~ 127boolbooleanbool 类型,值都只有 true 和 falsefloatfloat32 位,单精度浮点数doubledouble64 位,双精度浮点数
C++ 用字面量符号 f 或 F 表示一个直接数字是 float 类型浮点数,比如 250.1 f(或 250.1 F),这一点,Java 也是一样的。
反过来,Java 会用 d 或 D 表示一个直接数字是 double 类型的浮点数,比如 200.0d 或(200.0D),但是,C++ 不需要(也不支持 d 或 D),因为 C++ 默认一个浮点型的直接数字就是 double 类型。C++ 用字面量符号 l 或 L 表示 long,用 UL 表示 unsigned long,因为 long 不建议使用,所以基本上我的代码里是不会出现这些内容的。
字符串C++ 程序员喜欢的用 char * 或 char 类型的数组存储字符串,这其实是 C 语言用户带过来的习惯,我给出的 C++ 算法实现对字符串一般都用 std::string
,对应 Java 的 String。
如果十分特别的遇到我在算法实现中用了 char 类型的数组存储和表示字符串,Java 程序员可理解为结尾是 0 值的 byte 或 char 数组。
std::string
和 String
的用法对照如下表所示:
std::string name("Simon Zhang");
String name = new String("Simon Zhang");
std::string name = "Simon Zhang";
String name = "Simon Zhang";
std::string surname = name.substr(6,5);
//第二个参数是取的子串长度,从位置6开始取5个字符String surname = name.substring(6,11);
//注意,第二个参数不是count,而是end索引,取从位置6开始到位置11的子串std::string newname = "Jack " + surname;
String newname = "Jack " + surname;
newname.append(" is a good man!");
newname.concat(" is a good man!");
newname.length();
newname.length();
char k = newname.at(3);
char k = newname.charAt(3);
bool eq = (newname.compare(name) == 0);
boolean eq = newname.equals(name);
char k = newname[3];
char k = newname.charAt(3);
newname.clear();
newname = "";
基本语法
Java 的语法和 C++ 十分地相似,但是语言层面还是有一些不同。C++ 允许全局函数的存在,但是 Java 不允许,不过 Java 也留了个口子,就是用静态成员函数。即使用静态成员函数,你也要把全局函数分分组,放在不同的全局类里,这其实客观上促使程序员做一些简单的设计,比不假思索的给全局空间扔一堆全局符号要好很多。Java 没有指针,对象的传递和返回都是用的引用的方式,并且不需要像 C++ 那样用 “ & ” 做特殊的语法标记。
大多数介绍 Java 的书籍开篇就是类和抽象,然后才是基本的语法,这和 Java 上等人的气质是一致的,连这都不会,咋做程序员?C++ 应该多看看 “ 新闻联播 ”,提升一下气质,少用点指针和全局函数。
不过本文是为了对比 C++ 和 Java 的相似性,所以就从基本语法结构开始介绍。
运算符二者的运算符几乎一样,甚至 “ ++ ” 和 “ -- ” 运算符都一样有前缀式和后缀式两种形式,意义也一样。不仅如此,运算符的优先级规则也是一样的,所以 C++ 代码的基本运算对于 Java 程序员来说,理解起来并不困难。
赋值赋值语句两者基本上是一样的,看看每一行结尾的 “ ; ” 你就知道它们有多相似。
循环C++ 的三种基本循环方式是 while 循环、do...while 循环和 for 循环,Java 都支持,甚至连关键字和 break、continue 控制语句的意义也都完全一样,这几种循环方式不需要多说,一看就明白了。C++ 11 版本引入了一种根据范围循环的语法,一般理解和 Java 的增强 for 循环类似,比如这种 C++ 循环形式:
int numbers[] = { 1, 2, 3, 4, 5, 6, 7 };for(int x : numbers){ std::cout
关注
打赏