本专栏文章主要用于帮助Java使用者快速上手数据结构,刷算法题!
自古以来数据结构界就分为九重天,据说冲破这九重天之后就可以去进攻算法界最终修炼最后成佬,受万人敬仰。
但是这谈何容易,因为每一重天都有神兽把守,想要冲破每一重天都必须收服守护的神兽才行。
守护九重天的神兽分别是:数组、字符串、栈、队列、链表、树、散列表、堆、图。可见他们的战斗力也是逐层增强的。想只凭靠自身的能力拿下他们谈何容易。
不过大家不必惊慌,我这里有一本上古秘籍《Java小子怒闯数据结构九重天》,里面有每一重天神兽的攻略。只要修炼者仔细钻研里面的每一篇,对九重天了如指掌之后,冲破这九重天也是易如反掌的。
今天为大家带来的是第二重天的攻略!

目录
我们可以从这段段基本概念中知道:
也就是说Java中String并不是数据类型,在Java中所有的字符串都是String的实例化对象。
Java中的String
Java中的String类代表字符串,Java 程序中的所有字符串文字(例如“abc”)都为此类的实例。
也就是说,Java程序中所有的双引号字符串,都是 String 类的对象。String 类在 java.lang 包下,所以使用的时候不需要导包!
在Java中String最重要的特点就是:
String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了。我们将这种特性称为String的不可变性。
字符串的不可变性
不可变性:当你给一个字符串重新赋值之后,老值并没有在内存中销毁,而是重新开辟一块空间存储新值。也就是说一旦一个String对象在内存中创建,它将是不可改变的,所有的String类中方法并不是改变String对象自己,而是重新创建一个新的String对象。
例如:
String s="dcm";
String s="ddccmm"
当s的值发生改变之后,ddccmm这个值并没有覆盖dcm,只是重新开发了一个新的空间去储存ddccmm然后将s指向它。
如果我们在实际开发中对很含有大量字符的字符串进行遍历赋值修改,会对内存中产生很多无法释放的字符串对象,造成内存垃圾。
正因为String对象的不可变性,如果需要对字符串进行大量的修改、添加字符、删除字符等操作尽量不要使用String对象,因为这样会频繁的创建新的对象导致程序的执行效率下降。
这时我们可以使用Java中另外一个字符串类StringBuilder。
我们在做题的时候关于字符串一般用的都是String类,但是考虑到我们有时候也会用到StringBuilder类这里我就对StringBuilder类进行稍微细致一点的讲解。
StringBuilder是一个可变的字符串类,我们可以把它看成是一个容器,这里的可变指StringBuilder对象中的内容是可变的。
2.1 StringBuilder类常用的方法

可以看出来,构建一个StringBuilder的对象只能使用它的构造方法来构造,不像String一样可以直接String s= "123"来创建
因为StringBuilder类对象是可变的,所以当我们对一个字符串需要进行改变比较多的时候一般定义为StringBuilder类。
2.2 String和StringBuilder的区别
String 对象是不可改变的。每次使用String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。
StringBuilder对象是动态对象,允许扩充它所封装的字符串中字符的数量,但是您可以为它可容纳的最大字符数指定一个值,当修改 StringBuilder 时,在达到容量之前,它不会为其自己重新分配空间。当达到容量时,将自动分配新的空间且容量翻倍。也就是说当对字符串进行改变时,都是对当前对象的状态进行更新的。
可以使用重载的构造函数之一来指定 StringBuilder 类的容量。
2.3 String类与StringBuilder类的相互转换
String类转换为StringBuilder类
public class String{
public static void main(String[] args){
String s = "baibai";
StringBuilder s1 = new StringBuilder(s);
System.out.println(s1);
}
}
StringBuilder类转换为String类
public class String {
public static void main(String[] args){
StringBuilder s1 = new StringBuilder();
//连续连接
s1.append("abc").append("efg");
String s = s1.toString();
System.out.println(s);
}
}
3.1初始化String对象的两种方法:
//方法一:直接创建
String s1= "大聪明 超牛的";
//方法二:对象创建
String s2 = new String("大聪明 超牛的");
String s3 = new String();//也可以创建一个空串
虽然两种方法看起来是一样的但是本质上是不一样的。
String 创建的字符串存储在公共池中,而 new 创建的字符串对象在堆上。那存放在公共池(常量池)与堆中有什么不一样吗?
我们来举个例子:
String s1 = "大聪明 超牛的"; // String 直接创建
String s2 = "大聪明 超牛的"; // String 直接创建
String s3 = s1; // 相同引用
String s4 = new String("大聪明 超牛的"); // String 对象创建
String s5 = new String("大聪明 超牛的"); // String 对象创建
System.out.println(System.identityHashCode(s1));
System.out.println(System.identityHashCode(s2));
System.out.println(System.identityHashCode(s3));
System.out.println(System.identityHashCode(s4));
System.out.println(System.identityHashCode(s5));
输出:

可见前三个字符串的地址相同,后两个各不相同!
这是因为直接创建的字符串时,会先在公共池中找有没有这样的字符串,如果有那就将引用直接指向它,而不去开发新的空间。在这里s1,s2,s3这三个引用指向了公共池中的同一块内存。
对象创建时,每次都会在堆上开新的空间来存放字符串,也就是说s4,s5分别指向在堆上的两块不同的内存,只不过这两块内存里面都储存着相同的东西。
这里再次强调一下,我们在做题的时候遇到关于字符串相关题目我们几乎都是使用String类来解决问题,除了在字符串进行大量更改时我们可能会暂时用到StringBuilder类。
这里的暂时就是我们在对字符串更改等操作之后一般还是要把字符串转换为String类的。
所以我们要学习的API主要还是String类的API。对应刷题我StringBuilder的API我们只需要学习上面提到的两个就够了。
String 类在 java.lang 包下,所以使用的时候不需要导包!
4.1 基本数据类型转换成字符串
有三种办法:
(1)基本类型数据的值+“” (最常用,最简单);
(2)使用包装类中的静态方法static String toString(int i)返回一个表示指定整数的String 对象。如:在Integer中:Integer.toString(6);
(3)使用String类中的静态方法static String valueOf(int i)返回int 参数的字符串表示形式。如:String.valueOf(6);
String 类别中已经提供了将基本数据型态转换成 String 的 static 方法也就是 String.valueOf() 这个参数多载的方法 :
String.valueOf(boolean b)
//将 boolean 变量 b 转换成字符串
String.valueOf(char c)
//将 char 变量 c 转换成字符串
String.valueOf(char[] data)
//将 char 数组 data 转换成字符串
String.valueOf(char[] data, int offset, int count)
//将char数组data中由data[offset]开始取 count个元素转换成字符串
String.valueOf(double d)
//将 double 变量 d 转换成字符串
String.valueOf(float f)
//将 float 变量 f 转换成字符串
String.valueOf(int i)
//将 int 变量 i 转换成字符串
String.valueOf(long l)
//将 long 变量 l 转换成字符串
String.valueOf(Object obj)
//将 obj 对象转换成 字符串, 等于 obj.toString()
因为是静态方法所以不需要实例化。
4.2 字符串转换为基本数据类型
一般使用包装类的静态方法
parseXX("字符串")
要将 String 转换成基本数据类型大多需要使用基本数据型态的包装类别,如:String 转换成 byte可以使用 Byte.parseByte(String s)
Byte.parseByte(String s)
//将 s 转换成 byte
Byte.parseByte(String s, int radix)
//以 radix 为基底 将 s 转换为 byte
Double.parseDouble(String s)
//将 s 转换成 double
Float.parseFloat(String s)
//将 s 转换成 float
Integer.parseInt(String s)
//将 s 转换成 int
Long.parseLong(String s)
//将 s 转换成 long
注意这里也是静态方法,只不过都是对应包装类的静态方法
4.3 使用length()得到一个字符串的字符个数
int len = String.length();
4.4 使用toCharArray() 将一个字符串转换成字符数组
Char[] arr = String.toCharArray();
4.5 判断两个字符串的内容是否相等返回true/false
String1.equals(String2);//区分大小写
String1.equalsIgnoreCase(String2);//不区分大小写
4.6 与位置相关的字符串
charAt(int)//得到指定下标位置对应的字符
indexOf(String)//得到指定内容第一次出现的下标
lastIndexOf(String)//得到指定内容最后一次出现的下标
4.7 将一个字符串按照指定内容劈开split(String) ,返回字符串数组。
String s = "wa,dcm,nb!";
String[] str = s.split(",");//返回结果中str[1]=dcm
4.8 contains(String) 判断一个字符串里面是否包含指定的内容,返回true/false
Boolean a = String1.contains(String2)
4.9 使用substring()截取字符串,返回子串
String.substring(int)//从指定下标开始一直截取到字符串的最后
String.substring(int,int)//从下标x截取到下标y-1对应的元素
4.10 字符串大小写转换
String.toUpperCase()//将一个字符串全部转换成大写
String.toLowerCase()//将一个字符串全部转换成小写
4.11 使用replace()进行字符串内容替换
String.replace(String,String)//将某个内容全部替换成指定内容
String.replaceAll(String,String)//将某个内容全部替换成指定内容,支持正则
String.repalceFirst(String,String)//将第一次出现的某个内容替换成指定的内容
把字符串的单个字符转化为对应数组下标,遍历一遍字符串获得26个字母分别出现几次。然后在遍历一遍字符串看哪个字符先出现次数为1,就输出对应下标。
class Solution {
public int firstUniqChar(String s) {
int len = s.length();
int[] vis = new int[26];
int temp = -1;
for(int i = 0; i < len; i ++) {
vis[s.charAt(i) - 'a'] ++;
}
for(int i = 0; i < len; i ++) {
if(vis[s.charAt(i) - 'a'] == 1) {
return i;
}
}
return -1;
}
}
或者我们也可以把字符串先转换为字符数组来解题,原理都是一样的!
class Solution {
public int firstUniqChar(String s) {
int[] arr = new int[26];
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
arr[chars[i] - 'a']++;
}
for (int i = 0; i < chars.length; i++) {
if (arr[chars[i] - 'a'] == 1) {
return i;
}
}
return -1;
}
}
算法对程序员来说及其重要,语言和开发平台不断变化,但是万变不离其宗的是那些算法和理论,刷算法最最最直白的原因就是找一个好的工作,那刷题一定是必不可少的就是一个好的刷题平台。现在算法刷题平台鱼龙混杂,非常多。如何选择一个正确的刷题平台呢?
这里我推荐牛客网如果你对相应语言的基础语法已经比较自信了那你也可以刷算法题,这里牛客网对算法题的难度分类也是很棒的,有入门题,面试必刷题,面试高频题等等。如果基础比较差那么也不用担心,牛客网上面也有基础语法题来帮助你更好地学习基础。

为了大家方便刷题我直接把牛客网的链接放在下面,大家点击蓝色字体就可以直接进行跳转刷题了!
传送门:刷题入口
刷题这件事,大家一定要认真起来,不可懈怠!
恭喜你修炼到这里,你已经基本有了收服神兽字符串的能力。神兽字符串是我们到进攻算法界最基础的能力之一。大家不可懈怠。
感兴趣的修炼者可以关注下面公众号,会持续推送最新更新的!
持续更新中…
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
在我的Rails(2.3,Ruby1.8.7)应用程序中,我需要将字符串截断到一定长度。该字符串是unicode,在控制台中运行测试时,例如'א'.length,我意识到返回了双倍长度。我想要一个与编码无关的长度,以便对unicode字符串或latin1编码字符串进行相同的截断。我已经了解了Ruby的大部分unicode资料,但仍然有些一头雾水。应该如何解决这个问题? 最佳答案 Rails有一个返回多字节字符的mb_chars方法。试试unicode_string.mb_chars.slice(0,50)
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje
我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我有一大串格式化数据(例如JSON),我想使用Psychinruby同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解
在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg