1. 程式人生 > >Java謎題暢讀版之庫謎題

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有可能溢位.