Item 37: Use EnumMap instead of ordinal indexing(使用 EnumMap 替換序數(shù)索引)

Occasionally you may see code that uses the ordinal method (Item 35) to index into an array or list. For example, consider this simplistic class meant to represent a plant:

偶爾你可能會(huì)看到使用 ordinal() 的返回值(Item-35)作為數(shù)組或 list 索引的代碼坎背。例如窝剖,考慮這個(gè)簡(jiǎn)單的類(lèi)堪唐,它表示一種植物:

class Plant {
    enum LifeCycle { ANNUAL, PERENNIAL, BIENNIAL }
    final String name;
    final LifeCycle lifeCycle;

    Plant(String name, LifeCycle lifeCycle) {
        this.name = name;
        this.lifeCycle = lifeCycle;
    }

    @Override public String toString() {
        return name;
    }
}

Now suppose you have an array of plants representing a garden, and you want to list these plants organized by life cycle (annual, perennial, or biennial). To do this, you construct three sets, one for each life cycle, and iterate through the garden, placing each plant in the appropriate set. Some programmers would do this by putting the sets into an array indexed by the life cycle’s ordinal:

現(xiàn)在假設(shè)你有一個(gè)代表花園全部植物的 Plant 數(shù)組朴读,你想要列出按生命周期(一年生似踱、多年生或兩年生)排列的植物盾舌。要做到這一點(diǎn)墓臭,你需要構(gòu)造三個(gè)集合,每個(gè)生命周期一個(gè)妖谴,然后遍歷整個(gè)數(shù)組窿锉,將每個(gè)植物放入適當(dāng)?shù)募现校?/p>

// Using ordinal() to index into an array - DON'T DO THIS!
Set<Plant>[] plantsByLifeCycle =(Set<Plant>[]) new Set[Plant.LifeCycle.values().length];

for (int i = 0; i < plantsByLifeCycle.length; i++)
    plantsByLifeCycle[i] = new HashSet<>();

for (Plant p : garden)
    plantsByLifeCycle[p.lifeCycle.ordinal()].add(p);

// Print the results
for (int i = 0; i < plantsByLifeCycle.length; i++) {
    System.out.printf("%s: %s%n",
    Plant.LifeCycle.values()[i], plantsByLifeCycle[i]);
}

譯注:假設(shè) Plant 數(shù)組如下:

Plant[] garden = new Plant[]{
        new Plant("A", LifeCycle.ANNUAL),
        new Plant("B", LifeCycle.BIENNIAL),
        new Plant("C", LifeCycle.PERENNIAL),
        new Plant("D", LifeCycle.BIENNIAL),
        new Plant("E", LifeCycle.PERENNIAL),
};

輸出結(jié)果為:

ANNUAL: [A]
PERENNIAL: [E, C]
BIENNIAL: [B, D]

This technique works, but it is fraught with problems. Because arrays are not compatible with generics (Item 28), the program requires an unchecked cast and will not compile cleanly. Because the array does not know what its index represents, you have to label the output manually. But the most serious problem with this technique is that when you access an array that is indexed by an enum’s ordinal, it is your responsibility to use the correct int value; ints do not provide the type safety of enums. If you use the wrong value, the program will silently do the wrong thing or—if you’re lucky—throw an ArrayIndexOutOfBoundsException.

這種技術(shù)是有效的,但它充滿了問(wèn)題膝舅。因?yàn)閿?shù)組與泛型不兼容(Item-28)嗡载,所以該程序需要 unchecked 的轉(zhuǎn)換,否則不能順利地編譯仍稀。因?yàn)閿?shù)組不知道它的索引表示什么洼滚,所以必須手動(dòng)標(biāo)記輸出。但是這種技術(shù)最嚴(yán)重的問(wèn)題是技潘,當(dāng)你訪問(wèn)一個(gè)由枚舉序數(shù)索引的數(shù)組時(shí)遥巴,你有責(zé)任使用正確的 int 值;int 不提供枚舉的類(lèi)型安全性享幽。如果你使用了錯(cuò)誤的值铲掐,程序?qū)㈧o默執(zhí)行錯(cuò)誤的操作,如果幸運(yùn)的話琉闪,才會(huì)拋出 ArrayIndexOutOfBoundsException迹炼。

There is a much better way to achieve the same effect. The array is effectively serving as a map from the enum to a value, so you might as well use a Map. More specifically, there is a very fast Map implementation designed for use with enum keys, known as java.util.EnumMap. Here is how the program looks when it is rewritten to use EnumMap:

有一種更好的方法可以達(dá)到同樣的效果。該數(shù)組有效地充當(dāng)從枚舉到值的映射颠毙,因此你不妨使用 Map斯入。更具體地說(shuō),有一種非持郏快速的 Map 實(shí)現(xiàn)刻两,用于枚舉鍵,稱(chēng)為 java.util.EnumMap滴某。以下就是這個(gè)程序在使用 EnumMap 時(shí)的樣子:

// Using an EnumMap to associate data with an enum
Map<Plant.LifeCycle, Set<Plant>> plantsByLifeCycle =new EnumMap<>(Plant.LifeCycle.class);

for (Plant.LifeCycle lc : Plant.LifeCycle.values())
    plantsByLifeCycle.put(lc, new HashSet<>());

for (Plant p : garden)
    plantsByLifeCycle.get(p.lifeCycle).add(p);

System.out.println(plantsByLifeCycle);

This program is shorter, clearer, safer, and comparable in speed to the original version. There is no unsafe cast; no need to label the output manually because the map keys are enums that know how to translate themselves to printable strings; and no possibility for error in computing array indices. The reason that EnumMap is comparable in speed to an ordinal-indexed array is that EnumMap uses such an array internally, but it hides this implementation detail from the programmer, combining the richness and type safety of a Map with the speed of an array. Note that the EnumMap constructor takes the Class object of the key type: this is a bounded type token, which provides runtime generic type information (Item 33).

這個(gè)程序比原來(lái)的版本更短磅摹,更清晰滋迈,更安全,速度也差不多户誓。沒(méi)有不安全的轉(zhuǎn)換饼灿;不需要手動(dòng)標(biāo)記輸出,因?yàn)?Map 的鍵是能轉(zhuǎn)換為可打印字符串的枚舉帝美;在計(jì)算數(shù)組索引時(shí)不可能出錯(cuò)碍彭。EnumMap 在速度上與有序索引數(shù)組相當(dāng)?shù)脑蚴牵珽numMap 在內(nèi)部使用這樣的數(shù)組悼潭,但是它向程序員隱藏了實(shí)現(xiàn)細(xì)節(jié)庇忌,將 Map 的豐富的功能和類(lèi)型安全性與數(shù)組的速度結(jié)合起來(lái)。注意舰褪,EnumMap 構(gòu)造函數(shù)接受鍵類(lèi)型的 Class 對(duì)象:這是一個(gè)有界類(lèi)型標(biāo)記皆疹,它提供運(yùn)行時(shí)泛型類(lèi)型信息(Item-33)。

The previous program can be further shortened by using a stream (Item 45) to manage the map. Here is the simplest stream-based code that largely duplicates the behavior of the previous example:

通過(guò)使用流(Item-45)來(lái)管理映射占拍,可以進(jìn)一步縮短前面的程序略就。下面是基于流的最簡(jiǎn)單的代碼,它在很大程度上復(fù)制了前一個(gè)示例的行為:

// Naive stream-based approach - unlikely to produce an EnumMap!
System.out.println(Arrays.stream(garden).collect(groupingBy(p -> p.lifeCycle)));

譯注:以上代碼需要引入 java.util.stream.Collectors.groupingBy晃酒,輸出結(jié)果如下:

{BIENNIAL=[B, D], ANNUAL=[A], PERENNIAL=[C, E]}

The problem with this code is that it chooses its own map implementation, and in practice it won’t be an EnumMap, so it won’t match the space and time performance of the version with the explicit EnumMap. To rectify this problem, use the three-parameter form of Collectors.groupingBy, which allows the caller to specify the map implementation using the mapFactory parameter:

這段代碼的問(wèn)題在于它選擇了自己的 Map 實(shí)現(xiàn)残制,而實(shí)際上它不是 EnumMap,所以它的空間和時(shí)間性能與顯式 EnumMap 不匹配。要糾正這個(gè)問(wèn)題,可以使用 Collectors.groupingBy 的三參數(shù)形式胯陋,它允許調(diào)用者使用 mapFactory 參數(shù)指定 Map 實(shí)現(xiàn):

// Using a stream and an EnumMap to associate data with an enum
System.out.println(
    Arrays.stream(garden).collect(groupingBy(p -> p.lifeCycle,() -> new EnumMap<>(LifeCycle.class), toSet()))
);

譯注:以上代碼需要引入 java.util.stream.Collectors.toSet

This optimization would not be worth doing in a toy program like this one but could be critical in a program that made heavy use of the map.

這種優(yōu)化在示例程序中不值得去做赘来,但在大量使用 Map 的程序中可能非常重要。

The behavior of the stream-based versions differs slightly from that of the EmumMap version. The EnumMap version always makes a nested map for each plant lifecycle, while the stream-based versions only make a nested map if the garden contains one or more plants with that lifecycle. So, for example, if the garden contains annuals and perennials but no biennials, the size of plantsByLifeCycle will be three in the EnumMap version and two in both of the stream-based versions.

基于流的版本的行為與 EmumMap 版本略有不同。EnumMap 版本總是為每個(gè)植物生命周期生成一個(gè)嵌套 Map,而基于流的版本只在花園包含具有該生命周期的一個(gè)或多個(gè)植物時(shí)才生成嵌套 Map。例如折汞,如果花園包含一年生和多年生植物,但沒(méi)有兩年生植物盖腿,plantsByLifeCycle 的大小在 EnumMap 版本中為 3爽待,在基于流的版本中為 2。

You may see an array of arrays indexed (twice!) by ordinals used to represent a mapping from two enum values. For example, this program uses such an array to map two phases to a phase transition (liquid to solid is freezing, liquid to gas is boiling, and so forth):

你可能會(huì)看到被序數(shù)索引(兩次t娓)的數(shù)組鸟款,序數(shù)用于表示兩個(gè)枚舉值的映射。例如茂卦,這個(gè)程序使用這樣的一個(gè)數(shù)組來(lái)映射兩個(gè)狀態(tài)到一個(gè)狀態(tài)的轉(zhuǎn)換過(guò)程(液體到固體是凍結(jié)的何什,液體到氣體是沸騰的,等等):

// Using ordinal() to index array of arrays - DON'T DO THIS!
public enum Phase {
    SOLID, LIQUID, GAS;

    public enum Transition {
        MELT, FREEZE, BOIL, CONDENSE, SUBLIME, DEPOSIT;

        // Rows indexed by from-ordinal, cols by to-ordinal
        private static final Transition[][] TRANSITIONS = {
            { null, MELT, SUBLIME },
            { FREEZE, null, BOIL },
            { DEPOSIT, CONDENSE, null }
        };

        // Returns the phase transition from one phase to another
        public static Transition from(Phase from, Phase to) {
            return TRANSITIONS[from.ordinal()][to.ordinal()];
        }
    }
}

譯注:固體等龙、液體处渣、氣體三態(tài)伶贰,對(duì)應(yīng)的三組變化:融化 MELT,凍結(jié) FREEZE(固態(tài)與液態(tài))罐栈;沸騰 BOIL黍衙,凝固 CONDENSE(液態(tài)與氣態(tài));升華 SUBLIME荠诬,凝華 DEPOSIT(固態(tài)與氣態(tài))们豌。

This program works and may even appear elegant, but appearances can be deceiving. Like the simpler garden example shown earlier, the compiler has no way of knowing the relationship between ordinals and array indices. If you make a mistake in the transition table or forget to update it when you modify the Phase or Phase.Transition enum type, your program will fail at runtime. The failure may be an ArrayIndexOutOfBoundsException, a NullPointerException, or (worse) silent erroneous behavior. And the size of the table is quadratic in the number of phases, even if the number of non-null entries is smaller.

這個(gè)程序可以工作,甚至可能看起來(lái)很優(yōu)雅浅妆,但外表可能具有欺騙性。就像前面展示的更簡(jiǎn)單的 garden 示例一樣障癌,編譯器無(wú)法知道序數(shù)和數(shù)組索引之間的關(guān)系凌外。如果你在轉(zhuǎn)換表中出錯(cuò),或者在修改 Phase 或 Phase.Transition 枚舉類(lèi)型時(shí)忘記更新涛浙,你的程序?qū)⒃谶\(yùn)行時(shí)失敗康辑。失敗可能是拋出 ArrayIndexOutOfBoundsException、NullPointerException 或(更糟糕的)靜默錯(cuò)誤行為轿亮。并且即使非空項(xiàng)的數(shù)目更小疮薇,該表的大小也為狀態(tài)數(shù)量的二次方。

Again, you can do much better with EnumMap. Because each phase transition is indexed by a pair of phase enums, you are best off representing the relationship as a map from one enum (the “from” phase) to a map from the second enum (the “to” phase) to the result (the phase transition). The two phases associated with a phase transition are best captured by associating them with the phase transition enum, which can then be used to initialize the nested EnumMap:

同樣我注,使用 EnumMap 可以做得更好按咒。因?yàn)槊總€(gè)階段轉(zhuǎn)換都由一對(duì)階段枚舉索引,所以最好將這個(gè)關(guān)系用 Map 表示但骨,從一個(gè)枚舉(起始階段)到第二個(gè)枚舉(結(jié)束階段)到結(jié)果(轉(zhuǎn)換階段)励七。與階段轉(zhuǎn)換相關(guān)聯(lián)的兩個(gè)階段最容易捕捉到的是將它們與階段過(guò)渡的 enum 聯(lián)系起來(lái),這樣就可以用來(lái)初始化嵌套的 EnumMap:

// Using a nested EnumMap to associate data with enum pairs
public enum Phase {
    SOLID, LIQUID, GAS;

    public enum Transition {
        MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),
        BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),
        SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID);
        private final Phase from;
        private final Phase to;

        Transition(Phase from, Phase to) {
            this.from = from;
            this.to = to;
        }

        // Initialize the phase transition map
        private static final Map<Phase_new, Map<Phase_new, Transition>> m = Stream.of(values())
                .collect(groupingBy(
                        t -> t.from,
                        () -> new EnumMap<>(Phase_new.class),
                        toMap(t -> t.to, t -> t, (x, y) -> y, () -> new EnumMap<>(Phase_new.class))
                        )
                );

        public static Transition from(Phase from, Phase to) {
            return m.get(from).get(to);
        }
    }
}

The code to initialize the phase transition map is a bit complicated. The type of the map is Map<Phase, Map<Phase, Transition>>, which means “map from (source) phase to map from (destination) phase to transition.” This map-of-maps is initialized using a cascaded sequence of two collectors. The first collector groups the transitions by source phase, and the second creates an EnumMap with mappings from destination phase to transition. The merge function in the second collector ((x, y) -> y)) is unused; it is required only because we need to specify a map factory in order to get an EnumMap, and Collectors provides telescoping factories. The previous edition of this book used explicit iteration to initialize the phase transition map. The code was more verbose but arguably easier to understand.

初始化階段變化 Map 的代碼有點(diǎn)復(fù)雜奔缠。Map 的類(lèi)型是 Map<Phase, Map<Phase, Transition>>掠抬,這意味著「從(源)階段 Map 到(目標(biāo))階段 Map 的轉(zhuǎn)換過(guò)程」。這個(gè) Map 嵌套是使用兩個(gè)收集器的級(jí)聯(lián)序列初始化的校哎。第一個(gè)收集器按源階段對(duì)轉(zhuǎn)換進(jìn)行分組两波,第二個(gè)收集器使用從目標(biāo)階段到轉(zhuǎn)換的映射創(chuàng)建一個(gè) EnumMap。第二個(gè)收集器 ((x, y) -> y) 中的 merge 函數(shù)未使用闷哆;之所以需要它腰奋,只是因?yàn)槲覀冃枰付ㄒ粋€(gè) Map 工廠來(lái)獲得 EnumMap,而 Collector 提供了伸縮工廠抱怔。本書(shū)的上一個(gè)版本使用顯式迭代來(lái)初始化階段轉(zhuǎn)換映射氛堕。代碼更冗長(zhǎng),但也更容易理解野蝇。

譯注:第二版中的實(shí)現(xiàn)代碼如下:

// Initialize the phase transition map
private static final Map<Phase, Map<Phase,Transition> m =
    new EnumMap<Phase, Map<Phase ,Transition>>(Phase.class);

    static{
        for (Phase p : Phase. values())
            m.put(p,new EnumMap<Phase,Transition (Phase.class));
        for (Transition trans : Transition.values() )
            m.get(trans. src).put(trans.dst, trans) ;
    }

public static Transition from(Phase src, Phase dst) {
    return m.get(src).get(dst);
}

Now suppose you want to add a new phase to the system: plasma, or ionized gas. There are only two transitions associated with this phase: ionization, which takes a gas to a plasma; and deionization, which takes a plasma to a gas. To update the array-based program, you would have to add one new constant to Phase and two to Phase.Transition, and replace the original nine-element array of arrays with a new sixteen-element version. If you add too many or too few elements to the array or place an element out of order, you are out of luck: the program will compile, but it will fail at runtime. To update the EnumMap-based version, all you have to do is add PLASMA to the list of phases, and IONIZE(GAS, PLASMA) and DEIONIZE(PLASMA, GAS) to the list of phase transitions:

現(xiàn)在假設(shè)你想向系統(tǒng)中加入一種新階段:等離子體讼稚,或電離氣體括儒。這個(gè)階段只有兩個(gè)變化:電離,它把氣體轉(zhuǎn)為等離子體锐想;去離子作用帮寻,把等離子體變成氣體。假設(shè)要更新基于數(shù)組版本的程序赠摇,必須向 Phase 添加一個(gè)新常量固逗,向 Phase.Transition 添加兩個(gè)新常量,并用一個(gè)新的 16 個(gè)元素版本替換原來(lái)的數(shù)組中的 9 個(gè)元素?cái)?shù)組藕帜。如果你向數(shù)組中添加了太多或太少的元素烫罩,或者打亂了元素的順序,那么你就麻煩了:程序?qū)⒕幾g洽故,但在運(yùn)行時(shí)將失敗贝攒。相比之下,要更新基于 EnumMap 的版本时甚,只需將 PLASMA 添加到 Phase 列表中隘弊,將 IONIZE(GAS, PLASMA)DEIONIZE(PLASMA, GAS) 添加到 Phase.Transition 中:

// Adding a new phase using the nested EnumMap implementation
public enum Phase {
    SOLID, LIQUID, GAS, PLASMA;
    public enum Transition {
        MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),
        BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),
        SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID),
        IONIZE(GAS, PLASMA), DEIONIZE(PLASMA, GAS);
        ... // Remainder unchanged
    }
}

The program takes care of everything else and leaves you virtually no opportunity for error. Internally, the map of maps is implemented with an array of arrays, so you pay little in space or time cost for the added clarity, safety, and ease of maintenance.

這個(gè)程序會(huì)處理所有其他事情,實(shí)際上不會(huì)給你留下任何出錯(cuò)的機(jī)會(huì)荒适。在內(nèi)部梨熙,Map 的映射是用一個(gè)數(shù)組來(lái)實(shí)現(xiàn)的,因此你只需花費(fèi)很少的空間或時(shí)間成本就可以獲得更好的清晰度刀诬、安全性并易于維護(hù)咽扇。

In the interest of brevity, the above examples use null to indicate the absence of a state change (wherein to and from are identical). This is not good practice and is likely to result in a NullPointerException at runtime. Designing a clean, elegant solution to this problem is surprisingly tricky, and the resulting programs are sufficiently long that they would detract from the primary material in this item.

為了簡(jiǎn)潔起見(jiàn),最初的示例使用 null 表示沒(méi)有狀態(tài)更改(其中 to 和 from 是相同的)陕壹。這不是一個(gè)好的方式肌割,可能會(huì)在運(yùn)行時(shí)導(dǎo)致 NullPointerException。針對(duì)這個(gè)問(wèn)題設(shè)計(jì)一個(gè)干凈帐要、優(yōu)雅的解決方案是非常棘手的把敞,并且生成的程序冗長(zhǎng),以至于它們會(huì)偏離條目中的主要內(nèi)容榨惠。

In summary, it is rarely appropriate to use ordinals to index into arrays: use EnumMap instead. If the relationship you are representing is multidimensional, use EnumMap<..., EnumMap<...>>. This is a special case of the general principle that application programmers should rarely, if ever, use Enum.ordinal (Item 35).

總之奋早,用普通的序數(shù)索引數(shù)組是非常不合適的:應(yīng)使用 EnumMap 代替。 如果所表示的關(guān)系是多維的赠橙,則使用 EnumMap<..., EnumMap<...>>耽装。這是一種特殊的基本原則,程序員很少(即使有的話)使用 Enum.ordinalItem-35)期揪。


Back to contents of the chapter(返回章節(jié)目錄)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末掉奄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子凤薛,更是在濱河造成了極大的恐慌姓建,老刑警劉巖诞仓,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異速兔,居然都是意外死亡墅拭,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)涣狗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谍婉,“玉大人,你說(shuō)我怎么就攤上這事镀钓∷氚荆” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵丁溅,是天一觀的道長(zhǎng)唤蔗。 經(jīng)常有香客問(wèn)我,道長(zhǎng)唧瘾,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任别凤,我火速辦了婚禮饰序,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘规哪。我一直安慰自己求豫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布诉稍。 她就那樣靜靜地躺著蝠嘉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪杯巨。 梳的紋絲不亂的頭發(fā)上蚤告,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音服爷,去河邊找鬼杜恰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛仍源,可吹牛的內(nèi)容都是我干的心褐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼笼踩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼逗爹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起嚎于,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤掘而,失蹤者是張志新(化名)和其女友劉穎挟冠,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體镣屹,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡圃郊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了女蜈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片持舆。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖伪窖,靈堂內(nèi)的尸體忽然破棺而出逸寓,到底是詐尸還是另有隱情,我是刑警寧澤覆山,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布竹伸,位于F島的核電站,受9級(jí)特大地震影響簇宽,放射性物質(zhì)發(fā)生泄漏勋篓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一魏割、第九天 我趴在偏房一處隱蔽的房頂上張望譬嚣。 院中可真熱鬧,春花似錦钞它、人聲如沸拜银。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)尼桶。三九已至,卻和暖如春锯仪,著一層夾襖步出監(jiān)牢的瞬間泵督,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工庶喜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留幌蚊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓溃卡,卻偏偏與公主長(zhǎng)得像溢豆,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瘸羡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,325評(píng)論 0 10
  • 鬼是恐怖與惡毒的代名詞漩仙,在現(xiàn)代日本人的印象中,鬼頭上長(zhǎng)角,利爪獠牙队他,腰間纏繞著虎皮卷仑,手持狼牙棒。 日本鬼不同于中國(guó)...
    畫(huà)師昊淵閱讀 2,142評(píng)論 16 14
  • 【導(dǎo)讀】古琴曲《湘妃怨 》作者: 阿魯威 生卒年不詳麸折,字叔重锡凝,號(hào)東泉,蒙古族人垢啼。曾任南劍太守和經(jīng)筵官窜锯。今存小令十九...
    凱撒2000閱讀 1,178評(píng)論 0 1
  • 今天伊桐和軒軒進(jìn)行了第11屆尚翔杯籃球比賽。 剛剛聽(tīng)到消息是季軍芭析,孩子們付出了自己的努力锚扎,那就是成...
    古當(dāng)當(dāng)閱讀 194評(píng)論 0 0
  • 很多剛健身的小白都會(huì)問(wèn)一個(gè)問(wèn)題,我該吃什么馁启?如何去吃驾孔? 別急,小編找到一份健身飲食計(jì)劃惯疙,按照這個(gè)去執(zhí)行翠勉,保證吃出好...
    騎得隆咚貓閱讀 607評(píng)論 0 4