常用工具
随机数:Java 8
在Java8中java.util.Random
类的一个非常明显的变化就是新增了返回随机数流(random
Stream of numbers)的一些方法。
下面的代码是创建一个无穷大的double类型的数字流,这些数字在0(包括0)和1( 不包含1)之间。
Random random = new Random(); DoubleStream doubleStream = random.doubles();
下面的代码是创建一个无穷大的int类型的数字流,这些数字在0(包括0)和100(不包括 100)之间。
Random random = new Random(); IntStream intStream = random.ints(0, 100);
那么这些无穷大的数字流用来做什么呢?接下来,我通过一些案例来分析。记住,这些 无穷大的数字流只能通过某种方式被截断(limited)。
示例1:创建10个随机的整数流并打印出来:
intStream.limit(10).forEach(System.out::println);
示例2:创建100个随机整数:
List<Integer> randomBetween0And99 = intStream.limit(100).boxed().collect(Collectors.toList());
对于高斯伪随机数(gaussian pseudo-random values)来说,没有等价于
random.doubles()
方法所创建的流,然而,如果用java8所提供的功能是非常容易实现
的。
Random random = new Random(); DoubleStream gaussianStream = Stream.generate(random::nextGaussian).mapToDouble(e -> e);
这里,我使用了Stream.generate()
api,并传入Supplier
类的对象作为参数,这个
对象是通过调用Random类中的方法 nextGaussian()
创建另一个高斯伪随机数。
接下来,我们来对double类型的伪随机数流和double类型的高斯伪随机数流做一个更加 有意思的事情,那就是获得两个流的随机数的分配情况。预期的结果是:
- double类型的伪随机数是均匀的分配的,
- 而double类型的高斯伪随机数应该是正态分布的。
通过下面的代码,我生成了一百万个伪随机数,这是通过java8提供的api实现的:
Random random = new Random(); DoubleStream doubleStream = random.doubles(-1.0, 1.0); LinkedHashMap<Range, Integer> rangeCountMap = doubleStream.limit(1000000).boxed().map(Ranges::of).collect( Ranges::emptyRangeCountMap, (m, e) -> m.put(e, m.get(e) + 1), Ranges::mergeRangeCountMaps); rangeCountMap.forEach((k, v) -> System.out.println(k.from() + "\t" + v));
代码的运行结果如下:
-1 49730 -0.9 49931 -0.8 50057 -0.7 50060 -0.6 49963 -0.5 50159 -0.4 49921 -0.3 49962 -0.2 50231 -0.1 49658 0 50177 0.1 49861 0.2 49947 0.3 50157 0.4 50414 0.5 50006 0.6 50038 0.7 49962 0.8 50071 0.9 49695
为了类比,我们再生成一百万个高斯伪随机数:
Random random = new Random(); DoubleStream gaussianStream = Stream.generate(random::nextGaussian).mapToDouble(e -> e); LinkedHashMap<Range, Integer> gaussianRangeCountMap = gaussianStream.filter(e -> (e >= -1.0 && e < 1.0)).limit(1000000).boxed() .map(Ranges::of).collect(Ranges::emptyRangeCountMap, (m, e) -> m.put(e, m.get(e) + 1), Ranges::mergeRangeCountMaps); gaussianRangeCountMap.forEach( (k, v) -> System.out.println(k.from() + "\t" + v));
上面代码输出的结果恰恰与我们预期结果相吻合,即:double类型的伪随机数是均匀分配 的,而double类型的高斯伪随机数应该是正态分布的。
用伪随机数与高斯伪随机数所得的结果分别为:
完整代码:
package lambda; import org.junit.Test; import java.util.*; import java.util.stream.*; public class RandomStreamTest { @Test public void testRandomStream() { Random random = new Random(); DoubleStream doubleStream = random.doubles(-1.0, 1.0); LinkedHashMap<Range, Integer> rangeCountMap = doubleStream.limit(1000000) .boxed() .map(Ranges::of) .collect(Ranges::emptyRangeCountMap, (m, e) -> m.put(e, m.get(e) + 1), Ranges::mergeRangeCountMaps); rangeCountMap.forEach((k, v) -> System.out.println(k.from() + "\t" + v)); } @Test public void testGaussianRandomStream() { Random random = new Random(); DoubleStream gaussianStream = Stream.generate(random::nextGaussian).mapToDouble(e -> e); LinkedHashMap<Range, Integer> gaussianRangeCountMap = gaussianStream .filter(e -> (e >= -1.0 && e < 1.0)) .limit(1000000) .boxed() .map(Ranges::of) .collect(Ranges::emptyRangeCountMap, (m, e) -> m.put(e, m.get(e) + 1), Ranges::mergeRangeCountMaps); gaussianRangeCountMap.forEach((k, v) -> System.out.println(k.from() + "\t" + v)); } public static class Range { private final double from; private final double to; public Range(double from, double to) { this.from = from; this.to = to; } public double from() { return from; } public double to() { return to; } @Override public String toString() { return "From: " + from + " To: " + to; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Range range = (Range) o; if (Double.compare(range.from, from) != 0) return false; if (Double.compare(range.to, to) != 0) return false; return true; } @Override public int hashCode() { int result; long temp; temp = Double.doubleToLongBits(from); result = (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(to); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } } public static class Ranges { private static LinkedHashMap<Integer, Range> rangeMap = new LinkedHashMap<>(); static { rangeMap.put(-10, new Range(-1.0, -0.9)); rangeMap.put(-9, new Range(-0.9, -0.8)); rangeMap.put(-8, new Range(-0.8, -0.7)); rangeMap.put(-7, new Range(-0.7, -0.6)); rangeMap.put(-6, new Range(-0.6, -0.5)); rangeMap.put(-5, new Range(-0.5, -0.4)); rangeMap.put(-4, new Range(-0.4, -0.3)); rangeMap.put(-3, new Range(-0.3, -0.2)); rangeMap.put(-2, new Range(-0.2, -0.1)); rangeMap.put(-1, new Range(-0.1, 0.0 )); rangeMap.put(0, new Range(0.0, 0.1 )); rangeMap.put(1, new Range(0.1, 0.2 )); rangeMap.put(2, new Range(0.2, 0.3 )); rangeMap.put(3, new Range(0.3, 0.4 )); rangeMap.put(4, new Range(0.4, 0.5 )); rangeMap.put(5, new Range(0.5, 0.6 )); rangeMap.put(6, new Range(0.6, 0.7 )); rangeMap.put(7, new Range(0.7, 0.8 )); rangeMap.put(8, new Range(0.8, 0.9 )); rangeMap.put(9, new Range(0.9, 1.0 )); } public static Range of(double d) { int key = (int) Math.floor(d * 10); return rangeMap.get(key); } public static LinkedHashMap<Range, Integer> emptyRangeCountMap() { LinkedHashMap<Range, Integer> rangeCountMap = new LinkedHashMap<>(); for (Range range : rangeMap.values()) { rangeCountMap.put(range, 0); } return rangeCountMap; } public static void mergeRangeCountMaps(Map<Range, Integer> map1, Map<Range, Integer> map2) { for (Range range : rangeMap.values()) { map1.put(range, map1.get(range) + map2.get(range)); } } }
调用Bash
public static String runBashScript(String script, String[] envp, File dir) throws IOException, InterruptedException { String result = null; Process ps = Runtime.getRuntime().exec( new String[] { "/bin/bash", "-c", script }, envp, dir); ps.waitFor(); BufferedReader br = new BufferedReader(new InputStreamReader( ps.getInputStream())); StringBuffer sb = new StringBuffer(); String line; while ((line = br.readLine()) != null) { sb.append(line).append("\n"); } result = sb.toString(); return result; }
注意:
-
对于awk类的外部程序一定要
new String[]{"/bin/sh","-c",shStr}
才可以获得流。 -
ps.waitFor()
方法等待外部程序执行完毕。
取得源代码的行号
在 C/C++ 的程序,编译器提供了两个宏来支持取得源文件中的行号和文件名,这两个宏是
__FILE__
,__LINE__
你可以如下的方法打印行号和文件名:
#include <stdio.h> int main() { fprintf(stdout,"[%s:%d] Hello World!",__FILE__,__LINE__); return 0; }
但是在 JAVA 下没有这两个宏,那么我们如何来取得文件名和行号,翻阅 JDK ,我们找到
StackTraceElement
这个类。这个类可以从Throwable
取得,另外也可以从Thread
类
取得,通过这些我写如下的一个打印行号的测试程序:
public class LineNo { public static int getLineNumber() { return Thread.currentThread().getStackTrace()[2].getLineNumber(); } public static String getFileName() { return Thread.currentThread().getStackTrace()[2].getFileName(); } public static void main(String args[]) { System.out.println("["+getFileName()+" : "+ getLineNumber() +"]"+"Hello World!"); } }
留下一个问题,上面程序中的 magic 数字 2 代表什么含义呢?
混淆
Aliasing指不同类型的多个别名指向同一个位置。
栈和堆
方法和对象在运行时内存中的位置:
equals和hashCode
hashCode()
方法用来提升性能,它与equals()
的关系:
-
如果
equal
,hashCode
必须一致。 -
如果
hashCode
,不一定equal
。
集合
集合类型Collection
与工具类Collections
:
集合类型层级:
异常
红色为Checked Exception
,必须被捕获或在方法是声明抛出: