Java谜题畅读版之库谜题

谜题56:大问题

我们来测试一下你对BigInteger的了解程度。下面这个程序将打印出什么呢?
import java.math.BigInteger;
public class BigProblem {
    public static void main(String[ ] args) {
        BigInteger fiveThousand  = new BigInteger("5000");
        BigInteger fiftyThousand = new BigInteger("50000");
        BigInteger fiveHundredThousand = new BigInteger("500000");
        BigInteger total = BigInteger.ZERO;
        total.add(fiveThousand);
        total.add(fiftyThousand);
        total.add(fiveHundredThousand);
        System.out.println(total);
    }
}
你可能期待这个程序打印出555000,但是它打印的是0。说到这里你应该立刻意识到问题出在哪里。对此有一个很好理由可以解释:BigInteger实例是不可变的。String、BigDecimal以及包装器类型:Integer、Long、Short、Byte、Character、Boolean、Float和Double也是如此,你不能修改它们的值。

谜题57:名字里有什么?

这个程序会打印什么?
import java.util.*;
public class Name {
    private String first, last;
    public Name(String first, String last) {
        this.first = first;
        this.last = last;
    }
    public boolean equals(Object o) {
        if (!(o instanceof Name))
            return false;
        Name n = (Name)o;
        return n.first.equals(first) && n.last.equals(last);
    }
    public static void main(String[] args) {
        Set s = new HashSet();
        s.add(new Name("Mickey", "Mouse"));
        System.out.println(
            s.contains(new Name("Mickey", "Mouse")));
    }
}

当你看equal被重载的时候,一定要问hashCode有没有被重写。HashSet比较一个对象是否相等的时候,首先就会看hashCode, 因为本例没有写hashCode,则默认使用Object的hashCode,这个hashCode对于非等同的对象,一定是不同的。

谜题58:产生它的散列码

本谜题试图从前一个谜题中吸取教训。那么下面的程序将打印出什么呢?

import java.util.*;
public class Name {
    private String first, last;
    public Name(String first, String last) {
        this.first = first; this.last = last;
    }
    public boolean equals(Name n) {
        return n.first.equals(first) && n.last.equals(last);
    }    
    public int hashCode() {
        return 31 * first.hashCode() + last.hashCode();
    }
    public static void main(String[ ] args) {
        Set s = new HashSet();
        s.add(new Name("Donald", "Duck"));
        System.out.println(
            s.contains(new Name("Donald", "Duck")));
    }
}
该问题的原因在于没有正确地重载equals.

谜题59:什么是差?

下面的程序在计算一个int数组中的元素两两之间的差,将这些差置于一个集合中,然后打印该集合的尺寸大小。那么,这个程序将打印出什么呢?

import java.util.*;
public class Differences {
    public static void main(String[ ] args) {
        int vals[ ] = { 789, 678, 567, 456, 345, 234, 123, 012 };
        Set diffs = new HashSet();
        for (int i = 0; i < vals.length; i++)
            for (int j = i; j < vals.length; j++)
                diffs.add(vals[i] - vals[j]);
        System.out.println(diffs.size());
    }
}
程序似乎应该打印8,因为两两之间的差值是0,111,222,333,444,555,666,777,
但是,它在012上出了问题. 用0开头声明的整数字面量,它是8进制的.

谜题60:一行的方法

现在该轮到你写一些代码了。下面的谜题每一个都可以用一个方法来解决,这些方法的方法体都只包含一行代码。
A: 编写一个方法,它接受一个包含元素的List,并返回一个新的List,它以相同的顺序包含相同的元素,只不过它把第二次以及后续出现的重复元素都剔除了。例如,如果你传递了一个包含”spam”,”sausage”,”spam”,”spam”,”bacon”,”spam”,”tomato”和”spam”的列表,那么你将得到一个包含”spam”,”sausage”,”bacon”,”tomato”的新列表。
return new ArrayList(new HashSet(list));
B: 一个方法,它接受一个由0个或多个由逗号分隔的标志所组成的字符串,并返回一个表示这些标志的字符串数组,数组中的元素的顺序与这些标志在输入字符串中出现的顺序相同。每一个逗号后面都可能会跟随0个或多个空格字符,这个方法忽略它们。例如,如果你传递的字符串是”fear, surprise, ruthless efficiency, an almost fanatical devotion to the Pope, nice red uniforms”,那么你得到的将是一个包含5个元素的字符串数组,这些元素是”fear”,”surprise”,”ruthless efficiency”,”an almost fanatical devotion to the Pope” 和 “nice red uniform”。
return string.split(",\\S*");
C: 假设你有一个多维数组,出于调试的目的,你想打印它。你不知道这个数组有多少级,以及在数组的每一级中所存储的对象的类型。编写一个方法,它可以向你显示出在每一级上的所有元素。
Arrays.deepToString(arr);
D: 编写一个方法,它接受两个int数值,并在第一个数值与第二个数值以二进制补码形式进行比较,如果第一个数具有更多的位被置位时,返回true。
为了在一行代码中解决该谜题,你需要了解在5.0版本中添加到Java平台中的一整套位操作方法。整数类型的包装器类(Integer、Long、Short、Byte和Char)现在支持通用的位处理操作,包括highestOneBit、lowestOneBit、numberOfLeadingZeros、numberOfTrailingZeros、bitCount、rotateLeft、rotateRight、reverse、signum和reverseBytes。在本例中,你需要的是Integer.bitCount,它返回的是一个int数值中被置位的位数:
static Boolean hasMoreBitsSet(int i, int j) {
return (Integer.bitCount(i) > Integer.bitCount(j));
}

谜题61:日期游戏

下面的程序演练了Date和Calendar类的某些基本特性,它会打印出什么呢?

import java.util.*;
public class DatingGame {
    public static void main(String[ ] args) {
        Calendar cal = Calendar.getInstance();
        cal.set(1999, 12, 31); // Year, Month, Day
        System.out.print(cal.get(Calendar.YEAR) + " ");
        Date d = cal.getTime();
        System.out.println(d.getDay());
    }
}
这个题似乎应该打印出1999 31.但是结果是2000, 1.
这个题暴露了Calendar API中一个巨大的缺陷, 即月份的设定范围是从0到11. 如果本题给12,则将本题的日期推到了2000年.
Calendar.getDay打印的是某天是一星期中的第几天, 好在这个api已经被淘汰了.

谜题62:名字游戏

下面的程序考察你是否了解IdentityHashMap.

import java.util.*;
public class NameGame {
    public static void main(String args[ ]) {
        Map<String, String> m =
                new IdentityHashMap<String, String>();
        m.put("Mickey", "Mouse");
        m.put("Mickey", "Mantle");
        System.out.println(m.size());
    }
}
首先IdentityHashMap是基于标识符的HashMap,他认为k1等同k2当且仅当k1==k2. 于是你可能会说本程序会打印出2. 但是不要忘了,"Mickey"=="Mickey".

谜题63:更多同样的问题

为从前一个程序中已经吸取了教训,这个程序使用了一个通用目的的Map实现,即一个HashMap,来替代前一个程序的IdentityHashMap。那么,这个程序会打印出什么呢?

import java.util.*;
public class MoreNames {
    private Map<String,String> m = new HashMap<String,String>();
    public void MoreNames() {
        m.put("Mickey", "Mouse");
        m.put("Mickey", "Mantle");
    }
    public int size() {
        return m.size();
    }
    public static void main(String args[ ]) {
        MoreNames moreNames = new MoreNames();
        System.out.println(moreNames.size());
    }
}
这个程序的关键就在于public void MoreNames()不是构造函数.

谜题64:按余数编组

下面的程序将生成整数对3取余的柱状图,那么,它将打印出什么呢?

public class Mod {
    public static void main(String[ ] args) {
        final int MODULUS = 3;
        int[] histogram = new int[MODULUS];
        // Iterate over all ints (Idiom from Puzzle 26)
        int i = Integer.MIN_VALUE;
        do {
            histogram[Math.abs(i) % MODULUS]++;
        } while (i++ != Integer.MAX_VALUE);
        for (int j = 0; j < MODULUS; j++)
            System.out.println(histogram[j] + " ");
    }
}
这个程序会抛错:ArrayIndexOutOfBoundsException. 奇怪吧? 明明已经Math.abs了.
关键就在于1: Math.abs(Integer.MIN_VALUE)==Integer.MIN_VALUE. 2:负数取模是会有符号的.

谜题65:一种疑似排序的惊人传奇

下面的程序使用定制的比较器,对一个由随机挑选的Integer实例组成的数组进行排序,然后打印了一个描述了数组顺序的单词。回忆一下,Comparator接口只有一个方法,即compare,它在第一个参数小于第二个参数时返回一个负数,在两个参数相等时返回0,在第一个参数大于第二个参数时返回一个整数。这个程序是展示5.0版特性的一个样例程序。它使用了自动包装和解包、泛型和枚举类型。那么,它会打印出什么呢?

import java.util.*;
public class SuspiciousSort {
    public static void main(String[ ] args) {
        Random rnd = new Random();
        Integer[ ] arr = new Integer[100];
        for (int i = 0; i < arr.length; i++)
            arr[i] = rnd.nextInt();
        Comparator<Integer> cmp = new Comparator<Integer>() {
            public int compare(Integer i1, Integer i2) {
                return i2 - i1;
            }
        };
        Arrays.sort(arr, cmp);
        System.out.println(arr);
    }
}
如果你跑几下这个程序, 你会发现排序几乎不正确. 原因在于i2 - i1有可能溢出.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值