JAVA-编程基础-09-常用工具类

Lison <dreamlison@163.com>, v1.0.0, 2023.04.16

JAVA-编程基础-09-常用工具类

Arrays工具类

数组专用工具类指的是 java.util.Arrays 类,基本上常见的数组操作,这个类都提供了静态方法可供直接调用

package java.util;
/**
 * @author Josh Bloch
 * @author Neal Gafter
 * @author John Rose
 * @since  1.2
 */
public class Arrays {}

数组操作可分为以下 9 种。”

  • 创建数组
  • 比较数组
  • 数组排序
  • 数组检索
  • 数组转流
  • 打印数组
  • 数组转 List
  • setAll
  • parallelPrefix

创建数组

使用 Arrays 类创建数组可以通过以下三个方法:

  • copyOf,复制指定的数组,截取或用 null 填充
  • copyOfRange,复制指定范围内的数组到一个新的数组
  • fill,对数组进行填充
copyOf
String[] intro = new String[] { "L", "I", "S", "O","N" };
String[] revised = Arrays.copyOf(intro, 3);
String[] expanded = Arrays.copyOf(intro, 6);
System.out.println(Arrays.toString(revised));
System.out.println(Arrays.toString(expanded));

revised 和 expanded 是复制后的新数组,长度分别是 3 和 6,指定的数组长度是 5。来看一下输出结果:

Connected to the target VM, address: '127.0.0.1:63565', transport: 'socket'
[L, I, S]
[L, I, S, O, N, null]

revised 截取了最后一位,因为长度是 3 嘛;expanded 用 null 填充了一位,因为长度是 6

copyOfRange
String[] intro = new String[] { "L", "I", "S", "O","N" };
String[] abridgement = Arrays.copyOfRange(intro, 0, 3);
System.out.println(Arrays.toString(abridgement));

copyOfRange() 方法需要三个参数,第一个是指定的数组,第二个是起始位置(包含),第三个是截止位置(不包含)。

输出结果:

[L, I, S]

截取了从 0 位(包含)到 3 位(不包含)的数组元素

那假如说下标超出了数组的长度,会发生什么呢

String[] abridgementExpanded = Arrays.copyOfRange(intro, 0, 7);
System.out.println(Arrays.toString(abridgementExpanded));

结束位置此时为 7,超出了指定数组的长度 5,来看一下输出结果:

[L, I, S, O, N, null, null]

仍然使用了 null 进行填充。

Arrays 的设计者考虑到了数组越界的问题,不然每次调用 Arrays 类就要先判断很多次长度,很麻烦

fill
String[] stutter = new String[5];
Arrays.fill(stutter, "Lison");
System.out.println(Arrays.toString(stutter));

使用 new 关键字创建了一个长度为 5 的数组,然后使用 fill() 方法将 4 个位置填充为"Lison"

[Lison, Lison, Lison, Lison, Lison]

数组比较

	String[] intro = new String[] { "L", "I", "S", "O","N" }; 	
	boolean result = Arrays.equals(new String[] { "L", "I", "S", "O","N" }, intro);
  System.out.println(result);
  boolean result1 = Arrays.equals(new String[] { "L", "I", "S", "O","S" }, intro);
  System.out.println(result1);

输出结果如下所示:

true
false

指定的数组为LISON五个字,比较的数组一个是LISON,一个是LISOS,所以 result 为 true,result1 为 false

看一下equals()方案的源码

public static boolean equals(Object[] a, Object[] a2) {
    if (a==a2)
        return true;
    if (a==null || a2==null)
        return false;

    int length = a.length;
    if (a2.length != length)
        return false;

    for (int i=0; i<length; i++) {
        if (!Objects.equals(a[i], a2[i]))
            return false;
    }

    return true;
}

因为数组是一个对象,所以先使用“==”操作符进行判断,如果不相等,再判断是否为 null,其中一个为 null,返回 false;紧接着判断 length,不等的话,返回 false;否则的话,依次调用 Objects.equals() 比较相同位置上的元素是否相等。

“这段代码还是非常严谨的,对吧?三妹,这也就是我们学习源码的意义,欣赏的同时,可以学习源码作者清晰的编码思路。”我语重心长地给三妹讲。

除了 equals() 方法,还有另外一个诀窍可以判断两个数组是否相等,尽管可能会出现误差。那就是 Arrays.hashCode() 方法,先来看一下该方法的源码:

public static int hashCode(Object a[]) {
    if (a == null)
        return 0;

    int result = 1;

    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}

哈希算法本身是非常严谨的,所以如果两个数组的哈希值相等,那几乎可以判断两个数组是相等的。

String[] intro = new String[] { "L", "I", "S", "O","N" }; 				
System.out.println(Arrays.hashCode(intro));
System.out.println(Arrays.hashCode(new String[] { "L", "I", "S", "O","N" }));

输出结果:

101073780
101073780

两个数组的哈希值相等,毕竟元素是一样的。但这样确实不够严谨,优先使用 Objects.equals() 方法,当我们想快速确认两个数组是否相等时,可以通过比较 hashCode 来确认——算是投机取巧

数组排序

Arrays 类的 sort() 方法用来对数组进行排序,来看下面这个例子:

String[] intro1 = new String[] { "li", "cun", "520", "1314" };
String[] sorted = Arrays.copyOf(intro1, 4);
Arrays.sort(sorted);
System.out.println(Arrays.toString(sorted));

由于排序会改变原有的数组,所以我们使用了 copyOf() 方法重新复制了一份。来看一下输出结果:

[1314, 520, cun, li]

按照的是首字母的升序进行排列的。基本数据类型是按照双轴快速排序的,引用数据类型是按照 TimSort 排序的,使用了 Peter McIlroy 的“乐观排序和信息理论复杂性”中的技术

数组检索

String[] intro1 = new String[] { "li", "cun", "520", "1314" };
String[] sorted = Arrays.copyOf(intro1, 4);
Arrays.sort(sorted);
int exact = Arrays.binarySearch(sorted, "cun");
System.out.println(exact);
int caseInsensitive = Arrays.binarySearch(sorted, "CuN", String::compareToIgnoreCase);
System.out.println(caseInsensitive);

binarySearch() 方法既可以精确检索,也可以模糊检索,比如说忽略大小写。来看一下输出结果:

2
2

排序后的结果是 [1314, 520, cun, li],所以检索出来的下标是 2

如果要从数组或者集合中查找元素的话,尽量先排序,然后使用二分查找法,这样能提高检索的效率

数组转流

流的英文单词是 Stream,它可以极大提高 Java 程序员的生产力,让程序员写出高效、干净、简洁的代码。 这种风格将要处理的集合看作是一种流,想象一下水流在管道中流过的样子,我们可以在管道中对流进行处理,比如筛选、排序等等

Arrays 类的 stream() 方法可以将数组转换成流:

String[] intro = new String[] { "L", "I", "S", "O","N" };
System.out.println(Arrays.stream(intro).count());

还可以为 stream() 方法指定起始下标和结束下标:

System.out.println(Arrays.stream(intro, 1, 2).count());

如果下标的范围有误的时候,比如说从 2 到 1 结束,则程序会抛出 ArrayIndexOutOfBoundsException 异常:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: origin(2) > fence(1)
	at java.base/java.util.Spliterators.checkFromToBounds(Spliterators.java:387)

打印数组

因为数组是一个对象,直接 System.out.println 的话,结果是这样的:

[Ljava.lang.String;@3d075dc0

最优雅的打印方式是使用 Arrays.toString(),其实前面讲过。来看一下该方法的源码:

public static String toString(Object[] a) {
        if (a == null)
            return "null";

        int iMax = a.length - 1;
        if (iMax == -1)
            return "[]";

        StringBuilder b = new StringBuilder();
        b.append('[');
        for (int i = 0; ; i++) {
            b.append(String.valueOf(a[i]));
            if (i == iMax)
                return b.append(']').toString();
            b.append(", ");
        }
    }
  • 先判断 null,是的话,直接返回“null”字符串;
  • 获取数组的长度,如果数组的长度为 0( 等价于 length - 1 为 -1),返回中括号“[]”,表示数组为空的;
  • 如果数组既不是 null,长度也不为 0,就声明 StringBuilder 对象,然后添加一个数组的开始标记“[”,之后再遍历数组,把每个元素添加进去;其中一个小技巧就是,当遇到末尾元素的时候(i == iMax),不再添加逗号和空格“, ”,而是添加数组的闭合标记“]”。

数组转 List

尽管数组非常强大,但它自身可以操作的工具方法很少,比如说判断数组中是否包含某个值。如果能转成 List 的话,就简便多了,因为 Java 的集合框架 List中封装了很多常用的方法。

String[] intro = new String[] { "L", "I", "S", "O","N" };
List<String> rets = Arrays.asList(intro);
System.out.println(rets.contains("S"));

要想操作元素的话,需要多一步转化,转成真正的 java.util.ArrayList

List<String> rets1 = new ArrayList<>(Arrays.asList(intro));
rets1.add("A");
rets1.remove("S");

setAll

Java 8 新增了 setAll() 方法,它提供了一个函数式编程的入口,可以对数组的元素进行填充:

int[] array = new int[10];
Arrays.setAll(array, i -> i * 10);
System.out.println(Arrays.toString(array));

i 就相当于是数组的下标,值从 0 开始,到 9 结束,那么 i * 10 就意味着值从 0 * 10 开始,到 9 * 10 结束,来看一下输出结果:

[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

可以用来为新数组填充基于原来数组的新元素。

parallelPrefix

parallelPrefix() 方法和 setAll() 方法一样,也是 Java 8 之后提供的,提供了一个函数式编程的入口,通过遍历数组中的元素,将当前下标位置上的元素与它之前下标的元素进行操作,然后将操作后的结果覆盖当前下标位置上的元素。

int[] arr = new int[] { 1, 2, 3, 4};
Arrays.parallelPrefix(arr, (left, right) -> left + right);
System.out.println(Arrays.toString(arr));

上面代码中有一个 Lambda 表达式((left, right) -> left + right)这段代码等同于:

int[] arr = new int[]{1, 2, 3, 4};
Arrays.parallelPrefix(arr, (left, right) -> {
    System.out.println(left + "," + right);
    return left + right;
});
System.out.println(Arrays.toString(arr));

输出结果就明白了:

1,2
3,3
6,4
[1, 3, 6, 10]

也就是说, Lambda 表达式执行了三次:

  • 第一次是 1 和 2 相加,结果是 3,替换下标为 1 的位置
  • 第二次是 3 和 3 相加,结果是 6,也就是第一次的结果和下标为 2 的元素相加的结果
  • 第三次是 6 和 4 相加,结果是 10,也就是第二次的结果和下标为 3 的元素相加的结果

StringUtils工具类

org.apache.commons.lang3包下的StringUtils工具类,给我们提供了非常丰富的选择。

Maven 坐标:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

StringUtils 提供了非常多实用的方法,只截图两页。

[外链)]

在这里插入图片描述

字符串判空

其实空字符串,不只是 null 一种,还有"“,” ","null"等等,多种情况。

StringUtils 给我们提供了多个判空的静态方法,例如

String str1 = null;
String str2 = "";
String str3 = " ";
String str4 = "abc";
System.out.println(StringUtils.isEmpty(str1));
System.out.println(StringUtils.isEmpty(str2));
System.out.println(StringUtils.isEmpty(str3));
System.out.println(StringUtils.isEmpty(str4));
System.out.println("=====");
System.out.println(StringUtils.isNotEmpty(str1));
System.out.println(StringUtils.isNotEmpty(str2));
System.out.println(StringUtils.isNotEmpty(str3));
System.out.println(StringUtils.isNotEmpty(str4));
System.out.println("=====");
System.out.println(StringUtils.isBlank(str1));
System.out.println(StringUtils.isBlank(str2));
System.out.println(StringUtils.isBlank(str3));
System.out.println(StringUtils.isBlank(str4));
System.out.println("=====");
System.out.println(StringUtils.isNotBlank(str1));
System.out.println(StringUtils.isNotBlank(str2));
System.out.println(StringUtils.isNotBlank(str3));
System.out.println(StringUtils.isNotBlank(str4));

执行结果

true
true
false
false
=====
false
false
true
true
=====
true
true
true
false
=====
false
false
false
true

示例中的:isEmptyisNotEmptyisBlankisNotBlank,这 4 个判空方法你们可以根据实际情况使用。

优先推荐使用isBlankisNotBlank方法,因为它会把" "也考虑进去

分隔字符串

分隔字符串是常见需求,如果直接使用 String 类的 split 方法,就可能会出现空指针异常。

String str1 = null;
System.out.println(StringUtils.split(str1,","));
System.out.println(str1.split(","));

执行结果:

null
Exception in thread "main" java.lang.NullPointerException
\tat com.sue.jump.service.test1.UtilTest.main(UtilTest.java:21)

使用 StringUtils 的 split 方法会返回 null,而使用 String 的 split 方法会报指针异常。

判断是否纯数字

给定一个字符串,判断它是否为纯数字,可以使用isNumeric方法。例如:

String str1 = "123";
String str2 = "123q";
String str3 = "0.33";
System.out.println(StringUtils.isNumeric(str1));
System.out.println(StringUtils.isNumeric(str2));
System.out.println(StringUtils.isNumeric(str3));

执行结果:

true
false
false

将集合拼接成字符串

有时候,我们需要将某个集合的内容,拼接成一个字符串,然后输出,这时可以使用join方法。例如:

List<String> list = Lists.newArrayList("a", "b", "c");
List<Integer> list2 = Lists.newArrayList(1, 2, 3);
System.out.println(StringUtils.join(list, ","));
System.out.println(StringUtils.join(list2, " "));

执行结果:

a,b,c
1 2 3

其他方法

这里再列举一些,其他的方法可以自己去研究一下。

  • trim(String str):去除字符串首尾的空白字符。
  • trimToEmpty(String str):去除字符串首尾的空白字符,如果字符串为 null,则返回空字符串。
  • trimToNull(String str):去除字符串首尾的空白字符,如果结果为空字符串,则返回 null。
  • equals(String str1, String str2):比较两个字符串是否相等。
  • equalsIgnoreCase(String str1, String str2):比较两个字符串是否相等,忽略大小写。
  • startsWith(String str, String prefix):检查字符串是否以指定的前缀开头。
  • endsWith(String str, String suffix):检查字符串是否以指定的后缀结尾。
  • contains(String str, CharSequence seq):检查字符串是否包含指定的字符序列。
  • indexOf(String str, CharSequence seq):返回指定字符序列在字符串中首次出现的索引,如果没有找到,则返回 -1。
  • lastIndexOf(String str, CharSequence seq):返回指定字符序列在字符串中最后一次出现的索引,如果没有找到,则返回 -1。
  • substring(String str, int start, int end):截取字符串中指定范围的子串。
  • replace(String str, String searchString, String replacement):替换字符串中所有出现的搜索字符串为指定的替换字符串。
  • replaceAll(String str, String regex, String replacement):使用正则表达式替换字符串中所有匹配的部分。
  • join(Iterable<?> iterable, String separator):使用指定的分隔符将可迭代对象中的元素连接为一个字符串。
  • split(String str, String separator):使用指定的分隔符将字符串分割为一个字符串数组。
  • capitalize(String str):将字符串的第一个字符转换为大写。
  • uncapitalize(String str):将字符串的第一个字符转换为小写。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值