【java学习】String字符串

1,概念

1)String 不可变

不可变类:final,不可被继承。

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    // 省略... 
}

①创建过程

相同的字符串存在常量池中。

String s = "Google";
System.out.println("s = " + s);

s = "Runoob";
System.out.println("s = " + s);

这里写图片描述
在实例中的 s 只是一个 String 对象的引用,并不是对象本身,当执行 s = “Runoob”; 创建了一个新的对象 “Runoob”,而原来的 “Google” 还存在于内存中。

②地址比较

在内存中,相同的字符串只存在一个实例,所以地址相同;但char[]地址不同。
只要对字符串改变,就会产生新的实例。

public void test() {
        String str1 = "hello world";
        String str2 = "hello world";
        if (str1 == str2)
            System.out.println("str1 == str2");
        else
            System.out.println("str1 != str2");
        char[] str3 = "hello world".toCharArray();
        char[] str4 = "hello world".toCharArray();

        if (str3 == str4)
            System.out.println("str3 == str4");
        else
            System.out.println("str3 != str4");
    }

结果:

str1 == str2
str3 != str4

③new String

1,new会产生新的字符串。

String i3 = "100";
String i4 = "10"+new String("0");
System.out.println(i3 == i4);           //false
String i7 = "100";
String i8 = "10" +"0";
System.out.println(i7 == i8);           //true

2,new 产生一个字符串(假设为 ”china” )时,会先去常量池中查找是否已经有了 “china” 对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中 “china” 对象的拷贝对象。

 String ss3 = new String("china");   //产生两个对象,一个在常量池一个在堆中

④为啥要设计成final类?

  1. 安全性
    用户名、密码、访问链接、类的全限定名等大量的字符串用于存储敏感信息,字符串可变会导致这些内容不再可信。
  2. 线程安全
    多个线程改变同一个字符串不会影响其它线程,而是创建一个新的字符串放在缓存中。
  3. hashCode
    String类中的hashCode被重写,不可变性保证了字符串的值不会变,只要两个字符串一致它们hashCode就一样,且第一次使用时就缓存,这样计算hashCode更快。
  4. 缓存池和性能
    因为不可变性所以字符串存储在缓存池中,作为常用的数据结构之一极大的提高了性能。

2)空串

含有一个或多个空格字符的串称为空串

3)形参传递

引用类型作为形参传递会改变实参的值,但是String是特殊的引用类型,作为形参传递不会影响实参的值。

4)字符串长度限制

  1. 编译期间字符串长度限制:65535
    CONSTANT_Utf8_info用于表示String类型的常量对象,其结构的长度限制;
  2. 运行期间长度限制:231 -1
    String的length参数是int,最大长度就是int的最大范围:java.lang.Integer#MAX_VALUE

2,String、StringBuffer和StringBuilder区别

1)String

字符串常量。
是不可变对象。每次对String对象改变时,都等同生成了一个新的String对象,然后指针指向新的String对象,从而耗内存,影响系统性能。
在一下字符串对象生成中,String效率远比StringBuffer快:

 String S1 =This is only a” + “ simple” + “ test”;
 StringBuffer Sb = new StringBuilder(This is only a”).append(“ simple”).append(“ test”);

但如果拼接的字符串来自另外的String对象,速度就会变慢,如:

String S2 =This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;

好的编码习惯:
equalsIgnoreCase()替代equals()
equalsIgnoreCase()==》不区分大小写。

2)StringBuffer(线程安全)

①概念

区别于不可变对象String,StringBuffer和StringBuilder底层是char[]数组实现的,是可变长度的。
字符串变量(线程安全),类似String字符串缓冲区。
推荐使用。每次修改对象的值都是修改对象本身,不生成新的对象。减少GC,提高效率。

②容量

StringBuffer默认初始大小为16;
StringBuffer扩容:

s = new StringBuffer(x);
s.append(y);
1.当y<x时,capacity值为x;
以下情况,容器容量需要扩展:
2.当x<y<2*x+2时,值为 2*x+2
3.当y>2*x+2时,值为y

StringBuffer和StringBuilder的默认大小为16
ArrayList和LinkedList的默认大小10

③方法

方法说明举例
append追加字符串StringBuffer之所以是线程安全的就是因为append方法是synchronized修饰的
insert在指定点添加字符 z.insert(4, "le")//“start”-->“starlet”
length返回当前长度
capacity返回初始化的长度

3)StringBuilder(非线程安全)

字符串变量(非线程安全).
比StringBuffer快,但不保证同步,多用在单线程(推荐)。==》java中没有绝对的线程安全,这样说更合适一点:无状态类中使用。

字符串拼接底层原理:
str+str1其实就是每一次new StringBuilder(),然后.append 。==>不能在for循环种使用字符串拼接。频繁new StringBuilder耗费资源。

String s1 = "aaa";
String s2 = "bbb";
String s3 = s1 + "," + s2;
//经过反编译,其实是:String s3 = new StringBuilder().append(s1).append(",").append(s2).toString();

String s4 = s1 + s2;
//经过反编译,会发现 s4="aaabbb";常量池中创建了一个新的字符串常量。因为常量池存放的值必须是编译前就确定的。对于s3中的s1和s2是变量,在编译前并不确定其值,所以并不能放入常量池中。
常用API说明
sb.deleteCharAt(0)删除第0个位置的char
sb.toString()转换为字符串
sb.append()追加字符串

3,Character类

1)概念

Character 类用于对单个字符进行操作。
Character 类在对象中包装一个基本类型 char 的值

// 字符数组
char[] charArray ={ 'a', 'b', 'c', 'd', 'e' };

Character ch = new Character('a');

// 原始字符 'a' 装箱到 Character 对象 ch 中
Character ch = 'a';
// 返回拆箱的值到 'c',原始字符 'x' 用 test 方法装箱
char c = test('x');

2) java.lang.Character API

方法说明
isLetter()是否是一个字母
isDigit()是否是一个数字字符
isWhitespace()是否是一个空格
isUpperCase()是否是大写字母
isLowerCase()是否是小写字母
toUpperCase()指定字母的大写形式
toLowerCase()指定字母的小写形式
toString()返回字符的字符串形式,字符串的长度仅为1

4,字符串函数

1)转换

把一个基本数据类型转为字符串,基本数据类型data.toString()是最快的方式、String.valueOf(data)次之、data+""最慢。
因为String.valueOf()方法底层调用了Integer.toString()方法,但是会在调用前做空判断;Integer.toString()是直接调用了;i+""底层使用StringBuilder实现,先用append方法拼接,再用toString()方法获取字符串。

1>String 2 char[]

char[] ch = str.toCharArray();

2>String 2 String[]

将分隔符分开的字符串转换为数组

data = "aa_bb_cc";
String[] param = data.split("_");

param={“aa”,“bb”,“cc”}

3>char[] 2 String

字符数组转为字符串

String.valueOf(ch);

4>String2Uri

Uri uri = Uri.parse("skdfjlk");

5>int2String

int i = 1;  
String.valueOf(i);//方法一  
Integer.toString(i);//方法二  
String iStr = i+"";//方法三  

举例:

如果int x=20, y=5,则语句System.out.println(x+y +“”+(x+y)+y); 的输出结果是()
答案:25255

6>时间相关转换

  1. String转Date转Long
String dateStr = "2010-05-04 12:34:23";
        Date date = new Date();
        //注意format的格式要与日期String的格式相匹配
        DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            //String -> Date
            date = sdf.parse(dateStr);  //Tue May 04 12:34:23 CST 2010
            //Date -> String
            dateStr = sdf.format(date);
            //String ->Timestamp
            //String的类型必须形如: yyyy-mm-dd hh:mm:ss[.f...] 这样的格式,中括号表示可选,否则报错!!!
            Timestamp ts = Timestamp.valueOf(dateStr);
            //Timestamp -> String
            dateStr = sdf.format(ts);
            //Timestamp -> Date
            date = ts;
            //Date -> Timestamp  父类不能直接向子类转化,可借助中间的String
            //Timestamp -> Long(ms)
            Long time = ts.getTime();
            //Long(ms) -> Timestamp
            ts = new Timestamp(time);
        } catch (Exception e) {
            e.printStackTrace();
        }
  1. 获取前一天日期:
        DateFormat df = new SimpleDateFormat("YYYYMMdd");
        Date date = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        date = calendar.getTime();
        return df.format(date);
  1. 获取当前时间戳(Unix Timestamp):
long epoch = System.currentTimeMillis()/1000;
new Timestamp(System.currentTimeMillis())
  1. String2LocalDateTime
         DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
         LocalDateTime time = LocalDateTime.now();
         String localTime = df.format(time);
         LocalDateTime ldt = LocalDateTime.parse("2018-01-12 17:07:05",df);
         System.out.println("LocalDateTime转成String类型的时间:"+localTime);
         System.out.println("String类型的时间转成LocalDateTime:"+ldt);
  1. Timestamp和LocalDateTime 转换
//Timestamp 转 LocalDateTime 
LocalDateTime localDateTime = timestamp.toLocalDateTime();
//Timestamp time = Timestamp.valueOf(LocalDateTime.now());

8>String2int

		String s = "10";  
        int i1 = Integer.valueOf(s);//方法一  
        int i2 = Integer.parseInt(s)+1;//方法二  

9>编码方式转换

常见的字符编码:

  1. ASCII码
    128个字符;
  2. Unicode字符集:UTF-7 UTF-7.5 UTF-8 UTF-16 UTF-32
  3. 中文编码:GBK GB2312 BGB18030
//将ISO8859-1字符串转成GB2312编码  
new String("ISO8859-1".getBytes("ISO8859-1"),"GB2312")

10>String2List

String str = "a,b,c";
List<String> list1 = Arrays.asList(str.split(",")); //[a, b, c]

11> List2String

List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
String str1 = StringUtils.join(list, ","); //a,b,c

12>String[]2List

String[] array = {"a","b","c"};
  1. 使用Arrays.asList()
List<String> resultList= new ArrayList<>(Arrays.asList(array));

注意:调用Arrays.asList()时,其返回值类型是ArrayList,但此ArrayList是Array的内部类,调用add()时,会报错:java.lang.UnsupportedOperationException,并且结果会因为array的某个值的改变而改变,故需要再次构造一个新的ArrayList。
2. 使用List.of() (java9)

List<String> resultList = List.of(array);

此方法为 Java9新增方法,定义在List接口内,并且为静态方法,故可以由类名直接调用。
3. 使用Collections.addAll()

List<String> resultList = new ArrayList<>(array.length);
Collections.addAll(resultList,array);

2) Java String API

1	char charAt(int index)
返回指定索引处的 char 值。
2	int compareTo(Object o)
把这个字符串和另一个对象比较。
o -- 可以是一个 Byte, Double, Integer, Float, LongShort 类型的参数。
如果指定的数与参数相等返回0。
如果指定的数小于参数返回 -1。
如果指定的数大于参数返回 13	int compareTo(String anotherString)
按字典顺序比较两个字符串。
4	int compareToIgnoreCase(String str)
按字典顺序比较两个字符串,不考虑大小写。
5	String concat(String str)
将指定字符串连接到此字符串的结尾。(也可以使用+,它可以连接任何数据)
6	boolean contentEquals(StringBuffer sb)
当且仅当字符串与指定的StringButter有相同顺序的字符时候返回真。
7	static String copyValueOf(char[] data)
返回指定数组中表示该字符序列的 String8	static String copyValueOf(char[] data, int offset, int count)
返回指定数组中表示该字符序列的 String9	boolean endsWith(String suffix)
测试此字符串是否以指定的后缀结束。
10	boolean equals(Object anObject)
将此字符串与指定的对象比较。
11	boolean equalsIgnoreCase(String anotherString)
将此 String 与另一个 String 比较,不考虑大小写。
12	byte[] getBytes()
 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
13	byte[] getBytes(String charsetName)
使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
14	void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
将字符从此字符串复制到目标字符数组。
15	int hashCode()
返回此字符串的哈希码。
16	int indexOf(int ch)
返回指定字符在此字符串中第一次出现处的索引。
17	int indexOf(int ch, int fromIndex)
返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。
18	int indexOf(String str)
 返回指定子字符串在此字符串中第一次出现处的索引。
19	int indexOf(String str, int fromIndex)
返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
str1.IndexOf("字")//查找“字”在str1中的索引值(位置)
str1.IndexOf("字串")//查找“字串”的第一个字符在str1中的索引值(位置)
str1.IndexOf("字",start,end)//从str1第start+1个字符起,查找end个字符,查找“字”在字符串STR1中的位置[从第一个字符算起]注意:start+end不能大于str1的长度
20	String intern()
 返回字符串对象的规范化表示形式。
21	int lastIndexOf(int ch)
 返回指定字符在此字符串中最后一次出现处的索引。
22	int lastIndexOf(int ch, int fromIndex)
返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索。
23	int lastIndexOf(String str)
返回指定子字符串在此字符串中最右边出现处的索引。
24	int lastIndexOf(String str, int fromIndex)
 返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
25	int length()
返回此字符串的长度。
26	boolean matches(String regex)
告知此字符串是否匹配给定的正则表达式。
27	boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
测试两个字符串区域是否相等。
28	boolean regionMatches(int toffset, String other, int ooffset, int len)
测试两个字符串区域是否相等。
29	String replace(char oldChar, char newChar)
返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
30	String replaceAll(String regex, String replacement
使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
31	String replaceFirst(String regex, String replacement)
 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
32	String[] split(String regex)
根据给定正则表达式的匹配拆分此字符串。
33	String[] split(String regex, int limit)
根据匹配给定的正则表达式来拆分此字符串。
将“abc*ad*ac”换为“abc”,“ad”,"ac"
String[] str_split = new String[3];
str_split = str.split("\\*")
34	boolean startsWith(String prefix)
测试此字符串是否以指定的前缀开始。
35	boolean startsWith(String prefix, int toffset)
测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
36	CharSequence subSequence(int beginIndex, int endIndex)
 返回一个新的字符序列,它是此序列的一个子序列。
37	String substring(int beginIndex)
返回一个新的字符串,它是此字符串的一个子字符串。
38	String substring(int beginIndex, int endIndex)
返回一个新字符串,它是此字符串的一个子字符串。
str.substring(3);//"Hello world!"-->"lo world!"
str.substring(3,7);//"Hello world!"-->"lo w"
str = str.substring(0,str.length()-1) 去掉最后一位
39	char[] toCharArray()
将此字符串转换为一个新的字符数组。
40	String toLowerCase()
使用默认语言环境的规则将此 String 中的所有字符都转换为小写。
41	String toLowerCase(Locale locale)
 使用给定 Locale 的规则将此 String 中的所有字符都转换为小写。
42	String toString()
 返回此对象本身(它已经是一个字符串!)。
43	String toUpperCase()
使用默认语言环境的规则将此 String 中的所有字符都转换为大写。
44	String toUpperCase(Locale locale)
使用给定 Locale 的规则将此 String 中的所有字符都转换为大写。
45	String trim()
返回字符串的副本,忽略前导空白和尾部空白。
RTrim: s = s.ToString().RTrim(',') ; //直接trim掉一些字符 
类似的还有TrimEndTrimStart,LTrimchar[] MyChar = {'5',','};  s = s.TrimEnd(MyChar); 
46	static String valueOf(primitive data type x)
返回给定data type类型x参数的字符串表示形式。

3)格式化字符串(字符串拼接)

①对整数进行格式化

%[index$][标识][最小宽度]转换方式

格式化字符串由4部分组成:

  1. 特殊的格式:以%index$开头,index从1开始取值,表示将第index个参数拿进来进行格式化
  2. 标识:
符号说明
-在最小宽度内左对齐,不可以与"用0填充"同时使用
#只适用于8进制和16进制,8进制时在结果前面增加一个0,16进制时在结果前面增加0x
+结果总是包括一个符号(一般情况下只适用于10进制,若对象为BigInteger才可以用于8进制和16进制)
正值前加空格,负值前加负号一般情况下只适用于10进制,若对象为BigInteger才可以用于8进制和16进制
0结果将用零来填充
,只适用于10进制,每3位数字之间用","分隔
(若参数是负数,则结果中不添加负号而是用圆括号把数字括起来(同’+'具有同样的限制)
  1. [最小宽度]:最终该整数转化的字符串最少包含多少位数字。
  2. 转换方式:d-十进制 o-八进制 x或X-十六进制
    举例:
System.out.println(String.format("%1$,09d", -3123));//-0003,123
System.out.println(String.format("%1$9d", -31));//      -31
System.out.println(String.format("%1$-9d", -31));//-31
System.out.println(String.format("%1$(9d", -31));// (31)

②对浮点数进行格式化:

%[index$][标识][最少宽度][.精度]转换方式

转换方式:

'e', 'E' -- 结果被格式化为用计算机科学记数法表示的十进制数
'f' -- 结果被格式化为十进制普通表示方式
'g', 'G' -- 根据具体情况,自动选择用普通表示方式还是科学计数法方式
'a', 'A' -- 结果被格式化为带有效位数和指数的十六进制浮点数

③对字符进行格式化

对字符进行格式化是非常简单的,c表示字符,标识中’-'表示左对齐,其他就没什么了。

5,数据结构-串

1)概念

字符位置:字符在序列中的序号(从1开始)
子串位置:第一个字符在主串的位置。

2)串的模式匹配:KMP算法(克努特—莫里斯—普拉特操作)

①概念

串的模式匹配:子串的定位操作通常称做模式匹配,是各种串处理系统中最重要的操作之一。
KMP算法:给定两个字符串O和f,长度分别为n和 m,判断f是否在O中出现,如果出现则返回出现的位置。常规方法是遍历O的每一个位置,然后从该位置开始和f进行匹配,但是这种方法的复杂度是 O(nm)。kmp算法通过一个O(m)的预处理,使匹配的复杂度降为O(n+m)。

②KMP算法核心

每次匹配失败就隔N-X个字符进行下一次匹配(因为N的大小是N,没有N个字符是不可能匹配成功的)(X指的是在上一段字符中能匹配成功的字符数)

③复杂度分析

最好情况:每趟匹配不成功都是在第一个字符,即每趟都只需匹配一次就知道该趟是否匹配。O(m+n)
最坏情况:每趟匹配不成功都是在最后一个字符。时间复杂度O(m*n)

④计算

计算前缀 Next[i] 的值:
我们令 next[0] = -1 。从 next[1] 开始,每求一个字符的 next 值,就看它前面是否有一个最长的"字符串"和从第一个字符开始的"字符串"相等(需要注意的是,这2个"字符串"不能是同一个"字符串")。如果一个都没有,这个字符的 next 值就是0;如果有,就看它有多长,这个字符的 next 值就是它的长度。

计算修正后的 Nextval[i] 值:
我们令 nextval[0] = -1。
从 nextval[1] 开始,如果某位(字符)与它 next 值指向的位(字符)相同,则该位的 nextval 值就是指向位的 nextval 值(nextval[i] = nextval[ next[i] ]);
如果不同,则该位的 nextval 值就是它自己的 next 值(nextvalue[i] = next[i])。

举例:
这里写图片描述
计算前缀 Next[i] 的值:(next数组里面就是最长重复子串字符串的个数)
next[0] = -1;定值。
next[1] = 0;s[1]前面没有重复子串。
next[2] = 0;s[2]前面没有重复子串。
next[3] = 0;s[3]前面没有重复子串。
next[4] = 1;s[4]前面有重复子串s[0] = 'a’和s[3] = ‘a’。
next[5] = 2;s[5]前面有重复子串s[01] = 'ab’和s[34] = ‘ab’。
next[6] = 3;s[6]前面有重复子串s[012] = 'abc’和s[345] = ‘abc’。
next[7] = 4;s[7]前面有重复子串s[0123] = 'abca’和s[3456] = ‘abca’。

计算修正后的 Nextval[i] 值:
nextval[0] = -1;定值。
nextval[1] = 0; ( s[1] = b ) != ( s[ next[1] ] = s[0] = a ),nextval[1] = next[1] = 0。
nextval[2] = 0; ( s[2] = c ) != ( s[ next[2] ] = s[0] = a ),nextval[2] = next[2] = 0。
nextval[3] = -1; ( s[3] = a ) == ( s[ next[3] ] = s[0] = a ),nextval[3] = nextval[ next[3] ] = nextval[0] = -1。
nextval[4] = 0; ( s[4] = b ) == ( s[ next[4] ] = s[1] = b ),nextval[4] = nextval[ next[4] ] = nextval[1] = 0。
nextval[5] = 0; ( s[5] = c ) == ( s[ next[5] ] = s[2] = c ),nextval[5] = nextval[ next[5] ] = nextval[2] = 0。
nextval[6] = -1; ( s[6] = a ) == ( s[ next[6] ] = s[3] = a ),nextval[6] = nextval[3] = -1。
nextval[7] = 4; s[7] != s[4],nextval[7] = next[7] = 4。

注意:下标可以从1开始,那么nextval各位加1,应该为:0,1,1,0,1,1,0,5

再举个例子感受下next的计算:

i        0  1  2  3  4  5  6  7  8
s        a  b  a  b  a  a  b  a  b
next    -1  0  0  1  2  3  1  2  3 
nextval -1  0 -1  0 -1  3  0 -1  0

6,其他应用

1)判断字符串是否为数字

public class TypeJudgeUtil {  
  
    /** 
     * 判断字符串是否是数字 
     * @param s 
     * @return 
     */  
    public static boolean isNum(String s){  
          
        boolean result = false;  
          
        if ( null == s || s.equals("")) {  
            result = false;  
        }  
        else {  
            try {  
                Integer.parseInt(s);  
                result = true;  
            } catch (Exception e) {  
                result = false;  
            }  
        }  
        return result;  
    }  
}  

2)SpannableString

①设置分段字的颜色

 SpannableStringBuilder builder=new SpannableStringBuilder("");
        builder.setSpan(new ForegroundColorSpan(Color.RED),0,1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

②设置分段字的大小

//分段设置字的大小(第一个字号为12sp,第二个字号为13sp)
SpannableStringBuilder builder = new SpannableStringBuilder("¥" + str);
builder.setSpan(new AbsoluteSizeSpan(ChangeView.dip2px(context, 12)), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.setSpan(new AbsoluteSizeSpan(ChangeView.dip2px(context, 13)), 2, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv_price.setText(builder);

③设置分段点击事件

SpannableString spanableInfo = new SpannableString("温馨提示:为注册订易点账号的手机号,登录是将自动注册,且代表您已同意" + "《用户服务协议》");
spanableInfo.setSpan(new Clickable(clickListener), 34, 42, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

④文字中添加图片

drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); 
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM); 
SpannableString spannableString = new SpannableString(" "); 
spannableString.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

3)字符串智能截取

package luo.StringCut;
/**
 * 字符串截取函数
 * 要求:
 * 1,输入一个字符串和字节数,输出为按字节截取的字符串。
 * 2,保证汉字不被截半个,例如("人ABC",4)="人AB",("人ABC们EF",6)="人ABC"
 * @author luo
 *
 *分析:
 *java语言中,默认使用Unicode编码方式,即每个字符占两个字节,
 *因此可以用来存储中文。
 *虽然String是由char组成,但存储时,每个英文占一个字符,中文2个字符。
 */
public class StringCut {

	/**
	 * 判断字符c是否是中文字符,如果是返回true
	 * @return
	 */
	private static boolean isChinese(char c){
		String s = String.valueOf(c);
		return s.getBytes().length>1 ? true : false;
	}
	public static String StringCut(String str, int len){
		if(null == str || 0 == len){
			return "";
		}
		if(str.length() < len){
			System.out.println("非法信息:截取长度大于字符串长度!");
			return "";
		}
		char[] charArr = str.toCharArray();
		StringBuilder sb = new StringBuilder("");
		int count = 0;//用来记录当前截取字符的长度
		for(char cc:charArr){
			if(count < len){
				if(isChinese(cc)){
					//如果要截取子串的长度只差一个字符,但是接下来的字符是中文,则截取结果子串中不保存这个中文字符
					if(count + 1 == len){
						return sb.toString();
					}
					count = count + 2;
					sb = sb.append(cc);
				}else{
					count = count + 1;
					sb = sb.append(cc);
				}
			}else{
				break;
			}
		}
		return sb.toString();
	}
}

package luo.StringCut;

public class Main {

	public static void main(String[] args){
		//("人ABC",4)="人AB",("人ABC们EF",6)="人ABC"
		System.out.println(StringCut.StringCut("人ABC",4));
		System.out.println(StringCut.StringCut("人ABC们EF",6));
	}
}

4)字符串翻转

不利用java类库,实现字符串翻转

package luo.StringReverse;
/**
 * 不利用java类库,实现字符串翻转
 * @author luo
 *
 *分析:很多种方法可以实现。
 *1,数组翻转法:
 *将字符串转换为字符数组,然后对数组进行翻转,然后再翻转为字符串。
 *2,逆序遍历法:
 *把字符串转换为字符数组,逆向遍历数组,然后拼接遍历到的字符即可。
 *3,递归法:(高水平代码)
 *递归地把字符移动到字符串的最后一个位置。
 */
public class StringReverse {

	/**
	 * 1,数组翻转法:
	 *将字符串转换为字符数组,然后对数组进行翻转,然后再翻转为字符串。
	 */
	public static String reverse1(String str){
		char[] ch = str.toCharArray();
		char c;//交换中间值
		int len = ch.length;
		for (int i = 0; i < len/2; i++) {
			c = ch[i];
			ch[i] = ch[len - i - 1];
			ch[len - i - 1] = c;
		}
		return String.valueOf(ch);
	}
	
	/**
	 *2,逆序遍历法:
	 *把字符串转换为字符数组,逆向遍历数组,然后拼接遍历到的字符即可。
	 */
	public static String reverse2(String str){
		StringBuilder sb = new StringBuilder();
		char[] ch = str.toCharArray();
		for (int i = ch.length-1; i >=0; i--) {
			sb.append(ch[i]);
		}
		return sb.toString();
	}
	
	/**
	 *3,递归法:
	 *递归地把字符移动到字符串的最后一个位置。
	 */
	public static String reverse3(String str){
		
		if(str.length() < 1){
			return str;
		}
		return reverse3(str.substring(1)) + str.charAt(0);
	}
	/**
	*使用栈
	*①将字符串转换为char数组
	*②将char数组中的字符依次压入栈中
	*③将栈中的字符依次弹出赋值给char数组
	*/
	public static String strReverseWithStack(String string){
	    if(string==null||string.length()==0)return string;
	    Stack<Character> stringStack = new Stack<>();
	    char [] array = string.toCharArray();
	    for(Character c:array){
	        stringStack.push(c);
	    }
	    int length = string.length();
	    for(int i= 0;i<length;i++){
	        array[i] = stringStack.pop();
	    }
	    return new String(array);
	}

另一种写法,利用StringBuffer:

String src = "ABCDEF ";
String dst = new StringBuffer(src).reverse().toString();

5)把一个字符串转换成整数

java源码:

    public static int parseInt(String s, int radix)
                throws NumberFormatException
    {
        /*
         * WARNING: This method may be invoked early during VM initialization
         * before IntegerCache is initialized. Care must be taken to not use
         * the valueOf method.
         */

        if (s == null) {
            throw new NumberFormatException("null");
        }
        
		//如果是空字符串,或者进制低于能解析的最小进制(2)或者高于能解析的最大进制(36),则抛出异常;
        if (radix < Character.MIN_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " less than Character.MIN_RADIX");
        }
        if (radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " greater than Character.MAX_RADIX");
        }

        int result = 0;
        boolean negative = false;//是否是负数
        int i = 0, len = s.length();
        int limit = -Integer.MAX_VALUE;
        int multmin;
        int digit;

        if (len > 0) {
            char firstChar = s.charAt(0);
            //根据第一个字符去判断字符串代表的数字是正的还是负的,通过flag negative标记
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;
                } else if (firstChar != '+')
                    throw NumberFormatException.forInputString(s);

                if (len == 1) // Cannot have lone "+" or "-"
                    throw NumberFormatException.forInputString(s);
                i++;
            }
            multmin = limit / radix;
            //取出字符串中的每一位字符,按照进制radix转化为数字,倘若不是数字,则返回值为-1,抛出异常。
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = Character.digit(s.charAt(i++),radix);
                if (digit < 0) {
                    throw NumberFormatException.forInputString(s);
                }
                
                //Java中整数值都是32位的,它是有范围的。我们需要验证字符串转化之后是不是在这个范围以内,即[Integer.MIN_VALUE, Integer.MAX_VALUE]。这里并不存在统一设置上限的写法,因为-Integer.MIN_VALUE > Integer.MAX_VALUE!
                if (result < multmin) {//确保result * radix不会超出界限
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;
                if (result < limit + digit) {//判断最终是否超出界限
                    throw NumberFormatException.forInputString(s);
                }
                
                result -= digit;//最后返回的时候,值如果是负数直接返回,否则返回-result
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
        return negative ? result : -result;
    }

6)替换空格

请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
解题思路:

①从后往前打

复杂度为O(n)的解法:首先计算原字符串长度,空格个数;然后计算替换之后的长度;从字符串后面开始替换:设置两个指针分别指向原、新字符串的尾部,逐个赋值;

public void replaceSpace(char[] str) {
        if (str.length <= 0)
            return;

        int original_length = str.length;
        int number_of_space = 0;
        for (int i = 0; i < str.length; i++) {
            if (str[i] == ' ')
                ++number_of_space;
            ++i;
        }

        if (number_of_space <= 0)
            return;

        int new_length = original_length + 2 * number_of_space;

        char[] new_str = new char[new_length];
        while (original_length > 0 && new_length >= original_length) {
            if (str[original_length - 1] == ' ') {
                new_str[new_length-- - 1] = '0';
                new_str[new_length-- - 1] = '2';
                new_str[new_length-- - 1] = '%';
            } else {
                new_str[new_length-- - 1] = str[original_length - 1];
            }
            --original_length;
        }
        System.out.print(new_str);
    }

②用StringBuilder、StringBuffer类

public String replaceSpace(StringBuffer str) {
        if (str == null)
            return null;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            if (String.valueOf(str.charAt(i)).equals(" ")) {
                sb.append("%20");
            } else {
                sb.append(str.charAt(i));
            }
        }
        return String.valueOf(sb);
    }

7)大数运算

一般用字符串或数组表达大数。

①打印1到最大的n位数

思路:
n可能是大数。所以要用string。打印的时候,也要注意过滤掉String中前几位无用的000.
如果在数字前面补0,我们会发现n位所有十进制其实就是n各从0到9的全排列。
全排列的递归很容易表达,数字的每一位都可能是0~9中的一个数,然后设置下一位。递归结束条件:我们已经设置了数字的最后一位。

    public void printToMaxOfNDigits(int n) {
        int[] array=new int[n];
        if(n <= 0)
            return;
        printArray(array, 0);
    }
    private void printArray(int[] array,int n) {
        for(int i = 0; i < 10; i++) {
            if(n != array.length) {
                array[n] = i;
                printArray(array, n+1);
            } else {
                boolean isFirstNo0 = false;
                for(int j = 0; j < array.length; j++) {
                    if(array[j] != 0) {
                        System.out.print(array[j]);
                        if(!isFirstNo0)
                            isFirstNo0 = true;
                    } else {
                        if(isFirstNo0)
                            System.out.print(array[j]);
                    }
                }
                System.out.println();
                return ;
            }
        }
    }

②一个函数,实现任意两个整数的加法。

考虑大数问题。考虑输入的数字有负数问题。

③其他


    //n-m
    public static String sub(String n, String m){

        int n1 = getPart0(n);
        int m1 = getPart0(m);
        int n2 = getPart1(n);
        int m2 = getPart1(m);
        if (n2 >= m2){
            return String.valueOf(n1-m1) + String.valueOf(n2-m2);
        }else{

            int result = 10;
            while ((n2 /= 10) != 0){
                result *= 10;
            }
            return String.valueOf(n1-m1-1) + String.valueOf(n2+result-m2);
        }
    }

    /**
     * 判断n<m
     * @param n
     * @param m
     * @return
     */
    public static boolean big(String n , String m){
        if (n.length() < m.length()) return true;
        if (n.length() == m.length()){

            if (n.equals(m)) return true;

            int n1 = getPart0(n);
            int m1 = getPart0(m);
            if (n1 < m1 ) return true;
            else{

                int n2 = getPart1(n);
                int m2 = getPart1(m);
                if (n2 <m2) return true;
            }
        }
        return false;
    }

    /**
     * n*2
     * @param n
     * @return
     */
    public static String mul2(String n){

        int n1 = getPart0(n);
        int n2 = getPart1(n);
        String n1Str = String.valueOf(n1*2);
        String n2Str = String.valueOf(n2*2);
        if (n2Str.length() == (n.length() - n.length()/2)) return n1Str+n2Str;
        else return String.valueOf(n1*2 + 1) + n2Str.substring(1,n2Str.length());
    }

    /**
     * 获取前半部分
     * @param n
     * @return
     */
    public static int getPart0(String n){
        if (n.length() <2) return 0;
        return Integer.valueOf(n.substring(0,n.length()/2));
    }

    /**
     * 获取后半部分
     * @param n
     * @return
     */
    public static int getPart1(String n){
        if (n.length() <2) return 0;
        return Integer.valueOf(n.substring(n.length()/2, n.length()));
    }


    /**
     * n/2
     * @param n
     * @return
     */
    public static String div2(String n){

        int n1 = getPart0(n);
        int n2 = getPart1(n);
        if (n1 % 2 == 0){
            return String.valueOf(n1/2) + String.valueOf(n2/2);
        }
        int result = 5;
        while ((n2 /= 10) != 0){
            result *= 10;
        }
        return String.valueOf(n1/2) + String.valueOf(n2/2 + result);
    }

8)字符串排列(全排列)

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

解题思路:将当前位置的字符和前一个字符位置交换,递归。
如下图所示,把字符串分为两部分:一部分是字符串第一个字符,一部分是第一个字符之后的字符排列。接下来我们需要知道阴影部分的字符串排列。
这里写图片描述


import java.util.ArrayList;
import java.util.TreeSet;

    public ArrayList<String> Permutation(String str) {
        ArrayList<String> result = new ArrayList<String>();
        if (str == null || str.length() == 0)
            return result;
        char[] chars = str.toCharArray();
        TreeSet<String> temp = new TreeSet<>();
        Permutation(chars, 0, temp);
        result.addAll(temp);
        return result;
    }

    public void Permutation(char[] chars, int index, TreeSet<String> result) {
        if (chars == null || chars.length == 0)
            return;
        if (index < 0 || index > chars.length - 1)
            return;
        if (index == chars.length - 1) {
            result.add(String.valueOf(chars));
        } else {
            for (int i = index; i <= chars.length - 1; i++) {
                swap(chars, index, i);
                Permutation(chars, index + 1, result);
                // 回退
                swap(chars, index, i);
            }
        }
    }

    public void swap(char[] c, int a, int b) {
        char temp = c[a];
        c[a] = c[b];
        c[b] = temp;
    }

9)第一个只出现一次的字符

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)。

思路:利用LinkedHashMap保存字符和出现次数。

import java.util.LinkedHashMap;

   public int FirstNotRepeatingChar(String str) {
        
        if (str == null || str.length() == 0)
            return -1;
        char[] c = str.toCharArray();
        LinkedHashMap<Character,Integer> hash=new LinkedHashMap<Character,Integer>();
        for(char item : c) {
            if(hash.containsKey(item))
                hash.put(item, hash.get(item)+1);
            else
                hash.put(item, 1);
        }
        for(int i = 0;i < str.length(); i++){
            if (hash.get(str.charAt(i)) == 1) {
                return i;
            }
        }
        return -1;
    }

10)翻转字符串

如将“I am a student”,转为“student a am I”。

思路:先将整个字符串翻转,然后将每个单词翻转。


    public String ReverseSentence(String str) {
        if (str == null || str.length() == 0)
            return str;
        if (str.trim().length() == 0)
            return str;
        StringBuilder sb = new StringBuilder();
        String re = reverse(str);
        String[] s = re.split(" ");
        for (int i = 0; i < s.length - 1; i++) {
            sb.append(reverse(s[i]) + " ");
        }
        sb.append(reverse(s[s.length - 1]));
        return String.valueOf(sb);
    }

    public String reverse(String str) {
        StringBuilder sb = new StringBuilder();
        for (int i = str.length() - 1; i >= 0; i--) {
            sb.append(str.charAt(i));
        }
        return String.valueOf(sb);
    }

12)String2Int 自定义

public static int stringToInt(String str) {
        int result=0;
        char[] ch=str.toCharArray();
        int len=ch.length;
        for(int i=0;i<len;i++) {
            result+=(((int)ch[i]-'0')*Math.pow(10, len-1-i));
        }
       
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值