目录
1.8StringBuffer和StringBuilder的区别
1.9String和StringBuffer分别作为参数传递
一、StringBuffer类
1.1StringBuffer类概述
StringBuffer类是线程安全的可变字符序列,类似于String的字符串缓冲区,但是不能修改,
但是可以通过某些方法调用可以改变该序列的长度和内容,
String是一个不可变的序列,而StringBuffer是一个可变的字符序列。
这里的线程安全对比的是StringBuilder,后者是线程不安全,速度会快一些,
所以在使用的时候应该优先使用StringBuilder,因为他支持所有相同的操作。
1.2StringBuffer类的构造与常见方法
//构造方法
public StringBuffer()//无参构造,初始容量为16个字符
public StringBuffer(int capacity)//指定容量的字符串缓冲区对象
public StringBuffer(String str)//指定字符串内容的字符串缓冲区对象,此时缓冲区容量大小为初始容量+str.length()
//常用方法
public int capacity()//返回缓冲区容量
public int length()//返回字符长度(字符数)
每个字符串缓冲区都有一定的容量,只要字符串缓冲区所包含的字符序列的长度没有超过此容量,
则无需分配新的内部缓冲区数组,如果内部缓冲区溢出,则此容量自动增大。
1.3StringBuffer类的添加功能
public StringBuffer append(String str)//可以将任意类型数据添加到字符串缓冲区中,并返回字符串缓冲区本身
public StringBuffer insert(int offset,String str)//在指定位置把任意类型的数据插入到字符串缓冲区中,并返回字符串缓冲区本身
这里我们用一段程序测试一下StringBuffer类如何进行改变,
public class StringBufferTest {
public static void main(String []args){
StringBuffer sb=new StringBuffer();
StringBuffer sb1=sb.append(3837);
StringBuffer sb2=sb.append("java");
StringBuffer sb3=sb.append(false);
System.out.println(sb);
System.out.println(sb1);
System.out.println(sb2);
System.out.println(sb3);
}
}
然后我们看输出结果,
可以看出来sb、sb1、sb2、sb3都是指向同一个内存空间,他们记录的地址是相同的,
这和我们之前的String类不同,String对象在创建后值就不允许被改变,而StringBuffer则可以通过append的方法对其值进行修改。
1.4StringBuffer类的删除功能
public StringBuffer deleteCharAt(int intdex)//删除指定位置的字符,并返回本身
public StringBuffer delete(int start,int end)//删除从指定位置开始到指定位置结束的内容(包括start位置,不包括end位置),并返回本身
1.5StringBuffer类的截取功能
public String substring(int start)//从指定位置截取到末尾,返回值为String
public String substring(int start,int end)//从开始位置截取到结束位置(包括开始位置,不包括结束位置),返回值为String
1.6StringBuffer和String的相互转换
- String转换为StringBuffer
- 通过构造方法
- 通过append()方法
//构造方法
StringBuffer sb1=new StringBuffer("java");
//append方法
StringBuffer sb2=new StringBuffer();
sb2.append("java");
- StringBuffer转换为String
- 通过构造方法
- 通过toString()方法
- 通过subString(0,length)方法
StringBuffer sb=new StringBuffer("java");
//构造方法
String s1=new String(sb);
//toString()方法
String s2=sb.toString();
//subString(0,length)方法
String s3=sb.subString(0,sb.length());
1.7数组转成字符串与字符串反转练习
1、数组转成字符串练习
之前我们用String的加法完成了该功能,现在我们使用StringBuffer类的功能实现,
public class exchangeString {
public static void main(String []args){
//把数组中的数据按照指定格式拼接为字符串
//例:int[] arr={1,2,3},输出结果为"[1,2,3]"
int[] arr={1,2,3};
System.out.println(arrayToString(arr));
}
public static String arrayToString(int[] arr){
StringBuffer sb=new StringBuffer();
sb.append("[");
for(int i=0;i<arr.length;i++){
if (i==arr.length-1){
sb.append(arr[i]).append("]");
}else {
sb.append(arr[i]).append(",");
}
}
return sb.toString();
}
}
2、字符串反转练习
Scanner sc=new Scanner(System.in);
String str=sc.nextLine();
StringBuffer sb=new StringBuffer(str);
sb.reverse();
System.out.println(sb.toString());
这里我们直接将String转换为StringBuffer然后调用reverse方法进行反转就可以了,
最后通过toString()输出字符串即可。
1.8StringBuffer和StringBuilder的区别
- StringBuffer是jdk1.0版本的,是线程安全的,效率低,而StringBuilder是jdk1.5版本的,是线程不安全的,效率高
- String是不可变的字符序列,StringBuffer和StringBuilder是可变的字符序列
1.9String和StringBuffer分别作为参数传递
参数传递改变值问题是一个非常经典的问题,
基本数据类型值传递,不会改变值,但是引用数据类型值传递,会改变值,
那么String和StringBuffer同样都是引用数据类型,是不是在作为参数传递时,值都会发生改变呢?
这里我们先测试一下String作为参数传递,
public static void main(String []args){
String s="java";
System.out.println(s);
change(s);
System.out.println(s);
}
public static void change(String s){
s+="script";
}
结果如下,
可以看到String作为参数进行值传递的时候,值没有发生改变,
原来String虽然是引用数据类型,但是它作为参数传递时和基本数据类型是一样的,值都不会发生改变,
我们再测试一下StringBuffer作为参数传递看看值有没有发生改变,
public static void main(String []args){
StringBuffer s=new StringBuffer("java");
System.out.println(s);
change(s);
System.out.println(s);
}
public static void change(StringBuffer sb){
sb.append("script");
}
我们看看结果,
结果来看值发生了改变,看来StringBuffer作为参数传递时,值是会发生改变的。
二、数组排序与查找
2.1冒泡排序
冒泡排序是一种比较经典的排序算法,
该算法得名于其排序策略,与小的元素会慢慢“浮到”数列的顶端,如同水中的气泡一样,
其基本思路为两个相邻位置作比较(假设要求升序排列),那么如果前面的元素比后面的大,那么就交换位置,这样我们就能确保在第一轮排序的时候最大的数落到了数组的最后一个位置,然后第二轮同样让第二大的数落在倒数第二个位置,
以此类推,如果数组元素为n,我们一共需要进行n-1轮,因为最后排2个元素的时候,只需要确定一个元素的位置另一个元素的位置也确定了,接下来我们用java进行实现,
public static void main(String[] args){
int[] arr={5,7,1,3,9,8,2,6,4,0};
BubbleSort(arr);
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
}
public static void BubbleSort(int arr[]){
for(int i=0;i<arr.length-1;i++){
for(int j=0;j<arr.length-1-i;j++){
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
下面是结果,
2.2选择排序
选择排序就更简单了,直接挨个位置确定应该存放的元素,比如我们要求升序排列,
那么第一个位置就应该存放最小的元素,我们会用第一个位置上的元素与其他所有位置行的元素挨个比较,如果有比第一个位置上元素小的,就立马交换,这样一轮下来我们就可以保证第一个位置上的元素一定是最小的,然后进行下一轮确定第二个位置上的元素,一共也是进行n-1轮,
接下来我们用代码实现,
public static void main(String[] args){
int[] arr={5,7,1,3,9,8,2,6,4,0};
SelectSort(arr);
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
}
public static void SelectSort(int arr[]){
for(int i=0;i<arr.length-1;i++){
for(int j=i+1;j<arr.length;j++){
if(arr[i]>arr[j]){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
}
}
然后是结果,
选择排序和冒泡排序的时间复杂度都是,选择哪一个都行。
2.3数组的二分查找
二分查找也叫折半查找,在使用二分查找时有一个大前提,就数组元素为有序排列的,
假设有一个有序排列的数组a,元素个数为n,现在我们要查找数组中是否存在元素x,
二分查找的基本思想是将n个元素分成大致相等的两部分,取元素a[n/2]与x进行比较,比较会有三种情况:
- 如果a[n/2]=x,则查找成功
- 如果a[n/2]<x,则在a[0]到a[n/2-1]这n/2个元素中再取中间的a[n/4]进行比较
- 如果a[n/2]>x,则在a[n/2+1]到a[n]这n/2个元素中再取中间的a[3n/4]进行比较
以此类推,我们就可以不断找缩小查找范围,最后若数组大小为1时仍未找到则该数组中不存在该元素,
接下来我们用代码实现,
public static void main(String[] args){
int[] arr={1,3,9,18,27,65,88,100};
int index=BinarySearch(arr,8);
System.out.print(index);
}
public static int BinarySearch(int arr[],int x){
int low=0;
int high=arr.length-1;
while(low<=high){
int middle=(high+low)/2;
if(x==arr[middle]){
return middle;
}else if(x<arr[middle]){
high=middle-1;
}else{
low=middle+1;
}
}
return -1;
}
如果是未找到就会返回-1,找到了则会返回该元素在数组的下标,
三、Arrays类
3.1Arrays类概述
Arrays类是针对数组及逆行操作的工具类,该类包含用于操作数组的各种方法,
类中所有方法都是用static修饰的,我们可以直接使用类名加方法名调用,不用创建对象,
我们上面自己实现的查找和排序功能在该类中均有写好的方法可以直接使用,
如果指定的数组引用为空,则该类中的方法都抛出一个NullPointerException。
3.2Arrays类常用方法
public static String toString(int []a)//将数组元素转为字符串输出,例"[a,b,c]"
public static void sort(int []a)//将数组按照升序规则进行排序,这里使用的是快排方法
public static int binarySearch(int []a,int key)//利用二分查找在数组a中查找元素key的位置
四、基本类型包装类
4.1基本类型包装类概述
基本类型包装类就是将基本数据类型封装到对象中,
其好处在于可以在对象中定义更多的功能方法操作该数据,
常用的操作比如将基本数据类型与字符串之间的转换,java中已经给我们做了一些包装类,对应关系如下:
基本数据类型 | 基本类型包装类 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
这些基本类型包装类里面的方法都是静态的,直接可以通过类名+方法名调用里面的方法,使用起来比较方便。
4.2Integer类的概述及构造方法
Integer类在对象中包装了一个基本类型int的值,
该类提供了多个方法,能在int类型和String类型之间互相转换,
而且还提供了处理int类型时非常有用的其他一些常量(比如整型数据的最大最小值用MAX_VALUE和MIN_VALUE表示)和方法,
其主要构造方法如下:
public Integer(int value)//用int类型数据进行构造
public Integer(String s)//使用String数据类型进行构造
在使用String数据类型进行构造时,字符串s要为数字字符串,并且数字要是整型的,否则程序会报错,
4.3int类型与String类型的相互转换
- int转String
- int类型数据和""进行拼接
- public static String valueOf(int i),String类的静态方法
- 将int先转为Integer类,然后使用Integer类的toString()方法
- public static String toString(int i),Integer类的静态方法
- String转int
- 先将String转为Interger,然后hi用Integer的intValue方法转为int类型
- public static int parsesInt(String s),Integer类的静态方法
基本数据类型包装类有八种,其中七种都有paeseXxx的方法,可以将这七种的字符串表现形式转换为基本数据类型,
注:char的包装类Character中没有parseXxx方法,可以通过toCharArray()方法转换。
4.4自动装箱和拆箱
自动装箱:将基本类型转换为包装类类型
自动拆箱:将包装类类型转换为基本类型
int x=100;
Integer i1=new Integer(x);//手动装箱
int y=i1.intValue();//手动拆箱
Integer i2=100;//自动装箱
int z=i2+100;//自动拆箱
注意,在使用时,如果Integer x = null,则代码会出现空指针异常,
应该先判断是否为null再使用Integer包装类。
4.5Integer的面试题
public class IntegerPractice {
public static void main(String[] args){
Integer i1=new Integer(127);
Integer i2=new Integer(127);
System.out.println(i1==i2);
System.out.println(i1.equals(i2));
Integer i3=new Integer(128);
Integer i4=new Integer(128);
System.out.println(i3==i4);
System.out.println(i3.equals(i4));
Integer i5=127;
Integer i6=127;
System.out.println(i5==i6);
System.out.println(i5.equals(i6));
Integer i7=128;
Integer i8=128;
System.out.println(i7==i8);
System.out.println(i7.equals(i8));
}
}
我们看一下这个程序的输出,
可以看到在使用new方法构造Integer类对象时,值相同时他们的地址是不同的,
所以使用==号比较结果为false,使用equals方法结果为true,
而在使用自动装箱方法创建Integer对象时,会判断赋的值是否位于byte的取值范围内[-128,127],
如果在这个取值范围内,自动装箱就不会创建新对象,就会在常量池中直接获取,这也是为什么赋值为127时用==号结果为true,因为i5和i6都指向常量池中的同一个地址,
如果不在这个取值范围内,自动装箱就会创建新对象,无法从常量池中获取,所以i7和i8地址不同。