二—10:字符串相关的类(String类)

一、String类

1、String介绍

String类是代表字符串的类。字符串是常量,创建后值不可更改。因为字符串是常量,所以可以被共享。

  • String类实现了序列化接口Serializable 支持序列化和反序列化开发。
  • String类实现了比较器接口Comparable 支持自然比较。
  • String类实现了字符序列化接口CharSequence 底层采用字符序列存储数据。
  • String是一个final类,代表不可以有子类;而底层采取final char[] 存储字符串。由此可以推断出String类代表的是一个不可变字符序列。

2、String类的基本使用

2.1 String类构造方法

  • String() 创建一个空字符串。

  • String(byte[] bytes) 将byte数组按照平台默认编码转为字符串。

  • String(byte[] bytes, String charsetName) 将byte数组按照指定编码转为字符串。

  • String(byte[] bytes, int offset, int length) 将byte数组的部分内容按照平台默认编码转为字符串;需要设置开始下标,长度;

  • String(byte[] bytes, int offset, int length, String charsetName) 将byte数组的部分内容按照指定编码转为字符串;需要设置开始下标,长度;

  • String(char[] value) 将char数组转为字符串。

  • String(char[] value, int offset, int count) 将char数组的部分内容转为字符串;需要指定开始下标,长度;

代码演示:

/**
 *  String字符串对象的创建演示
 */
public class TestString {
    public static void main(String[] args) throws UnsupportedEncodingException {
        /**
         * 1) 直接赋值字符串字面量
         *      String是类名, 其实就是一个数据类型,   s1变量名,也称为对象名, 双引号引起来的"hello"才是真正的String对象,
         *      s1变量保存的是"hello"对象的引用
         */
        String s1 = "hello";
        System.out.println("s1=" + s1);
        /**
         * 2) String (byte [] bytes ) 根据字节数组创建String对象
         */ 67, 68, 69
        byte[] bytes = {65, 66,, 70, 71, 72, 73};//手动定义字节数组
        //把bytes字节数组中所有字节, 以当前默认的字符编码UTF-8解析为字符串
        String s2 = new String(bytes);
        //ABCDEFGHI
        System.out.println("s2=" + s2);
        /**
         * 把bytes数组中从0开始的6个字节解析为字符串
         */
        s2 = new String(bytes, 0, 6);
        //ABCDEF
        System.out.println("s2=" + s2);
        /**
         *      指定字符编码
         *      计算机中, 不管是CPU, 内存, 还是硬盘上,所有的数据都是01二进制形式的, 8个01二进制位组成1个byte字节.
         *
         *      记事本, Editplus, IDEA文本编辑器中打开文本文件, 显示字符数据, 也可以在文本文件中直接输入字符保存到文件中.
         *    这就涉及一个字符与01二进制之间的一个转换问题, 在字符与01序列之间进行转换需要使用字符编码
         *
         *      字符编码就是字符与一组01序列之间的映射, 这一组01序列称为码值, 8个01二进制位组成1个字节.
         *    把字符转换为01序列(码值)过程称为编码,  把一组01序列(码值)转换为字符称为解码,使用文本编辑器打开文本文件时,
         *    使用默认的字符编码把文件中的01序列解析为字符显示出来; 在文本编辑器输入文本后, 使用默认的字符编码把这些文本编码为
         *    01序列(码值)保存到文件中,将来可能会从文件中读取一组01序列,或者从网络中读取一组01序列, 可以把这一组01序列
         *    转换为字符串, 就调用String(byte [] bytes)构造方法,利用字符编码可以巧妙的解决乱码问题
         *
         *     //把bytes数组中所有的字节, 以指定的GB2312编码解析为字符串, 调用String(byte [] bytes, String charsetName)构造方法,
         *     会显示语法错误:Unhandled exception: java.io.UnsupportedEncodingException未处理的异常,
         *     即这个构造方法有检查异常需要预处理, 当前Alt + Enter, 选择 Add exception to method signature抛出处理,
         *     当程序支行后, 如果指定的字符编码Java不支持就会抛出异常
         */
        s2 = new String("犯我中华者虽远必诛");
        byte[] bs = s2.getBytes("UTF-8");//编码
        s2 = new String(bs, "UTF-8");//解码
        System.out.println("s2=" + s2);

        /**
         * 3) String(char [] chars) 把字符数组中的字符连接为字符串
         */
        char[] chars = {'a', 'b', '5', '汉', '字', 97};
        //char字符类型的变量实际存储的是字符的码值, 给cc变量分配2个字节存储空间, 在这存储空间中存储的是'a'字符的码值97,
        //对应的01序列为: 00000000 01100001
        char cc = 'a';
        //把chars数组中所有的字符连接为字符串
        String s3 = new String(chars);
        //ab5汉字a
        System.out.println(s3);
        //把chars数组中从0开始的5个字符连接为字符串
        s3 = new String(chars, 0, 5);
        System.out.println(s3);

        /**
         *  4) String()
         *  new运算符在堆中创建对象, 把对象的引用赋值给数组名
         */
        String s4 = new String();
        //false
        System.out.println(s4 == null);
//s4未赋值,地址中没有存东西,为空白。

        /**
         * 5) String( String )
         */
        String s5 = new String(s1);
        //hello
        System.out.println(s5);
        //false
        System.out.println(s1 == s5);
    }
}

2.2 从底层来看String对象的创建

3、 String类的内存分布

3.1、字符串常量的存储位置 

1.String str1="abc";      String str2=new String("abc");

  • JDK1.7之前,字符串常量池放在方法区中,这时的方法区也叫做永久代;
  • JDK1.7时,常量池是放在堆空间中的;静态变量(静态域)也放在堆空间中。
  • 此时常量池所在的堆空间的这个位置也叫做永久代。
  • JDK1.8,常量池还在堆内存中,只不过常量池所在的堆空间的这个位置不叫做永久代了,而是叫做元空间.
  • 总结:从JDK1.7开始,字符串常量池就放在堆中

1.为什么要把字符串常量池所在的方法区改为堆空间呢?

垃圾回收(GC):栈中没有垃圾回收的概念,堆中是有的,而且堆空间是我们频繁需要GC的一个区域。 但方法区很少GC,因为方法区中,主要加载的是类的一些信息。这些类的信息回收没有什么用。你也回收不掉。所以方法区很少被回收。但是字符串常量池空间是需要回收的,所以把字符串常量池放在不被回收的方法区中,那么方法区就会越来越大。所以我们就把方法区字符串常量池移出来放在了堆空间中。

2.JDK8为什么把方法区叫做叫元空间呢?

主要原因就是,从JDK8开始使用的是我们本地的物理内存。因为方法区的回收效果不好,那干脆直接使用本地的物理内存。这个本地的物理内存就是元空间。

3.2、 字符串字面量与字符串对象的区别

1、掌握以下3个结论:

  • 常量 + 常量:  结果仍然存储在字符串常量池中,返回此字面量的地址。注:此时的常量可能是字面量,也可能是final修饰的常量
  • 常量 + 变量  或  变量 + 变量 :都会通过new的方式创建一个新的字符串,返回堆空间中此字符串对象的地址
  • 调用字符串的intern():  返回的是字符串常量池中字面量的地址。
  • concat(xxx):    不管是常量调用此方法,还是变量调用,同样不管参数是常量还是变量,总之,调用完concat()方法都返回一个新new的对象。

总结:

  • 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
  • 常量与字符串变量拼接会在堆内存创建对象
  • 如果拼接的结果调用intern()方法,返回值就在常量池中
  • 调用完concat()方法都返回一个新new的对象。

3.3、 不可变性(地址的不可变)

1.当对字符变量重新赋值时,需要重新指定一个字符串常量的位置进行赋值,不能在原有的位置修改。

2.当对现有的字符串进行拼接操作时,需要重新开辟空间保存拼接以后的字符串,不能在原有的位置修改。使用加号+对字符串连接会生成新的字符串对象, 频繁字符串连接,不建议使用String。

  String s1 = "hello";
    String s2 = "hello";
    s2 += "world";
    System.out.println(s1); //hello
    System.out.println(s2);//helloworld

 3.当对现有的字符串进行替换操作时,需要重新开辟空间保存替换以后的字符串,不能在原有的位置修改。

    String s1 = "hello";
    String s2 = "hello";
    String s3 = s2.replace('l', 'w');
    System.out.println(s1);//hello
    System.out.println(s2);//hello
    System.out.println(s3);//hewwo

不可变的本质 :

String底层是字符数组常量,因此String类的所有方法都不会改变源字符串,而是返回一个新的字符串。

因为String底层使用字符数组保存字符串的每个字符, 该字符数组使用final修饰无法重新赋值(地址的改变),  String类也没有提供相应的方法修改value数组的元素, 并且String类还是final修饰的,无法定义子类.

4、String类下常用方法

4.1、判断类

  • boolean contains(CharSequence s)   判断当前字符串中是否包含s。

  • boolean endsWith(String suffix)    判断当前字符串是否以suffix结尾。

  • boolean startsWith(String prefix)  判断当前字符串是否以prefix开始。

/**
    boolean contains(CharSequence s)   判断当前字符串中是否包含s
    boolean startsWith(String prefix)  判断当前字符串是否以prefix开始
    boolean endsWith(String suffix)    判断当前字符串是否以suffix结尾
  */
String s = "hello中国";
System.out.println( s.contains("hello"));//true
System.out.println( s.startsWith("hello"));//true
System.out.println( s.endsWith("hello"));//false

4.2、获取类

  1. 返回字符串长度

 int length();

String s = "A small step forward,A big step of civilization";
 System.out.println(s.length());

2.将指定的字符串连接到该字符串的末尾。

String concat(String str)

       String s1="Hello";
       String s2="World";
       String s=s1.concat(s2);
       System.out.println(s);//HelloWorld

3.返回索引处的char值

char chatAt(int index)

  4.int intdexOf(String str)//返回指定字符串第一次出现在该字符串的索引

 5.String substring(int beginIndex)//返回一个子字符串,从beginIndex开始截取字符串到字符串结尾。

 6. String substring(int beginIndex,int endIndex)//返回一个子字符串,从beginIndex开始截取字符串,含beginIndex,不含endIndeStri

7.  int lastIndexOf(String str)//返回str在当前字符串中最后一次出现的索引值。

String path = "D:\\course\\03-JavaSE\\Day04\\Code\\src\\string\\Test01.java";
//把文件名Test01.java取出来, 需要确定反斜杠最后一次出现的索引值.
int slash = path.lastIndexOf("\\");
String filename = path.substring(slash + 1);
System.out.println(filename);

 8. String trim() 去掉字符串前后的空白符,返回新的字符串,原来字符串不变。

str = "    Hello    world     ";
String trim = str.trim();
System.out.println("--" + trim + "--");//--Hello    world--
System.out.println("--" + str + "--"); //--    Hello    world     --

 4.3、转换类

1. String与基本数据类型、包装类转换:

 2.String与字符数组转换:

3.String与字节数组转换:

4.4.分割类的

 4.5、比较类的

  • int compareTo(String anotherString)//直接比较字符串的大小。
  • compareToIgnoreCase(String str) //忽略大小后再比较字符串的大小。
public class TestString01 {
    public static void main(String[] args) {
        System.out.println("hello".compareTo("hehe"));//4, 正数表示前面字符串大
        System.out.println("hello".compareTo("helloworld"));//-5 ,负数表示第二个字符串大
        System.out.println("hello".compareTo("z"));//-18, 负数表示第二个字符串大
        /**
         * int compareTo(String anotherString)
         比较当前字符串与参数字符串大小, 如果当前字符串大返回正数,参数字符串大返回负数
         int compareToIgnoreCase(String str)  忽略大小写再判断字符串大小
         String类实现了Comparable接口, 重写了compareTo()方法, 在该方法中定义了字符串的比较规则:
         1)逐个比较两个字符串的每个字符, 遇到第一个不相等的字符码值相减;
         2)如果比较过的字符串都一样,再比较两个字符串的长度. 如果compareTo()方法返回正数表示前面第一个字符串大,
         3)返回0表示两个字符串相等, 返回负数表示参数第二个字符串大
         */
        System.out.println("hello".compareTo("Hello"));//32, 区分大小写
        /**
         *  如果不区分大小写比较,调用compareToIgnoreCase()
         */
        System.out.println("hello".compareToIgnoreCase("HEllo")); //0
        String str = "hello";
        System.out.println("hello".compareTo(str));//0
        str = null;
        //NullPointerException, 在调用compareTo()比较两个字符串大小时, 参数字符串不允许为null, 否则会产生空指针异常
        System.out.println("hello".compareTo(str));
    }
}

4.6  正则表达式类

正则表达式就是一个模式串, 验证字符串是否匹配指定的模式。

正则表达式的终极定义: 

正则表达式(英语:Regular Expression,在代码中常简写为regex)。正则表达式是一个字符串,使用单个字符串来描述、用来定义匹配规则,匹配一系列符合某个句法规则的字符串。在开发中,正则表达式通常被用来检索、替换那些符合某个规则的文本。

 1.String类中与正则表达式相关的方法

boolean matches(String regex)

判断字符串是否匹配指定的regex正则表达式。

String replaceAll(String regex, String replacement)

把当前字符串中符合Regex正则表达式的字符串使用replacement替换,返回替换后的字符串。

String[] split(String regex)

把当前字符串使用符合regex正则表达式的字符串拆分 ,返回拆分 后的字符串组成 的数组。

 2.正则表达式的匹配规则
  •  字符类匹配规则

  •  边界匹配器

  •  数量词

 5、String算法

1.模拟一个trim方法,去除字符串两端的空格。

public String myTrim(String str) {
    if (str != null) {
        int start = 0;// 用于记录从前往后首次索引位置不是空格的位置的索引
        int end = str.length() - 1;// 用于记录从后往前首次索引位置不是空格的位置的索引

        while (start < end && str.charAt(start) == ' ') {
            start++;
        }

        while (start < end && str.charAt(end) == ' ') {
            end--;
        }
        if (str.charAt(start) == ' ') {
            return "";
        }

        return str.substring(start, end + 1);
    }
    return null;
}

@Test
public void testMyTrim() {
    String str = "   a   ";
    // str = " ";
    String newStr = myTrim(str);
    System.out.println("---" + newStr + "---");
}

2.将一个字符串进行反转。将字符串中指定部分进行反转。

/ 方式一:
public String reverse1(String str, int start, int end) {// start:2,end:5
    if (str != null) {
        // 1.
        char[] charArray = str.toCharArray();
        // 2.
        for (int i = start, j = end; i < j; i++, j--) {
            char temp = charArray[i];
            charArray[i] = charArray[j];
            charArray[j] = temp;
        }
        // 3.
        return new String(charArray);

    }
    return null;

}

// 方式二:
public String reverse2(String str, int start, int end) {
    // 1.
    String newStr = str.substring(0, start);// ab
    // 2.
    for (int i = end; i >= start; i--) {
        newStr += str.charAt(i);
    } // abfedc
    // 3.
    newStr += str.substring(end + 1);
    return newStr;
}

// 方式三:推荐 (相较于方式二做的改进)
public String reverse3(String str, int start, int end) {// ArrayList list = new ArrayList(80);
    // 1.
    StringBuilder s = new StringBuilder(str.length());
    // 2.
    s.append(str.substring(0, start));// ab
    // 3.
    for (int i = end; i >= start; i--) {
        s.append(str.charAt(i));
    }

    // 4.
    s.append(str.substring(end + 1));

    // 5.
    return s.toString();

}

@Test
public void testReverse() {
    String str = "abcdefg";
    String str1 = reverse3(str, 2, 5);
    System.out.println(str1);// abfedcg

}

3.获取一个字符串在另一个字符串中出现的次数。

public int getCount(String mainStr, String subStr) {
    if (mainStr.length() >= subStr.length()) {
        int count = 0;
        int index = 0;
        // while((index = mainStr.indexOf(subStr)) != -1){
        //      count++;
        //      mainStr = mainStr.substring(index + subStr.length());
        // }
        // 改进:
        while ((index = mainStr.indexOf(subStr, index)) != -1) {
            index += subStr.length();
            count++;
        }

        return count;
    } else {
        return 0;
    }
}

@Test
public void testGetCount() {
    String str1 = "cdabkkcadkabkebfkabkskab";
    String str2 = "ab";
    int count = getCount(str1, str2);
    System.out.println(count);
}

4.获取两个字符串中最大相同子串。

 // 如果只存在一个最大长度的相同子串
    public String getMaxSameSubString(String str1, String str2) {
        if (str1 != null && str2 != null) {
            String maxStr = (str1.length() > str2.length()) ? str1 : str2;
            String minStr = (str1.length() > str2.length()) ? str2 : str1;

            int len = minStr.length();

            for (int i = 0; i < len; i++) {// 0 1 2 3 4 此层循环决定要去几个字符

                for (int x = 0, y = len - i; y <= len; x++, y++) {

                    if (maxStr.contains(minStr.substring(x, y))) {

                        return minStr.substring(x, y);
                    }

                }

            }
        }
        return null;
    }

    // 如果存在多个长度相同的最大相同子串
    // 此时先返回String[],后面可以用集合中的ArrayList替换,较方便
    public String[] getMaxSameSubString1(String str1, String str2) {
        if (str1 != null && str2 != null) {
            StringBuffer sBuffer = new StringBuffer();
            //List<String> list = new ArrayList<String>();
            String maxString = (str1.length() > str2.length()) ? str1 : str2;
            String minString = (str1.length() > str2.length()) ? str2 : str1;

            int len = minString.length();
            for (int i = 0; i < len; i++) {
                for (int x = 0, y = len - i; y <= len; x++, y++) {
                    String subString = minString.substring(x, y);
                    if (maxString.contains(subString)) {
                        sBuffer.append(subString + ",");
                           //list.add(subString);
                    }
                }
                System.out.println(sBuffer);
                if (sBuffer.length() != 0) {
                    break;
                }
            }
            String[] split = sBuffer.toString().replaceAll(",$", "").split("\\,");
            return split;
        }

        return null;
    }
  @Test
    public void testGetMaxSameSubString() {
        String str1 = "abcwerthelloyuiodef";
        String str2 = "cvhellobnmiodef";
        String[] strs = getMaxSameSubString1(str1, str2);
        System.out.println(Arrays.toString(strs));
    }

5.对字符串中字符进行自然顺序排序

@Test
public void testSort() {
    String str = "abcwerthelloyuiodef";
    char[] arr = str.toCharArray();
    Arrays.sort(arr);

    String newStr = new String(arr);
    System.out.println(newStr);
}

二、可变字符串类

1、StringBuffer

可变的字符序列;线程安全的,效率相对String高很多。

1. 1 常用构造方法

StringBuffer()

构造一个没有字符的字符串缓冲区,并构造了16个字符的初始容量。

StringBuffer(int capacity)

构造一个没有字符的字符串缓冲区,并构造指定的初始容量。

1.2 常用方法

  • append()连接
  • delete()删除
  • insert()插入
  • replace()替换
  • reverse()逆序
public class TestStringBuffer {
    @Test
    public void StringBufferMethodTest(){
        //创建实例
        StringBuffer sb = new StringBuffer();
        //拼接字符串
        sb.append(1);
        sb.append(true);
        sb.append("hello");
        System.out.println(sb);
        //从0下标删除到5下标,包含开始下标,不包含结束下标
        sb.delete(1,5);
        System.out.println(sb);
        //替换内容,包含开始,不包含结束下标
        sb.replace(1,5,"aaa");
        System.out.println(sb);
        //插入指定内容
        sb.insert(1,"hello");
        System.out.println(sb);
        //字符串反转
        sb.reverse();
        System.out.println(sb);
        //截取字符串,返回新的字符串
        String result = sb.substring(0, 3);
        System.out.println(result);
        //将StringBuffer转为String类型
        System.out.println(sb.toString());
    }
}

总结:StringBuffer类的大部分方法都是操作原对象,改变原对象。

 2、StringBuilder类

  • 是可变字符串, 即它的字符序列可以修改。
  • StringBuilder不是线程安全的.但是StringBuilder执行效率高。

 2.1 常用方法

  • append()连接
  • delete()删除
  • insert()插入
  • replace()替换
  • reverse()逆序

代码:

public class TestStringBuilder {
    public static void main(String[] args) {
        //创建StringBuilder对象
        StringBuilder sb = new StringBuilder();

        //append()连接字符串
        for (int i = 0; i < 10; i++) {
            sb.append(i);
        }
        //0123456789
        System.out.println(sb);

        //delete()删除
        sb.delete(3, 7);
        //012789
        System.out.println(sb);

        //insert()插入
        sb.insert(3, "Hello");
        //012Hello789
        System.out.println(sb);

        //replace()替换
        sb.replace(3, 8, "World");
        //012World789
        System.out.println(sb);

        //逆序
        sb.reverse();
        //987dlroW210
        System.out.println(sb);
    }
}

 三、String、StringBuffer、StringBuilder三者的区别

1、相同点

  1. String、StringBuffer、StringBuilder 三者都实现了字符序列接口,都满足了字符序列规范
  2. 都被final修饰的类,禁止这三个类的方法被重写。

2、不同点

  1. String 底层采用的是final char[] 进行存储字符串,是不可变的字符序列,每次拼接字符串都会开辟新的内存空间。拼接字符串效率非常低下。
  2. StringBuffer 底层采用char[] 进行存储字符串,数组的长度默认16。是可变的字符序列,StringBuffer对象会根据使用情况扩充字符缓冲(原来的2倍+2这种算法扩充)。拼接字符串效率较String来说提高很多。方法都是被synchronized修饰的,都是线程安全的。

  3. StringBuilder和StringBuffer功能一样,只是底层方法没有被synchronized修饰,是线程不安全的,执行效率高于StringBuffer ;

  4. 所以开发一般对字符串的增加、删除、插入操作一般用StringBuffer效率更高,其他操作用String更为便。

四、排序接口Comparable

案例:

公司类:

4、需求如下:
/**
 * 定义Employee员工对象 , 有姓名, 年龄, 工资等属性
 * <p>
 * 定义Company公司类, 公司中有若干员工, 使用数组保存员工信息
 * -提供方法,添加员工
 * -提供方法,显示所有员工信息
 * -提供方法, 判断是否存在指定姓名的员工
 * -提供方法,删除指定姓名的员工
 * <p>
 * 定义测试类
 * -创建公司对象
 * -添加10个员工
 * -显示员工信息
 * -判断是否存在lisi
 * -删除wangwu
 */
public class Company {
    //1) 公司有若干员工, 使用数组保存员工信息
    //在定义A类时用到了B类, 说明A类与B类有一定关系, 类之间的关系 有四种: 继承, 实现, 关联,依赖. 在A类中定义变量使用到B类,称A类关联B类;  在A类中定义方法使用到B类称A依赖B类
    private Employee[] data;
    //通常情况下使用对象数组时, 会定义变量记录数组中元素的数量
    private int size;
    //通常情况下,会在无参构造方法中给数组进行初始化
    public Company() {
        data = new Employee[100];   //数组初始化大小需要估算
    }
    //有时, 也会通过构造方法指定数组的大小
    public Company(int capacity) {
        if (capacity > 0) {
            data = new Employee[capacity];
        } else {
            data = new Employee[0];
        }
    }
    //2) 提供方法,添加员工, 通过参数来接收一个员工对象
    public void add(Employee e) {
        //通常情况下, 向数组中存储数据前,会判断数组是否有足够容量
        if (size == data.length) {
            //size数组中元素的数量 等于 数组的长度, 表示数组已满 , 需要对数组扩容
            Employee[] bigger = new Employee[data.length * 2];
            System.arraycopy(data, 0, bigger, 0, data.length);
            data = bigger;
        }
        //把e接收的员工添加到data数组中
        data[size++] = e;
    }
    //3) 提供方法显示员工信息
    public void showAll() {
        System.out.println("----------所有员工信息-----------");
        for (int i = 0; i < size; i++) {
            System.out.println(data[i]);
        }
    }
    //4) 提供方法, 判断是否存在指定姓名的员工
    public boolean containsName(String name) {
        //在实际开发中, 当形参是引用对象, 在方法体中访问形参对象的属性,或者调用形参对象的方法前,通常会判断形参对象是否为null, 为了避免出现空指针异常
        if (name != null) {
            //遍历data数组中前size个员工对象,
            for (int i = 0; i < size; i++) {
//            System.out.println( data[i] + " ------------" + data[i].getName());
                if (name.equals(data[i].getName())) {
                    return true;
                }
            }
        } else {
            //遍历前size个数组元素,判断是否存在某个员工的姓名也为null
            for (int i = 0; i < size; i++) {
                if (data[i].getName() == null) {
                    return true;
                }
            }

        }
        return false;
    }
    //5)提供方法删除指定姓名的员工
    public void deleteByName(String name) {
        //找到name姓名的员工在数组中的索引值
        if (name != null) {
            for (int i = 0; i < size; i++) {
                if (name.equals(data[i].getName())) {
                    delete(i);
                    i--;//当有重复性元素时需使用
                }
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (data[i].getName() == null) {
                    delete(i);
                    i--;   //修正,当有重复性元素时需使用
                }
            }
        }
    }
    //定义方法删除指定索引值的元素
    private void delete(int i) {
        //从i+1开始 每个元素逐个前移
        System.arraycopy(data, i + 1, data, i, size - i - 1);
        //元素个数减1
        size--;
        //最后元素清空
        data[size] = null;
    }
    //定义方法,根据员工的姓名升序排序
    public void sortByName() {
        //对data数组中存储的前size个员工对象进行排序
        Arrays.sort(data, 0 ,size);//要求data数组中存储的Employee对象自身具有比较大小的功能, 即让Employee类实现Comparable接口
        /*Arrays.sort(data, 0, size, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });*/
    }
    //定义方法,根据工资降序排序
    public void sortBySalaryDesc() {
        //Arrays.sort( 对象数组, from, to, Comparator) 可以对数组中[from, to)范围内的元素进行排序, 排序时,使用指定的第四个Comparator比较器
        Arrays.sort(data, 0, size, new Comparator<Employee>() {
            //在匿名内部类中重写接口抽象方法, 在该方法中定义一个比较规则
            @Override
            public int compare(Employee o1, Employee o2) {
                //o1 > o2 返回正数, o1 == o2返回0, o1 < o2 返回负数,对应升序
                //o1 > o2 返回负数, o1 == o2返回0, o1 < o2 返回正数,对应降序
                if (o1.getSalary() > o2.getSalary()) {
                    return -1;
                } else if (o1.getSalary() == o2.getSalary()) {
                    return 0;
                } else {
                    return 1;
                }
            }
        });
    }
    //定义方法,根据年龄升序排序, 年龄相等,根据工资降序排序
    public void sortByAgeSalary() {
        Arrays.sort(data, 0, size, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                if (o1.getAge() == o2.getAge()) {
                    if (o1.getSalary() > o2.getSalary()) {
                        return -1;
                    } else if (o1.getSalary() == o2.getSalary()) {
                        return 0;
                    } else {
                        return 1;
                    }
                }
                return o1.getAge() - o2.getAge();   //o1的年龄大返回正数, 对应年龄升序
            }
        });
    }
}

员工类:

/**
 * 员工类
 * 按照实体类规范定义,  实体类也称为JavaBean, 就是现实世界中客观存在的物体,如人, 电脑, 书...
 *  1) 把属性私有化
 *  2) 提供无参构造方法
 *  3) 重写equals()/hashCode()
 *  4) 重写toString()
 *  5) 提供getter/setter方法
 */
public class Employee implements Comparable<Employee>{
    private String name;
    private  int age;
    private  double salary;

    //重写接口的抽象方法,在该方法中定义一个比较规则
    @Override
    public int compareTo(Employee o) {
        //this当前对象的姓名比参数o对象的姓名大, 返回正数, 在排序时对应升序
        //比较this当前对象的姓名与 参数o对象的姓名 的大小, 姓名是String字符串类型, 调用compareTo()方法比较大小, String字符串比较大小时, 不允许为null, 否则会产生空指针异常
        if ( this.name == null ){
            return -1;
        }
        if ( o.name == null ){
            return 1;
        }
        return this.name .compareTo( o.name );
    }

    public Employee() {
    }
    //为了自己测试方便,提供有参数的构造方法
    public Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

   @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return Objects.equals(name, employee.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }


    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }
 
    public void setSalary(double salary) {
        this.salary = salary;
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        //创建公司对象
        Company company = new Company();

        //添加员工
        company.add( new Employee("zhangsan", 25, 9000));
        company.add( new Employee("lisi", 26, 3000));
        company.add( new Employee("wangwu", 27, 4000));
        company.add( new Employee("zhaoliu", 28, 5000));
        company.add( new Employee("chenqi", 25, 7000));
        company.add( new Employee("zhuba", 24, 8000));
        company.add( new Employee("feifei", 23, 6000));
        company.add( new Employee("zhaoliu", 22, 9000));

        //来了两位新员工 ,但是员工信息还没有录入
        company.add( new Employee());
        company.add( new Employee());

        //显示所有员工
        company.showAll();

        //判断是否存在指定姓名的员工
         System.out.println( company.containsName("lisi"));
         System.out.println( company.containsName("lixiaosi"));
        //判断是否还有员工没有录入姓名,即判断是否存在姓名 为null的员工
        System.out.println(company.containsName(null));

        //删除
        company.deleteByName("zhaoliu");
        company.showAll();
        company.deleteByName(null);
        company.showAll();

        //根据姓名升序排序
        company.sortByName();
        System.out.println("---");
        /*----------所有员工信息-----------
        Employee{name='chenqi', age=25, salary=7000.0}
        Employee{name='feifei', age=23, salary=6000.0}
        Employee{name='lisi', age=26, salary=3000.0}
        Employee{name='wangwu', age=27, salary=4000.0}
        Employee{name='zhangsan', age=25, salary=9000.0}
        Employee{name='zhuba', age=24, salary=8000.0}*/

        company.showAll();

        company.sortBySalaryDesc();
        company.showAll();

        company.sortByAgeSalary();
        company.showAll();
    }
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值