Java常见疑难问题-应用19


60. 期常量表达式
第一个PrintWords代表客户端,第二个Words代表一个类库:
class PrintWords {
public static void main(String[] args) {
System.out//引用常量变量
.println(Words.FIRST + ” “
+ Words.SECOND + ” “
+ Words.THIRD);
}
}
class Words {
// 常量变量
public static final String FIRST = “the”;
// 非常量变量
public static final String SECOND = null;
// 常量变量
public static final String THIRD = “set”;
}
现在假设你像下面这样改变了那个库类并且重新了这个类,但并不重新客户端的程
序PrintWords:
class Words {
public static final String FIRST = “physics”;
public static final String SECOND = “chemistry”;
public static final String THIRD = “biology”;
}
此时,端的程序会打印出什么呢?结果是 the chemistry set,不是the null set,也不是physics
chemistry biology,为什么?原因就是 null 不是一个期常量表达式,而其他两个都是。

对于常量变量(如上面Words类中的FIRST、THIRD)的引用(如在PrintWords类中对
Words.FIRST、Words.THIRD的引用)会在期被转换为它们所表示的常量的值(即
PrintWords类中的Words.FIRST、Words.THIRD引用会替换成”the”与”set”)。

一个常量变量(如上面Words类中的FIRST、THIRD)的定义是,一个在编译期被常量表
达式(即编译期常量表达式)初始化的final的原生类型或String类型的变量。

那什么是”编译期常量表达式”?精确定义在[JLS 15.28]中可以找到,这样要说的是null不
是一个编译期常量表达式。

由于常量变量会编译进客户端,API的设计者在设计一个常量域之前应该仔细考虑一下是否
应该定义成常量变量。

如果你使用了一个非常量的表达式去初始化一个域,甚至是一个final或,那么这个域就不
是一个常量。下面你可以通过将一个常量表达式传给一个方法使用得它变成一个非常量:
class Words {
// 以下都成非常量变量
public static final String FIRST = ident(“the”);
public static final String SECOND = ident(null);
public static final String THIRD = ident(“set”);
private static String ident(String s) {
return s;
}
}

总之,常量变量将会被编译进那些引用它们的类中。一个常量变量就是任何常量表达式初始
化的原生类型或字符串变量。且null不是一个常量表达式。
61. 打乱数组
class Shuffle {
private static Random rd = new Random();
public static void shuffle(Object[] a) {
for (int i = 0; i swap(a, i, rd.nextInt(a.length));
}
}
public static void swap(Object[] a, int i, int j) {
Object tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
public static void main(String[] args) {
Map map = new TreeMap();
for (int i = 0; i map.put(i, 0);
}

// 测试数组上的每个位置放置的元素是否等概率
for (int i = 0; i Integer[] intArr = new Integer[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
shuffle(intArr);
for (int j = 0; j map.put(j,(Integer)map.get(j)+intArr[j]);
}
}
System.out.println(map);
for (int i = 0; i map.put(i,(Integer) map.get(i)/10000f);
}
System.out.println(map);
}
}
上面的算法不是很等概率的让某个元素打乱到其位置,程序运行了多次,大致的结果为:
{0=36031, 1=38094, 2=39347, 3=40264, 4=41374, 5=41648, 6=41780, 7=41188, 8=40274}
{0=3.6031, 1=3.8094, 2=3.9347, 3=4.0264, 4=4.1374, 5=4.1648, 6=4.178, 7=4.1188, 8=4.0274}

如果某个位置上等概率出现这9个值的话,则平均值会趋近于4,但测试的结果表明:开始
的时候比较低,然后增长超过了平均值,最后又降下来了。

如果改用下面算法:
public static void shuffle(Object[] a) {
for (int i = 0; i swap(a, i, i + rd.nextInt(a.length – i));
}
}
多次测试的结果大致如下:
{0=40207, 1=40398, 2=40179, 3=39766, 4=39735, 5=39710, 6=40074, 7=39871, 8=40060}
{0=4.0207, 1=4.0398, 2=4.0179, 3=3.9766, 4=3.9735, 5=3.971, 6=4.0074, 7=3.9871, 8=4.006}
所以修改后的算法是合理的。

另一种打乱集合的方式是通过Api中的Collections工具类:
public static void shuffle(Object[] a) {
Collections.shuffle(Arrays.asList(a));
}
其实算法与上面的基本相似,当然我们使用API中提供的会更好,会在效率上获得最大的
受益。

声明: 除非转自他站(如有侵权,请联系处理)外,本文采用 BY-NC-SA 协议进行授权 | 智乐兔
转载请注明:转自《Java常见疑难问题-应用19
本文地址:https://www.zhiletu.com/archives-252.html
关注公众号:智乐兔

赞赏

wechat pay微信赞赏alipay pay支付宝赞赏

上一篇
下一篇

相关文章

在线留言

你必须 登录后 才能留言!

在线客服
在线客服 X

售前: 点击这里给我发消息
售后: 点击这里给我发消息

智乐兔官微