Kotlin Koans學(xué)習(xí)筆記(2)

這是Kotlin Koans學(xué)習(xí)筆記的第二部分佑刷,上一篇在這里

第二部分一共12個(gè)任務(wù)耸峭,都是關(guān)于Kotlin集合操作瞒斩。

13.Introduction

Kotlin提供了一系列的to方法將一個(gè)集合類(lèi)型轉(zhuǎn)換成另外一個(gè)集合類(lèi)型。

這一部分的第一個(gè)任務(wù)很簡(jiǎn)單啄巧,根據(jù)提示就可以完成寻歧,關(guān)于任務(wù)就不必多說(shuō)。

先說(shuō)明一下第二部分所有任務(wù)的數(shù)據(jù)模型秩仆。這一部分所有的任務(wù)都是圍繞一個(gè)商店(Shop)展開(kāi)熄求,商店有一個(gè)客戶(Customer)列表。

客戶具有姓名逗概、城市和訂單(Order)列表三個(gè)屬性弟晚。

訂單具有商品(Product)列表和是否已經(jīng)發(fā)貨兩個(gè)屬性。

商品具有名稱(chēng)和價(jià)格兩個(gè)屬性逾苫。

data class Shop(val name: String, val customers: List<Customer>)

data class Customer(val name: String, val city: City, val orders: List<Order>) {
    override fun toString() = "$name from ${city.name}"
}

data class Order(val products: List<Product>, val isDelivered: Boolean)

data class Product(val name: String, val price: Double) {
    override fun toString() = "'$name' for $price"
}

data class City(val name: String) {
    override fun toString() = name
}

第二部分所有的任務(wù)都是使用擴(kuò)展函數(shù)的形式完成卿城。

14.Filter Map

這個(gè)任務(wù)主要練習(xí)使用filtermap這兩個(gè)方法。

filter

filter方法返回一個(gè)包含所有滿足指定條件元素的列表铅搓。與之對(duì)應(yīng)的還有filterNot瑟押,顧名思義就是返回一個(gè)包含所有不滿足指定條件的元素列表。還有一個(gè)filterNotNull星掰,返回所有不為null的元素列表多望。

回到我們的任務(wù)要求:返回指定城市所有客戶的列表。使用filter方法就可以完成:

fun Shop.getCustomersFrom(city: City): List<Customer> {
    // Return a list of the customers who live in the given city
    return customers.filter{it.city == city}
}

再精簡(jiǎn)一下:

// Return a list of the customers who live in the given city
fun Shop.getCustomersFrom(city: City) = customers.filter{it.city == city}

map

map就是將指定的轉(zhuǎn)換函數(shù)運(yùn)用到原始集合的每一個(gè)元素氢烘,并返回一個(gè)轉(zhuǎn)換后的集合怀偷。

任務(wù)要求返回所有客戶所在城市的Set。這里我們需要使用map 和toSet兩個(gè)方法:

// Return the set of cities the customers are from
fun Shop.getCitiesCustomersAreFrom() = customers.map { it.city }.toSet()

15.All Any and others Predicates

這個(gè)任務(wù)主要練習(xí)all ,anycount等幾個(gè)方法播玖。

all

第一個(gè)小任務(wù)是判斷是否所有的客戶都來(lái)自指定的城市椎工。這需要使用Kotlin庫(kù)提供的all方法。如果所有的元素都滿足指定的條件那么all方法就返回true:

// Return true if all customers are from the given city
fun Shop.checkAllCustomersAreFrom(city: City): Boolean {
    //return customers.filter { it.city != city }.isEmpty()
    //return customers.filter{!it.isFrom(city)}.isEmpty()
    return customers.all { it.isFrom(city) }
}

當(dāng)然也可以不使用all來(lái)完成,不過(guò)效率可能沒(méi)有all高维蒙,因?yàn)閍ll方法在遍歷的過(guò)程中遇到第一個(gè)不滿足條件的元素就返回結(jié)果(false):

// Return true if all customers are from the given city
fun Shop.checkAllCustomersAreFrom(city: City) = customers.filter{!it.isFrom(city)}.isEmpty()

any

第二個(gè)小任務(wù)就是查詢是否至少存在一個(gè)用戶來(lái)自指定的城市掰吕。需要使用any方法,如果至少有一個(gè)元素滿足指定的條件any就返回ture:

// Return true if there is at least one customer from the given city
fun Shop.hasCustomerFrom(city: City) = customers.any{it.city==city}

count

第三個(gè)小任務(wù)計(jì)算來(lái)自指定城市的客戶數(shù)量颅痊。需要使用count方法殖熟,count方法返回滿足指定條件的元素?cái)?shù)量。

fun Shop.countCustomersFrom(city: City): Int {
    // Return the number of customers from the given city
    return customers.count{ it.city == city}
}

firstOrNull

最后一個(gè)小任務(wù)斑响,返回一個(gè)來(lái)自指定城市的客戶菱属,如果沒(méi)有就返回null。需要使用firstOrNull方法恋捆,該方法返回第一個(gè)滿足指定條件的元素照皆,如果沒(méi)有就返回null重绷。和它相似的還有first沸停,不過(guò)first是返回第一個(gè)滿足指定條件的元素,如果沒(méi)有元素滿足指定條件則拋出異常NoSuchElementException昭卓。

// Return a customer who lives in the given city, or null if there is none
fun Shop.findAnyCustomerFrom(city: City) = customers.firstOrNull { it.city == city }

16.FlatMap

這個(gè)任務(wù)的兩個(gè)小項(xiàng)都是練習(xí)使用flatmap方法愤钾。flatmap方法就是針對(duì)列表中的每一項(xiàng)根據(jù)指定的方法生成一個(gè)列表,最后將所有的列表拼接成一個(gè)列表返回候醒。

第一個(gè)小項(xiàng)是要求返回一個(gè)客戶所有已訂購(gòu)的產(chǎn)品能颁,需要使用flatmap方法,遍歷該用戶所有的訂單倒淫,然后將所有訂單的產(chǎn)品拼接起來(lái):

val Customer.orderedProducts: Set<Product> get() {
    // Return all products ordered by customer
    return orders.flatMap { it.products }.toSet()
}

第二個(gè)小項(xiàng)是要求返回所有至少被一個(gè)客戶訂購(gòu)過(guò)的商品集合伙菊。這個(gè)在第一個(gè)小任務(wù)的基礎(chǔ)上再flatmap一次:

val Shop.allOrderedProducts: Set<Product> get() {
    // Return all products that were ordered by at least one customer
    return customers.flatMap { it.orderedProducts }.toSet()
}

17.Max Min

第一個(gè)任務(wù)是返回商店中訂單數(shù)目最多的一個(gè)客戶。使用Kotlin庫(kù)提供的max方法很好實(shí)現(xiàn)敌土。max方法返回最大的一個(gè)元素镜硕,如果沒(méi)有元素則返回null。對(duì)于自定義的對(duì)象返干,我們可以通過(guò)maxBy方法提供最大的評(píng)判標(biāo)準(zhǔn)兴枯,maxBy方法返回第一個(gè)滿足指定評(píng)判標(biāo)準(zhǔn)的最大值。

fun Shop.getCustomerWithMaximumNumberOfOrders(): Customer? {
    // Return a customer whose order count is the highest among all customers
    return customers.maxBy { it.orders.size }
}

第二個(gè)任務(wù)是要求返回一個(gè)客戶所訂購(gòu)商品中價(jià)格最高的一個(gè)商品矩欠,使用flatmapmaxBy組合:

fun Customer.getMostExpensiveOrderedProduct(): Product? {
    // Return the most expensive product which has been ordered
    return orders.flatMap { it.products }.maxBy { it.price }
}

當(dāng)然和maxmaxBy對(duì)應(yīng)的還有minminBy财剖,只不過(guò)返回的是最小值。

18.Sort

Kotlin庫(kù)提供了為元素排序的方法sorted癌淮。sorted方法會(huì)返回一個(gè)升序排序的列表躺坟,同樣可以通過(guò)sortedBy指定排序的標(biāo)準(zhǔn),按照指定的標(biāo)準(zhǔn)排序乳蓄。

任務(wù)的要求返回一個(gè)客戶列表瞳氓,客戶的順序是根據(jù)訂單的數(shù)量由低到高排列:

fun Shop.getCustomersSortedByNumberOfOrders(): List<Customer> {
    // Return a list of customers, sorted by the ascending number of orders they made
    return customers.sortedBy { it.orders.size }
}

對(duì)于排序操作同樣可以要求按照降序排序,兩個(gè)方法分別是:sortedDescendingsortedByDescending

還有另外一個(gè)操作方法就是反轉(zhuǎn)reverse匣摘。

19.Sum

任務(wù)要求計(jì)算一個(gè)客戶所有已訂購(gòu)商品的價(jià)格總和店诗。使用Kotlin的sumBy方法就可以完成,sumBy將集合中所有元素按照指定的函數(shù)變換以后的結(jié)果累加音榜。當(dāng)然先要將所有的訂單flatmap:

fun Customer.getTotalOrderPrice(): Double {
    // Return the sum of prices of all products that a customer has ordered.
    // Note: a customer may order the same product for several times.
   return  orders.flatMap { it.products }.sumByDouble { it.price }
}

20.GroupBy

groupBy方法返回一個(gè)根據(jù)指定條件分組好的map庞瘸。任務(wù)要求是返回來(lái)自每一個(gè)城市的客戶的map:

fun Shop.groupCustomersByCity(): Map<City, List<Customer>> {
    // Return a map of the customers living in each city
    return  customers.groupBy { it.city }
}

21.Parition

任務(wù)要求返回所有未發(fā)貨訂單數(shù)目多于已發(fā)貨訂單的用戶。

任務(wù)的范例中展示了怎么使用partition方法赠叼。partition方法會(huì)將原始的集合分成一對(duì)集合擦囊,這一對(duì)集合中第一個(gè)是滿足指定條件的元素集合,第二個(gè)是不滿足指定條件的集合嘴办。

這里我們先給Customer定義一個(gè)函數(shù)瞬场,判斷該用戶是否屬于未發(fā)貨訂單大于已發(fā)貨訂單,處理方法就是使用partition方法將所有的訂單分割涧郊,分割的條件就是該訂單已經(jīng)發(fā)貨:

fun Customer.isMoreUndeliveredOrdersThanDelivered(): Boolean{
    val(delivered, undelivered) = orders.partition { it.isDelivered }
    return delivered.size < undelivered.size
}

然后再對(duì)所有的客戶進(jìn)行篩選:

fun Shop.getCustomersWithMoreUndeliveredOrdersThanDelivered(): Set<Customer> {
    // Return customers who have more undelivered orders than delivered
    return customers.filter { it.isMoreUndeliveredOrdersThanDelivered() }.toSet()
}

將這兩個(gè)函數(shù)寫(xiě)到一起:

fun Shop.getCustomersWithMoreUndeliveredOrdersThanDelivered() =
        customers.filter {
            val(delivered, undelivered) = it.orders.partition { it.isDelivered }
            undelivered.size > delivered.size 
        }.toSet()

22.Fold

任務(wù)要求返回每一個(gè)顧客都購(gòu)買(mǎi)過(guò)的商品集合贯被。

先來(lái)看一下fold方法,fold方法就是給定一個(gè)初始值妆艘,然后通過(guò)迭代對(duì)集合中的每一個(gè)元素執(zhí)行指定的操作并將操作的結(jié)果累加彤灶。注意操作函數(shù)的兩個(gè)參數(shù)分別是累加結(jié)果和集合的元素。

直接看fold函數(shù)的定義吧:

public inline fun <T, R> Iterable<T>.fold(initial: R, operation: (R, T) -> R): R {
    var accumulator = initial
    for (element in this) accumulator = operation(accumulator, element)
    return accumulator
}

回到我們的任務(wù)批旺,使用fold函數(shù)幌陕,由于任務(wù)要求返回所有客戶都已經(jīng)訂購(gòu)的商品,所以初始值設(shè)置為所有已經(jīng)訂購(gòu)的商品汽煮,然后用這個(gè)初始值去和每一個(gè)客戶已訂購(gòu)的商品求交集搏熄,最終的結(jié)果就是所有用戶都已經(jīng)購(gòu)買(mǎi)過(guò)的商品:

fun Shop.getSetOfProductsOrderedByEveryCustomer(): Set<Product> {
    // Return the set of products ordered by every customer
    return customers.fold(allOrderedProducts, {
        orderedByAll, customer -> orderedByAll.intersect(customer.orderedProducts)
    })
}

這里使用了Kotlin提供的intersect方法。

23.CompoundTasks

終于快結(jié)束這一部分的任務(wù)了暇赤。這一部分包括幾個(gè)小任務(wù)心例,完成任務(wù)需要用到前面所練習(xí)的各種方法的組合。

來(lái)看第一個(gè)小任務(wù):返回所有購(gòu)買(mǎi)了指定商品的客戶列表翎卓。首先給Customer擴(kuò)展一個(gè)方法契邀,判斷他是否已經(jīng)訂購(gòu)指定的商品,使用any方法:

fun Customer.hasOrderedProduct(product: Product) = orders.any{it.products.contains(product)}

然后根據(jù)他是否已經(jīng)訂購(gòu)指定商品來(lái)做過(guò)濾:

fun Shop.getCustomersWhoOrderedProduct(product: Product): Set<Customer> {
    // Return the set of customers who ordered the specified product
    return customers.filter { it.hasOrderedProduct(product) }.toSet()
}

第二個(gè)小任務(wù):查找某個(gè)用戶所有已發(fā)貨的商品中最昂貴的商品失暴。首先過(guò)濾出已發(fā)貨的訂單坯门,然后flatmap,再求最大值:

fun Customer.getMostExpensiveDeliveredProduct(): Product? {
    // Return the most expensive product among all delivered products
    // (use the Order.isDelivered flag)
    return orders.filter { it.isDelivered }.flatMap { it.products }.maxBy { it.price }
}

第三個(gè)小任務(wù):查找指定商品被購(gòu)買(mǎi)的次數(shù)逗扒。首先獲取到客戶所有已訂購(gòu)的商品列表古戴,使用flatmap

fun Customer.getOrderedProducts() = orders.flatMap { it.products }

然后繼續(xù)flatmap,將所有客戶已經(jīng)訂購(gòu)的商品組成一個(gè)列表矩肩,最后再count

fun Shop.getNumberOfTimesProductWasOrdered(product: Product): Int {
    // Return the number of times the given product was ordered.
    // Note: a customer may order the same product for several times.
    return customers.flatMap { it.getOrderedProducts() }.count{it == product}
}

將兩個(gè)函數(shù)組合到一起:

    // Return the number of times the given product was ordered.
    // Note: a customer may order the same product for several times.
fun Shop.getNumberOfTimesProductWasOrdered(product: Product) 
= customers.flatMap { it.orders.flatMap { it.products } }.count{it == product}

24.Extensions On Collections

最后一個(gè)任務(wù)现恼,就是實(shí)現(xiàn) _24_JavaCode.doSomethingStrangeWithCollection函數(shù)的功能。所以先讀懂_24_JavaCode.doSomethingStrangeWithCollection的意圖:

public class _24_JavaCode extends JavaCode {
    public Collection<String> doSomethingStrangeWithCollection(Collection<String> collection) {
        Map<Integer, List<String>> groupsByLength = Maps.newHashMap();
        for (String s : collection) {
            List<String> strings = groupsByLength.get(s.length());
            if (strings == null) {
                strings = Lists.newArrayList();
                groupsByLength.put(s.length(), strings);
            }
            strings.add(s);
        }

        int maximumSizeOfGroup = 0;
        for (List<String> group : groupsByLength.values()) {
            if (group.size() > maximumSizeOfGroup) {
                maximumSizeOfGroup = group.size();
            }
        }

        for (List<String> group : groupsByLength.values()) {
            if (group.size() == maximumSizeOfGroup) {
                return group;
            }
        }
        return null;
    }
}
  • 將一個(gè)字符串集合按照長(zhǎng)度分組,放入一個(gè)map中
  • 求出map中所有元素(String List)的最大長(zhǎng)度
  • 根據(jù)步驟2的結(jié)果叉袍,返回map中字符串?dāng)?shù)目最多的那一組

Kotlin的實(shí)現(xiàn),首先根據(jù)長(zhǎng)度分組始锚,然后求最大值:

fun doSomethingStrangeWithCollection(collection: Collection<String>): Collection<String>? {
    val groupsByLength = collection.groupBy { it.length }

    return groupsByLength.values.maxBy { it.size }
}

精簡(jiǎn)一下:

fun doSomethingStrangeWithCollection(collection: Collection<String>) =
        collection.groupBy { it.length }.values.maxBy { it.size }

好了,第二部分全部12個(gè)任務(wù)都在這里了喳逛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瞧捌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子润文,更是在濱河造成了極大的恐慌姐呐,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件典蝌,死亡現(xiàn)場(chǎng)離奇詭異曙砂,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)骏掀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)鸠澈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人砖织,你說(shuō)我怎么就攤上這事款侵∧┘觯” “怎么了侧纯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)甲脏。 經(jīng)常有香客問(wèn)我眶熬,道長(zhǎng),這世上最難降的妖魔是什么块请? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任娜氏,我火速辦了婚禮,結(jié)果婚禮上墩新,老公的妹妹穿的比我還像新娘贸弥。我一直安慰自己,他們只是感情好海渊,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布绵疲。 她就那樣靜靜地躺著,像睡著了一般臣疑。 火紅的嫁衣襯著肌膚如雪盔憨。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,806評(píng)論 1 290
  • 那天讯沈,我揣著相機(jī)與錄音郁岩,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛问慎,可吹牛的內(nèi)容都是我干的萍摊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼如叼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼记餐!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起薇正,我...
    開(kāi)封第一講書(shū)人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤片酝,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后挖腰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體雕沿,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年猴仑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了审轮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辽俗,死狀恐怖疾渣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情崖飘,我是刑警寧澤榴捡,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站朱浴,受9級(jí)特大地震影響吊圾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜翰蠢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一项乒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧梁沧,春花似錦檀何、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至酥泞,卻和暖如春砚殿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背芝囤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工似炎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辛萍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓羡藐,卻偏偏與公主長(zhǎng)得像贩毕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子仆嗦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,769評(píng)論 25 707
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理辉阶,服務(wù)發(fā)現(xiàn),斷路器瘩扼,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • 1.Sqlite的簡(jiǎn)介 Sqlite是嵌入式的關(guān)系型數(shù)據(jù)庫(kù)谆甜,其特點(diǎn)如下: 1)它是基于c語(yǔ)言開(kāi)發(fā)的數(shù)據(jù)庫(kù),libs...
    _TT_閱讀 1,316評(píng)論 0 1
  • 春來(lái)綠一川集绰,石橋朱塔兩依然规辱。 壹 益生第一時(shí)間知道了向海峽對(duì)岸開(kāi)放探親的政策,那一股尋親浪潮席卷每一個(gè)老兵的神經(jīng)栽燕。...
    xhy0606閱讀 290評(píng)論 1 9