目录
其他API
我会按照java的各个体系,分类介绍一些比较基础的API,比如string体系的,collection体系的,本章介绍的是一些比较常用,但我并不打算介绍完他们的整个体系的API,所以先放在“其他API”一类。
快速获得随机数
大家想获得随机数,一般都是要两个步骤:新建random对象,用random对象获得随机数
public static int getRandomValue(int n) {
/* 获取[0,n)的随机整数 */
Random random = new Random(); //获得random对象
return random.nextInt(n); //通过random对象获得随机值
}
通过Math.random()可以一步到位:
public static int getRandomInt(int n) {
/* 获取[0,n)的随机整数 */
return (int) (Math.random() * n);
}
这个方法可以让我们省去创建random对象的麻烦,而且可扩展性一点不比random.next()差:
/**
*获得[0,max)的随机整数
*/
public static int getRandomInt(int max) {
return (int) (Math.random() * max);
}
/**
* 获得[lo,hi)的随机小数
*/
public static double getRandomDouble(double lo, double hi) {
return Math.random() * (hi - lo) + lo;
}
/**
* 获得[lo,hi)的随机整数
*/
public static int getRandomInt(int lo, int hi) {
return getRandomInt(hi - lo) + lo;
}
其实通过查看源码,发现Math.random()的本质还是random.nextDouble()而已:
/* Math类中的部分源码 */
private static final class RandomNumberGeneratorHolder {
static final Random randomNumberGenerator = new Random();
}
public static double random() {
return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
以后我们就用Math.random()来代替新建random对象吧!
强行四舍五入
有时当我们计算得到一个双精度浮点数,结果会很长,因为double是可以保留到小数点后面15位(虽然并不精确),这就导致我们的打印结果非常难看。
public class TestDemo {
public static void main(String[] args) {
// 打印20以内随机11个数的平均数
IntStream.generate(() -> ((int) (Math.random() * 20))) //Math生成随机数
.limit(11)
.average()
.ifPresent(System.out::println); //9.909090909090908
}
}
打印结果很长而且无意义,Math有一个around()方法能让小数四舍五入得到一个整数。
public class RoundTest {
public static void main(String[] args) {
double db=2.551549683573541;
System.out.println("Math.round(db) = " + Math.round(db)); //Math.round(db) = 3
}
}
但是当我们需要要小数点后面两个数值的时候,around()方法就显得异常尴尬,但其实只需要简单一步就能化解尴尬:printf()
public class TestDemo {
public static void main(String[] args) {
// 改进版的四舍五入!
IntStream.generate(() -> ((int) (Math.random() * 20))) //Math生成随机数
.limit(11)
.average()
.ifPresent(aver-> System.out.printf("%.2f",aver)); //5.73
}
}
马上乖乖变成5.73,其中printf()是一个规定了打印格式的方法,以后的博文会进一步讲下这个方法。现在就先记住printf()这个方法就好了!
另外要注意,double的值是不精确的,这个方法也只是强行把后面的小数去掉的方法,在开发测试中可以用,在实际工作中,还是要用BigDecimal的setScale方法。
查看数值二进制的方法
String Integer.toBinaryString(int) 这个静态方法可以把一个整数类型变成二进制的字符串输出。同理还有Integer.toHexString(int) / Integer.toOrcString(int) 分别返回十六进制和八进制的字符串,这些方法对我们开发中查看数值底层是很有帮助的:
public class IntegerTest {
public static void main(String[] args) {
testInt();
System.out.println("---------");
testChar();
System.out.println("---------");
String str = "你好吗";
char[] chars = str.toCharArray();
System.out.println("查看每个字符的2进制码:");
for (char aChar : chars) {
System.out.println(aChar+" : "+Integer.toBinaryString(aChar));
}
System.out.println();
System.out.println("查看每个字符的16进制码");
for (char aChar : chars) {
System.out.println(aChar+" : "+Integer.toHexString(aChar));
}
}
public static void testInt() {
System.out.println("Integer.toBinaryString(30) = " + Integer.toBinaryString(30));
System.out.println("Integer.toHexString(30) = " + Integer.toHexString(30));
System.out.println("Integer.toOctalString(30) = " + Integer.toOctalString(30));
}
public static void testChar() {
System.out.println("Integer.toBinaryString('b') = " + Integer.toBinaryString('b'));
System.out.println("Integer.toHexString('b') = " + Integer.toHexString('b'));
System.out.println("Integer.toOctalString('b') = " + Integer.toOctalString('b'));
}
}
Integer.toBinaryString(30) = 11110
Integer.toHexString(30) = 1e
Integer.toOctalString(30) = 36
---------
Integer.toBinaryString('b') = 1100010
Integer.toHexString('b') = 62
Integer.toOctalString('b') = 142
---------
查看每个字符的2进制:
你 : 100111101100000
好 : 101100101111101
吗 : 101010000010111
查看每个字符的16进制码
你 : 4f60
好 : 597d
吗 : 5417
但是这个方法在处理负数的时候就不太好使:
public class ByteString {
public static void main(String[] args) {
byte[] bytes = "小鹏头".getBytes(); //通过字符串获得字节数组
int count = 1;
for (byte aByte : bytes) {
System.out.print(aByte+":"+Integer.toHexString(aByte)+" ");
//System.out.printf("%x ",aByte);
if (count++%3==0) {
System.out.println();
}
}
}
}
运行结果:
-27:ffffffe5 -80:ffffffb0 -113:ffffff8f
-23:ffffffe9 -71:ffffffb9 -113:ffffff8f
-27:ffffffe5 -92:ffffffa4 -76:ffffffb4
可以看到有很多诡异的ffff在前面,这是因为java 的负数是取数字的补码的原因,我们这里不深入探讨这方面,想进一步了解的同学可以前往:https://blog.csdn.net/weixin_37870009/article/details/79775926 了解。
那如何拿到byte正确的16进制字符串呢?还是用格式化输出语句:printf() 我们用"%x"可以指定一个整数以16进制的字符串格式输出。
public class ByteString {
public static void main(String[] args) {
byte[] bytes = "小鹏头".getBytes(); //通过字符串获得字节数组
int count = 1;
for (byte aByte : bytes) {
//System.out.print(":"+Integer.toHexString(aByte)+" ");
System.out.printf(aByte+":%x ",aByte); //用格式化输出处理字节元素
if (count++%3==0) {
System.out.println();
}
}
}
}
输出结果:
-27:e5 -80:b0 -113:8f
-23:e9 -71:b9 -113:8f
-27:e5 -92:a4 -76:b4
可以得到清晰的16进制字符串,我们是把“小鹏头”这个字符串按照“UTF-8”编码来编程字节码然后输出了。
测试运行时间的更精确方式
很多人都用System.currentTimeMillis()来测试任务耗时,但有的时候,currentTimeMillis并不是很精确,用System.nanoTime()可以获得更精确的时间:
public class TestDemo {
public static void main(String[] args) {
/* 新建一个任务:用foreach方式遍历一个数组 */
int[] ints = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
TodoList one = () -> {
for (int anInt : ints) {
System.out.print(anInt + ",");
}
};
/* 新建一个:用Stream方式遍历一个数组 */
TodoList two = () -> IntStream.of(ints).forEach(n-> System.out.print(n+","));
//分别测试各个耗时:
System.out.println("任务一");
testInNano(one);
System.out.println("任务二");
testInNano(two);
System.out.println("任务一:");
testInMillion(one);
System.out.println("任务二:");
testInMillion(two);
}
public static void testInMillion(TodoList todoList) {
long begin = System.currentTimeMillis();
todoList.doSomething();
System.out.println("以毫秒为单位的测试:" + (System.currentTimeMillis() - begin));
}
public static void testInNano(TodoList todoList) {
long begin = System.nanoTime();
todoList.doSomething();
System.out.println("以纳秒为单位的测试:" + (System.nanoTime() - begin));
}
}
任务一
1,2,3,4,5,6,7,8,9,10,以纳秒为单位的测试:18332792
任务二
1,2,3,4,5,6,7,8,9,10,以纳秒为单位的测试:4451214
任务一:
1,2,3,4,5,6,7,8,9,10,以毫秒为单位的测试:0
任务二:
1,2,3,4,5,6,7,8,9,10,以毫秒为单位的测试:0
可以看到,一些操作如果以毫秒为单位会获得0毫秒,只有以纳秒为单位才能得到运行时间。1毫秒 = 1000 000 纳秒 。
System.currentTimeMillis()得到的是1970年0时0分0秒到现在的毫秒值,而System.nanoTime()得到的时间毫无意义,他只能用来跟之前的纳秒值作相减才有意义,每次启动JVM都会以不同的时间点作为纳秒的0点。
另外,其实从上述测试可以看出,sream比传统的foreach是真的速度快很多的!
本章所有源码已经上传github:https://github.com/huheman/Blog/tree/master/API_test/src,
本人水平有限,如您发现有任何错漏,请在评论中不吝指教