小米彈出式相機專利曝光,採反射鏡機構解決前後鏡頭拍攝需求_包裝設計

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

近年許多手機品牌都為了讓手機擁有更大的螢幕占比,無論是早期的瀏海螢幕、水滴螢幕,到後來彈出升降式前鏡頭、翻轉鏡頭或挖孔螢幕,甚至是今年有望正式量產的螢幕下前鏡頭,不過上述做法都是解決螢幕體驗和前鏡頭的自拍需求。日前,小米也向美國專利商標局和世界智慧財產權組織申請了一項相機專利,藉由彈出式機構搭配反射透鏡,讓它同時能解決以往對於前後的鏡頭的拍攝需求。

▲圖片來源:LetsGoDigital

小米彈出式相機專利曝光,採反射鏡機構解決前後鏡頭拍攝需求

去年至今年目前這段期間,在螢幕挖孔的設計已經能說是目前中階、旗艦手機的趨勢。近日外媒 LetsGoDital 曝光了小米向美國專利商標局和世界智慧財產權組織申請最新的相機專利,則帶來不同以往的相機結構。這項新專利利用相似於彈出式相機的設計,但在升降出來的並非相機模組,而是一組帶有反射鏡的機構。
它將相機鏡頭本體隱藏於手機內部朝上,並透過彈出的反射鏡機構將外界光線反射至相機鏡頭進行拍攝。同時,小米也針對這項設計設計旋轉機構,可將反射鏡旋轉 180° ,完成正反面的影像拍攝需求。

▲圖片來源:LetsGoDigital

倘若這項專利正式量產至手機上,未來在機身正反面將不再有可見的相機鏡頭,機身背面設計也將更為簡化、甚至不排除能有更多空間配備像是小米 MIX 那般全環繞螢幕手機。目前還未確定小米這項專利將在何時才有機會實現並量產,不過傳聞在今年下半年將有多款採用螢幕下前鏡頭的手機會在市場推出。

消息來源:LetsGoDigital

延伸閱讀:
小米 Redmi 系列首款支援雙 5G 的 Redmi Note 9T 5G 以及 6000mAh 大電量 Redmi 9T 在台推出

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

POCO M3 開箱、評測|6000mAh 大電量超乎想像,極致性價比王者重返台灣市場

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

字符串太占內存了,我想了各種奇思淫巧對它進行壓縮_潭子電動車

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

一:背景

1. 講故事

在我們的一個全內存項目中,需要將一家大品牌店鋪小千萬的trade灌入到內存中,大家知道trade中一般會有訂單來源,省市區 ,當把這些字段灌進去后,你會發現他們特別侵蝕內存,因為都是字符串類型,不知道大家對內存侵蝕性是不是很清楚,我就問一個問題。

Question: 一個空字符串佔用多大內存? 你知道嗎?

思考之後,下面我們就一起驗證下,使用windbg去託管堆一查究竟,代碼如下:


        static void Main(string[] args)
        {
            string s = string.Empty;

            Console.ReadLine();
        }

0:000> !clrstack -l
OS Thread Id: 0x308c (0)
        Child SP               IP Call Site
ConsoleApp6.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp6\Program.cs @ 19]
    LOCALS:
        0x00000087391febd8 = 0x000002605da91420
0:000> !DumpObj /d 000002605da91420
Name:        System.String
String:      
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9eb2b85a0  4000281        8         System.Int32  1 instance                0 m_stringLength
00007ff9eb2b6838  4000282        c          System.Char  1 instance                0 m_firstChar
00007ff9eb2b59c0  4000286       d8        System.String  0   shared           static Empty
                                 >> Domain:Value  000002605beb2230:NotInit  <<
0:000> !objsize 000002605da91420
sizeof(000002605da91420) = 32 (0x20) bytes (System.String)

從圖中你可以看到,僅僅一個空字符串就要佔用 32byte,如果500w個空字符串就是: 32byte x 500w = 152M,是不是不算不知道,一算嚇一跳。。。 這還僅僅是一個什麼都沒有的空字符串哦。

2. 回歸到Trade

問題也已經擺出來了,接下來回歸到Trade中,為了方便演示,先模擬以文件的形式從數據庫讀取20w的trade。

    class Program
    {
        static void Main(string[] args)
        {
            var trades = Enumerable.Range(0, 20 * 10000).Select(m => new Trade()
            {
                TradeID = m,
                TradeFrom = File.ReadLines(Environment.CurrentDirectory + "//orderfrom.txt")
                                 .ElementAt(m % 4)
            }).ToList();

            GC.Collect();  //方便測試,把臨時變量清掉
            Console.WriteLine("執行成功");
            Console.ReadLine();
        }
    }

    class Trade
    {
        public int TradeID { get; set; }
        public string TradeFrom { get; set; }
    }

然後用windbg去跑一下託管堆,再量一下trades的大小。


0:000> !dumpheap -stat
Statistics:
              MT    Count    TotalSize Class Name
00007ff9eb2b59c0   200200      7010246 System.String

0:000> !objsize 0x000001a5860629a8
sizeof(000001a5860629a8) = 16097216 (0xf59fc0) bytes (System.Collections.Generic.List`1[[ConsoleApp6.Trade, ConsoleApp6]])

從上面輸出中可以看到託管堆有200200 = 20w(程序分配)+ 200(系統分配)個,然後再看size: 16097216/1024/1024= 15.35M,這就是展示的所有原始情況。

二:壓縮技巧分析

1. 使用字典化處理

其實在託管堆上有20w個字符串,但你仔細觀察一下會發現其實就是4種狀態的重複显示,要麼一淘,要麼淘寶。。。這就給了我優化機會,何不在獲取數據的時候構建好OrderFrom的字典,然後在trade中附增一個TradeFromID記錄字典中的映射值,因為特徵值少,用byte就可以了,有了這個思想,可以把代碼修改如下:

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污


    class Program
    {
        public static Dictionary<int, string> orderfromDict = new Dictionary<int, string>();

        static void Main(string[] args)
        {
            var trades = Enumerable.Range(0, 20 * 10000).Select(m =>
            {
                var tradefrom = File.ReadLines(Environment.CurrentDirectory + "//orderfrom.txt")
                                 .ElementAt(m % 4);

                var kv = orderfromDict.FirstOrDefault(k => k.Value == tradefrom);

                if (kv.Key == 0)
                {
                    orderfromDict.Add(orderfromDict.Count + 1, tradefrom);
                }

                var trade = new Trade() { TradeID = m, TradeFromID = (byte)kv.Key };

                return trade;

            }).ToList();

            GC.Collect();  //方便測試,把臨時變量清掉

            Console.WriteLine("執行成功");

            Console.ReadLine();
        }
    }

    class Trade
    {
        public int TradeID { get; set; }

        public byte TradeFromID { get; set; }

        public string TradeFrom
        {
            get
            {
                return Program.orderfromDict[TradeFromID];
            }
        }
    }

代碼還是很簡單的,接下來用windbg看一下空間到底壓縮了多少?

0:000> !dumpheap -stat
Statistics:
              MT    Count    TotalSize Class Name
00007ff9eb2b59c0      204        10386 System.String

0:000> !clrstack -l
OS Thread Id: 0x2ce4 (0)
        Child SP               IP Call Site
ConsoleApp6.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp6\Program.cs @ 42]
    LOCALS:
        0x0000006f4d9ff078 = 0x0000016fdcf82ab8

0000006f4d9ff288 00007ff9ecd96c93 [GCFrame: 0000006f4d9ff288] 
0:000> !objsize 0x0000016fdcf82ab8
sizeof(0000016fdcf82ab8) = 6897216 (0x693e40) bytes (System.Collections.Generic.List`1[[ConsoleApp6.Trade, ConsoleApp6]])

從上面的輸出中可以看到,託管堆上string現在是:204 = 4(程序分配) + 200(系統分配)個,這4個就是字典中的4個哦,空間的話:6897216 /1024/1024= 6.57M,對應之前的 15.35M優化了將近60%。

雖然優化了60%,但這種優化是破壞性的優化,需要修改我的Trade結構,同時還要定義個Dictionary,而且還有不小幅度的修改業務邏輯,大家都知道線上的代碼是能不改則不改,不改肯定沒錯,改出問題肯定是你兜着走,是吧,那問題就來了,如何最小化的修改而且還能壓縮空間,有這樣兩全其美的事情嗎???

2. 利用字符串駐留池

貌似一說出來,大家都如夢初醒,駐留池的出現就是為了解決這個問題,CLR會在內部維護了一個我剛才定義的字典機制,重複的字符串就不需要在堆上再次分配,直接存它的引用地址即可,如果你不清楚駐留池,建議看一下我這篇: https://www.cnblogs.com/huangxincheng/p/12799736.html

接下來只需要在tradefrom 字段包一層 string.Intern 即可,改動不要太小,代碼如下:


        static void Main(string[] args)
        {
            var trades = Enumerable.Range(0, 20 * 10000).Select(m => new Trade()
            {
                TradeID = m,
                TradeFrom = string.Intern(File.ReadLines(Environment.CurrentDirectory + "//orderfrom.txt")
                                 .ElementAt(m % 4)),   //包一層 string.Intern
            }).ToList();

            GC.Collect();  //方便測試,把臨時變量清掉
            Console.WriteLine("執行成功");
            Console.ReadLine();
        }

然後用windbg抓一下託管堆。


0:000> !dumpheap -stat 
Statistics:
              MT    Count    TotalSize Class Name
00007ff9eb2b59c0      204        10386 System.String

0:000> !clrstack -l
OS Thread Id: 0x13f0 (0)
        Child SP               IP Call Site

ConsoleApp6.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp6\Program.cs @ 27]
    LOCALS:
        0x0000005e4d3ff0a8 = 0x000001f8a15129a8

0000005e4d3ff2b8 00007ff9ecd96c93 [GCFrame: 0000005e4d3ff2b8] 
0:000> !objsize 0x000001f8a15129a8
sizeof(000001f8a15129a8) = 8497368 (0x81a8d8) bytes (System.Collections.Generic.List`1[[ConsoleApp6.Trade, ConsoleApp6]])

觀察后發現,當用了駐留池之後空間為: 8497368 /1024/1024 =8.1M,你可能有疑問,為什麼和字典化相比內存要大24%呢? 仔細觀察你會發現,當用駐留池后,List<Trade> 中的TradeFrom存的是string在堆中的內存地址,在x64機器上要佔用8個字節,而字典化方式內存堆上Trade是不分配TradeFrom,而是用了一個byte來替代,總體來說相當於一個trade省了7byte的空間,然後用windbg看一下。


0:000> !da -length 1 -details 000001f8b16f9b68
Name:        ConsoleApp6.Trade[]
Size:        2097176(0x200018) bytes
Array:       Rank 1, Number of elements 262144, Type CLASS

    Fields:
                      MT    Field   Offset                 Type VT     Attr            Value Name
        00007ff9eb2b85a0  4000001       10             System.Int32      1     instance                    0     <TradeID>k__BackingField
        00007ff9eb2b59c0  4000002        8            System.String      0     instance     000001f8a1516030     <TradeFrom>k__BackingField

0:000> !DumpObj /d 000001f8a1516030
Name:        System.String
String:      WAP

可以看到, 000001f8a1516030 就是 堆上 string=Wap的引用地址,這個地址佔用了8byte空間。

再回頭dump一下使用字典化方式的Trade,可以看到它是沒有 <TradeFrom>k__BackingField 字段的。


0:000> !da -length 1 -details 000001ed52759ac0
Name:        ConsoleApp6.Trade[]
Size:        262168(0x40018) bytes
Array:       Rank 1, Number of elements 32768, Type CLASS
    Fields:
                      MT    Field   Offset                 Type VT     Attr            Value Name
        00007ff9eb2b85a0  4000002        8             System.Int32      1     instance                    0     <TradeID>k__BackingField
        00007ff9eb2b7d20  4000003        c              System.Byte      1     instance                    0     <TradeFromID>k__BackingField


三:總結

大家可以根據自己的情況使用,使用駐留池方式是改變最小的,簡單粗暴,自己構建字典化雖然最省內存,但需要修正業務邏輯,這個風險自擔哦。。。

如您有更多問題與我互動,掃描下方進來吧~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

C#中的TemplateMethod模式_包裝設計

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

一個真實的故事

大學的時候就開過一門課程,講設計模式,可是大學生沒什麼編程實踐經驗,在大學裏面聽設計模式的感覺,就像聽天書。聽着都有道理,可是完全領會不到其中的奧妙,大抵原因就在於沒有走過彎路,沒有吃過設計不當的虧。古人云,“操千曲而後曉聲,觀千劍而後識器”,誠不欺我。
 
博主在之前的某個項目中,設計出了一些工具類,像屬性窗口,錯誤提示窗口,還有一個窗口管理類管理它們,當時我實現工具保存時候的代碼是這樣的:

    class WindowManager
    {
        private List<ITool> _Tools = new List<ITool>();        

        public void AddTool(ITool tool)
        {
            _Tools.Add(tool);
        }

        public void SaveAllTools()
        {
            foreach(var tool in _Tools)
            {
                tool.Save();
            }
        }
    }

    interface ITool
    {
        bool BeforeSave();
        void Save();
        void AfterSave();
    }

    class PropertyWindow : ITool
    {
        public bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        public void Save()
        {
            if (BeforeSave())
            {
                //do save
                AfterSave();
            }
        }

        public void AfterSave()
        {

        }
    }

    class ErrorLis : ITool
    {
        public bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        public void Save()
        {
            if (BeforeSave())
            {
                //do save
                AfterSave();
            }
        }

        public void AfterSave()
        {

        }
    }

當時博主對這段代碼還挺滿意,完全沒有看出這兒有什麼問題,覺得這簡直寫的太OO了,有類,有接口,有針對接口編程,至於新加的工具類,也不會影響原來的代碼,簡直太符合開閉原則了。老鐵,沒毛病!
 
好日子就這麼繼續下去,每當需要新添加一個工具,我就新加一個類,在類裏面實現Save的邏輯,直到有一天,添加了一個ResourceControl

    class ResourceControl : ITool
    {
        public bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        public void Save()
        {
            if (!BeforeSave())
            {
                //do save
                AfterSave();
            }
        }

        public void AfterSave()
        {

        }
    }

 
在它的save裏面,我把if(BeforeSave())寫成了if(!BeforeSave())。。。
於是,我又額外花了一些時間來找到這個問題,修改它並在下次添加新類的時候戰戰兢兢提醒自己不要犯這種低級的錯誤。那麼,我們有沒有好的辦法來解決這個問題呢?

問題分析

其實就算每次添加新類的時候我們都能仔細的小心避免維護相同的邏輯,這段代碼的設計也還是有可以改進的地方,比如,BeforeSave和AfterSave在這裏作為接口ITool的一部分而公開,意味着客戶代碼可以自由的調用BeforeSave和AfterSave,然而這很可能並不是代碼作者的本意,畢竟,不調用Save而單獨調用BeforeSave和AfterSave有什麼意義呢?讓客戶能夠看到更多不必要的方法,增加了客戶錯誤使用接口的可能性,不是么?
 
綜上所述,我們需要解決的問題如下:

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

  • 抽象出Save, BeforeSave和AfterSave的邏輯關係,在一個地方固定下來,確保新增加的類所實現的這三個方法,都能自動具有這種邏輯關係。
  • 對客戶代碼隱藏不必要的接口。
     
    這種場景下面,我們需要用到設計模式中的TemplateMethod(模版方法)模式。
     

TemplateMethod模式

在WIKI上面,TemplateMethod模式的定義如下,
In software engineering, the template method pattern is a behavioral design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets one redefine certain steps of an algorithm without changing the algorithm’s structure.

大概意思就是,模版方法模式是一種行為類設計模式,允許軟件在更高的層次定義程序骨架,但是可以在子類推遲實現某些步驟。
 
類圖如下:

這完全符合我們的需求,讓我們試着修改我們的代碼。
 

使用TemplateMethod重新實現的代碼

    class WindowManager
    {
        private List<AbstractTool> _Tools = new List<AbstractTool>();        

        public void AddTool(AbstractTool tool)
        {
            _Tools.Add(tool);
        }

        public void SaveAllTools()
        {
            foreach(var tool in _Tools)
            {
                tool.Save();
            }
        }
    }

    abstract class AbstractTool
    {
        protected abstract bool BeforeSave();
        protected abstract void DoSave();
        protected abstract void AfterSave();
        public void Save()
        {
            if(!BeforeSave())
            {
                DoSave();
                AfterSave();
            }

        }        
    }

    class PropertyWindow : AbstractTool
    {
        protected override bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        protected override void DoSave()
        {
            
        }

        protected override void AfterSave()
        {

        }
    }

    class ErrorLis : AbstractTool
    {
        protected override bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        protected override void DoSave()
        {

        }

        protected override void AfterSave()
        {

        }
    }

從上面我們可以看到,我們用一個抽象類AbstractTool代替之前的ITool接口,抽象類和接口的一個區別就是,抽象類可以在其中嵌入某些邏輯,所以我們在Save這個公共的非虛方法中,完全實現了我們的BeforeSave和AfterSave邏輯,僅僅留下了BeforeSave,AfterSave和DoSave給子類覆蓋。這樣我們得到的好處是:

  • 抽象類只公開了一個Save方法,所以客戶代碼不用擔心會調用其他錯誤的方法。
  • 抽象類完全固定了Save邏輯,先調用BeforeSave檢查,之後執行DoSave進行具體的Save事項,最後進行AfterSave行為。子類只需要重新依據子類的需求覆蓋這三個虛方法即可。新添加的工具類,只要覆蓋這三個虛方法,至於虛方法之間的邏輯,抽象類已經固定,不用擔心。

結論

“紙上得來終覺淺,絕知此事要躬行”,祖宗的話,不會錯的,如果沒有一定的編程實踐和總結,是沒有辦法領悟設計模式的,博主也是通過之前那個例子才領悟到TemplateMethod模式的妙用。希望大家多多編程,早日領悟。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

可以Postman,也可以cURL.進來領略下cURL的獨門絕技_台中搬家公司

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

文章已經收錄在 Github.com/niumoo/JavaNotes ,更有 Java 程序員所需要掌握的核心知識,歡迎Star和指教。
歡迎關注我的公眾號,文章每周更新。

cURL 是一個開源免費項目,主要是命令行工具 cURL 和 libcurl,cURL 可以處理任何網絡傳輸協議,但是不涉及任何具體的數據處理

cURL 支持的通信協議非常豐富,如 DICT,FILE,FTP,FTPS,GOPHER,HTTP,HTTPS,IMAP,IMAPS,LDAP,LDAPS,MQTT,POP3,POP3S,RTMP, RTMPS,RTSP,SCP,SFTP,SMB,SMBS,SMTP,SMTPS,TELNET 以及 TFTP。查看 cURL 源代碼可以訪問官方 Github。

如果安裝 cURL 呢?

ubuntu / Debian.

sudo apt install curl

CentOS / Fedora.

sudo yum install curl

Windows.

如果你已經安裝了 Git,那麼 Git Bash 自帶 cURL . 如果作為開發者你 git 都沒有,那麼只能官方手動下載。

1. 請求源碼

直接 curl 。

$ curl http://wttr.in/

上面請求的示例網址是一個天氣網站,很有意思,會根據你的請求 ip 信息返回你所在位置的天氣情況。

寫這篇文字時我所在的上海正在下雨,窗外飄雨無休無止。

2. 文件下載

使用 -o 保存文件,類似於 wget 命令,比如下載 README 文本保存為 readme.txt 文件。如果你需要自定義文件名,可以使用 -O自定使用 url 中的文件名。

$ curl -o readme.txt https://mirrors.nju.edu.cn/kali/README
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   159  100   159    0     0   1939      0 --:--:-- --:--:-- --:--:--  1939

下載文件會显示下載狀態,如數據量大小、傳輸速度、剩餘時間等。可以使用 -s 參數禁用進度表。

$ curl -o readme.txt https://mirrors.nju.edu.cn/kali/README
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   159  100   159    0     0   1939      0 --:--:-- --:--:-- --:--:--  1939
$ 
$ curl -o readme.txt https://mirrors.nju.edu.cn/kali/README -s

也可以使用 --process-bar 參數讓進度表显示為進度條。

$ curl -o readme.txt https://mirrors.nju.edu.cn/kali/README --progress-bar
########################################################################################## 100.0%

cURL 作為強大的代名詞,斷點續傳自然手到擒來,使用 -C - 參數即可。下面是斷點續傳下載 ubuntu20.04 鏡像的例子。

$ curl -O https://mirrors.nju.edu.cn/ubuntu-releases/20.04/ubuntu-20.04-desktop-amd64.iso --progress-bar
##                                                                                               1.7%
^C
$ curl -C - -O https://mirrors.nju.edu.cn/ubuntu-releases/20.04/ubuntu-20.04-desktop-amd64.iso --progress-bar
###                                                                                              2.4%
^C
$ curl -C - -O https://mirrors.nju.edu.cn/ubuntu-releases/20.04/ubuntu-20.04-desktop-amd64.iso --progress-bar
###                                                                                               2.7%
^C
$ 

什麼?下載時不想佔用太多網速?使用 --limit-rate 限個速吧。

curl -C - -O https://mirrors.nju.edu.cn/ubuntu-releases/20.04/ubuntu-20.04-desktop-amd64.iso --limit-rate 100k

什麼?你又要從 FTP 服務器下載文件了?不慌。

curl -u user:password -O ftp://ftp_server/path/to/file/

3. Response Headers

使用 -i 參數显示 Response Headers 信息。使用 -I 可以只显示 Response Headers 信息。

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

$ curl -I http://wttr.in
HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Sat, 30 May 2020 09:57:03 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 8678
Connection: keep-alive
Access-Control-Allow-Origin: *

4. 請求方式(GET/POST/…)

使用 -X 輕鬆更改請求方式。

$ curl -X GET http://wttr.in
$ curl -X POST http://wttr.in
$ curl -X PUT http://wttr.in
...

5. 請求參數

以傳入參數 name 值為 未讀代碼 為例。

Get 方式參數直接url拼接參數。

$ curl -X GET http://wttr.in?name=未讀代碼

Post 方式使用 --data 設置參數。

$ curl -X POST --data "name=未讀代碼" http://wttr.in

請求時也可以自定義 header 參數,使用 --harder 添加。

$ curl --header "Content-Type:application/json" http://wttr.in

6. 文件上傳

cURL 的強大遠不止此,表單提交,上傳文件內容也不在話下,只需要使用 -F 或者 -D參數,-F 會自動加上請求頭 Content-Type: multipart/form-data ,而 -D 則是 Content-Type : application/x-www-form-urlencoded.

比如上傳一個 protrait.jpg 圖片。

$ curl -F profile=@portrait.jpg https://example.com/upload

提交一個具有 name 和 age 參數的 form 表單。

curl -F name=Darcy -F age=18 https://example.com/upload

參數對應的內容也可以從文件中讀取。

curl -F "content=<達西的身世.txt" https://example.com/upload

上傳時同時指定內容類型。

curl -F "content=<達西的身世.txt;type=text/html" https://example.com/upload

上傳文件的和其他參數一起。

curl -F 'file=@"localfile";filename="nameinpost"' example.com/upload

7. 網址通配

cURL 可以實現多個網址的匹配,你可以使用 {} 結合逗號分割來標識使用 url 中的某一段,也可以使用 [] 來表示範圍參數。

# 請求 www.baidu.com 和  pan.baidu.com 和 fanyi.baidu.com
$ curl http://{www,pan,fanyi}.baidu.com
# 虛構網址1-10開頭的baidu.com,然後請求
$ curl http://[1-10].baidu.com
# 虛構網址a-z開頭的baidu.com,然後請求
$ curl http://[a-z].baidu.com

這種方式有時候還是很有用處的,比如說你發現了某個網站的 url 規律。

8. 使用 cookie

請求時使用 -c 參數存儲響應的 cookie,使用 -b 可以在請求時帶上指定 cookie.

$ curl -c wdbyte_cookies http://www.wdbyte.com
$ curl -b wdbyte_cookes http://www.wdbyte.com

總結

以上就是 cURL 的常見用法了,最後告訴你一個小技巧,Chrome、Firefox 等瀏覽器可以直接拷貝請求為 cURL 語句。保存之後下次請求測試非常方便。

參考資料

  1. https://curl.haxx.se/docs/manpage.html

最後的話

文章已經收錄在 Github.com/niumoo/JavaNotes ,歡迎Star和指教。更有一線大廠面試點,Java程序員需要掌握的核心知識等文章,也整理了很多我的文字,歡迎 Star 和完善,希望我們一起變得優秀。

文章有幫助可以點個「」或「分享」,都是支持,我都喜歡!
文章每周持續更新,要實時關注我更新的文章以及分享的乾貨,可以關注「 未讀代碼 」公眾號或者我的博客。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

3歲男童掉入15米深井底 鑽繩套成功自救_網頁設計公司

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

新聞出處:北京新浪網    





放大圖
看更多圖片

  本報訊(記者 陳海峰 通訊員 馬世華 文圖) 本報商丘訊 柘城縣梁庄鄉一名3歲男孩在捉蝴蝶時,不慎掉入15米深的機井中。由於井口直徑僅40厘米,救援人員根本無法下井。消防官兵衹好將救援繩子打結,慢慢放入井內,現場指導落井孩子自己操作,半個小時後,孩子被救上來。經醫生檢查,小孩衹受了些擦傷,無生命危險。

  3歲娃掉入15米深機井里

  3月11日下午,提起兒子掉入井里的那一幕,柘城縣梁庄鄉的劉先生仍然心有余悸。

  據他介紹,3月8日下午,兒子和鄰居的一個同齡孩子出去玩耍,誰知,一會兒兩人便不見了蹤影。正在家人尋找時,鄰居的孩子跑回來說他兒子在捉蝴蝶時,掉進了麥地的井里。

  “我一路找一路喊我兒子名字,聽到他的哭聲,立即趕過去,看到兒子正在井下,水淹到孩子胸口處。”劉先生說,他趕緊撥打了119。

  接到求助電話後,柘城縣消防官兵迅速趕赴現場。

  消防員在現場看到,該機井直徑約40厘米,深約15米,水面距離地面約14米。借著燈光觀察,可見男孩上半身卡在井下水面之上,情況萬分危急。

  消防員指導協助

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

  孩子自救成功

  消防官兵迅速利用壓縮氣瓶向井底輸送新鮮空氣,並對落井小孩持續喊話:“小朋友,不要怕,叔叔馬上救妳上來。”

  由於井口太窄,救援人員無法下井。於是,救援人員制訂了三個救援方案:一是利用井下救援鉤進行救援﹔二是利用結繩法打套進行救援﹔三是利用挖掘機挖土救援。

  由於孩子被卡在井壁內,救援器材無法展開,第一個救援方案未能成功﹔此時,救援人員調來了一台大型挖掘機,第三個方案和第二個方案同時進行。

  救援人員將繩索打個套,慢慢放入井內,接近落井小孩,同時向其喊話:“小朋友,不要害怕,叔叔馬上就救妳上來,妳配合一下,把妳的雙手合在一塊,伸入繩圈,然後抓住繩圈上面的繩結。小朋友,不要害怕,慢慢來,妳馬上就能上來了。”

  聽到消防人員的喊話後,小孩將手套入繩套中,救援人員慢慢地收緊繩子。但剛向上提升約兩米,小孩的頭部被井壁突出部分卡住,無法繼續上升。哇哇的哭聲又從井底傳出。

  “小朋友,不要怕,妳馬上就能出來了,請按照叔叔說的做,把妳的頭慢慢地向前趴,向妳手臂的方向……”消防隊員小心翼翼地拉著繩子,耐心地向孩子喊話。

  聽到喊話,小孩的情緒好轉,並按照消防隊員的方法將頭慢慢靠近自己的手臂,經過一番努力,落井小孩終於通過了那段狹窄的井壁,慢慢地被拉了上來。

  劉先生夫婦看到孩子被救了上來,一把抱住孩子,哭了起來。經醫生檢查,孩子的臉部和右腳部有些擦傷,生命體征完好。

(原標題:真能!3歲娃15米深井底鑽繩套自救)

詳全文 3歲男童掉入15米深井底 鑽繩套成功自救-兩岸新聞-新浪新聞中心 http://news.sina.com.tw/article/20130312/9135110.html

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

泳池變態男猥褻小六童 還說:你可以摸我_網頁設計

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

※推薦評價好的iphone維修中心

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

台中出現泳池變態男,竟然在公共浴室洗澡時,伸手撫摸旁邊小六男童的下體,甚至還開口跟他說「你也可以摸我的」,男童嚇得大聲叫阿公,男子這才趕緊收拾東西離開,看到男童追了出來,他還警告男童不准跟著他。事後男童身心受傷,跟媽媽哭訴後,家人趕緊帶著他報警,並且調閱監視器,揪出這名泳池色狼。

游泳池浴室,卡其色上衣、綠色短褲男子匆忙跑了出來,快步離開,後面1名男童顧不得頭髮還濕濕的,也跟著衝出來找人,原來這名男子,竟然在游泳池浴室伸手猥褻他!

受害男童母親說:「電話中他就開始哭了,整個崩潰大哭,然後就跟我講說,他摸我的小鳥,然後我把他手撥開。」

男童身心靈受傷,事後打電話給媽媽時崩潰大哭,家人這才知道,他在游泳池遭到猥褻。8日傍晚,這名小六男童跟阿公一起到豐原這間游泳池游泳,結束後在大眾澡堂淋浴時,1名男子突然伸手抓他下體,還告訴他說「你也可以摸我的啊」,男童嚇得大叫阿公求救,男子才嚇得匆忙離開,看到男童跑出浴室,還轉頭警告他不准跟上來。

事後家人帶著男童報警,調閱監視器,男童一眼就認出猥褻男子。警方依照車牌,將他約談到案到案,男子坦承有對男童伸手,被依妨害性自主罪名,函送法辦。

網頁設計最專業,超強功能平台可客製化

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

家長疏忽?6歲童飯店獨棟villa泳池溺水 送醫救回_網頁設計公司

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

台東縣池上鄉一家五星級渡假村今天傍晚驚傳6歲男童險溺斃別墅型飯店水池,消防人員趕抵,男童已無生命跡象,經送往關山慈濟醫院急救,才恢復心跳和呼吸,住院觀察中。

救護人員緊急將溺水男童送往關山慈濟醫院,經搶救恢復生命跡象。(記者陳賢義翻攝)

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

男童溺水水池位於飯店獨棟villa外。(記者陳賢義翻攝)

這起溺水意外發生在今天傍晚5點26分左右,台東縣消防局勤指中心獲報,池上鄉一家五星級渡假村,有個6歲小朋友在飯店房間水池溺水,溺者已救出水池,立即出動池上消防分隊及通報關山高救隊馳援,抵達現場發現是飯店獨棟villa,男童已無生命跡象,施予救護處置,旋即送往關山慈濟急救。

消防單位說,事發當時,男童家長也在附近,可能是疏於注意,才會導致意外發生。警方則說,男童經搶救,恢復心跳和呼吸,但仍須住院觀察,家長是否涉及兒少法或相關刑責,須再調查釐清。

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

DDD之3實體和值對象_貨運

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

圖中是一個別墅的模型,代表實體,可以真實的看得到。那麼在DDD設計方法論中,實體和值對象是什麼呢?

背景

實體和值對象是領域模型中的領域對象,是組成領域模型的基礎單元,一起實現實體最基本的核心領域邏輯。

那麼問題來了:

1, 他兩在領域模型中的作用是什麼?

2,在系統中跟代碼模型和數據模型是怎麼對應的?

搞清楚這兩個問題很重要。回答問題是需要有知識基礎的,先來捋清楚這兩個概念的定義和內涵。然後在小結部分我們來回答這兩個問題。

實體

定義: DDD中的一類對象,擁有唯一標識符,經歷各種狀態變更后仍然可以保持一致,對這類對象而言,重要的是延續性和標識,(對象的延續性和標識可以超出軟件的生命周期)而非屬性。

形態:不同的設計過程中,形態不一致。

值對象

定義:通過對象的屬性值來識別的對象是值對象,它將多個相關屬性組合為一個概念整體。它是沒有標識符的對象;
**

特點:值對象描述了領域中的一件東西,這個東西是不可變的,它將不同的相關屬性組合成了一個概念整體,當度量和描述改變的時候,它可以用另外一個值對象替換,並進行相等性比較而不會帶來副作用;
**
**
簡單來說: 值對象本質就是一個集合;
**
意義:領域建模過程中,值對象可以保證屬性歸類的清晰和概念的完整性;
**

**
**

上圖中: 如果把省市區地址放在人員實體中,會顯得屬性很多很零碎。 推薦的做法是把省市區地址構成一個集合,即地址值對象;

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

例子:人員地址案例;

缺點:如果實體引用的值對象過多,會導致實體堆積一批缺乏概念完整性的屬性,值對象失去了業務含義,操作起來不方便;

實體PK值對象

DDD提倡從領域模型設計出發,而不是先設計數據模型;

小結

首先明確了實體和值對象的概念,以及在不同的設計階段的形態。然後通過一個例子展示了實體和值對象的概念和使用;

這是一個從業務模型向系統模型落地過程,考驗的是設計能力,我們應該結合自己的業務場景,選擇合適的方法進行微服務設計。

最後我來回答一下在背景部分拋出的兩個問題?

1, 實體和值對象在領域模型中的作用是什麼?

2,在系統中跟代碼模型和數據模型是怎麼對應的?

經過上面的分析,我的回答如下:

希望大家都理解好DDD的實體和值對象,設計出高度靈活的代碼;

原創不易,關注誠可貴,轉發價更高!轉載請註明出處,讓我們互通有無,共同進步,歡迎溝通交流。
我會持續分享Java軟件編程知識和程序員發展職業之路,歡迎關注,我整理了這些年編程學習的各種資源,關注公眾號‘李福春持續輸出’,發送’學習資料’分享給你!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

LeetCode 74,直擊BAT經典面試題_網頁設計公司

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是LeetCode專題43篇文章,我們今天來看一下LeetCode當中的74題,搜索二維矩陣,search 2D Matrix。

這題的官方難度是Medium,通過率是36%,和之前的題目不同,這題的點贊比非常高,1604個贊,154個反對。可見這題的質量還是很高的,事實上也的確如此,這題非常有意思。

題意

這題的題意也很簡單,給定一個二維的數組matrix和一個整數target,這個數組當中的每一行和每一列都是遞增的,並且還滿足每一行的第一個元素大於上一行的最後一個元素。要求我們返回一個bool變量,代表這個target是否在數組當中。

也就是說這個是一個典型的判斷元素存在的問題,我們下面來看看兩個樣例:

Input:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 3
Output: true
Input:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 13
Output: false

題解

這題剛拿到手可能會有些蒙,我們當然很容易可以看出來這是一個二分的問題,但是我們之前做的二分都是在一個一維的數組上,現在的數據是二維的,我們怎麼二分呢?

我們仔細閱讀一下題意,再觀察一下樣例,很容易發現,如果一個二維數組滿足每一行和每一列都有序,並且保證每一行的第一個元素大於上一行的最後一個元素,那麼如果我們把這個二維數組reshape到一維,它依然是有序的。

比如說有這樣一個二維數組:

[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]

它reshape成一維之後會變成這樣:

[1, 2, 3, 4, 5, 6, 7, 8, 9]

reshape是numpy當中的說法,也可以簡單理解成把每一行串在一起。所以這題最簡單的做法就是把矩陣降維,變成一位的數組之後再通過二分法來判斷元素是否存在。如果偷懶的話可以用numpy來reshape,如果不會numpy的話,可以看下我之前關於numpy的教程,也可以自己用循環來處理。

reshape之後就是簡單的二分了,完全沒有任何難度:

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        import numpy as np
        arr = np.array(matrix)
        # 通過numpy可以直接reshape
        arr = arr.reshape((-1, ))
        l, r = 0, arr.shape[0]
        if r == 0:
            return False
        # 套用二分
        while l+1 < r:
            m = (l + r) >> 1
            if arr[m] <= target:
                l = m
            else:
                r = m
        return arr[l] == target

正經做法

引入numpy reshape只是給大家提供一個解決的思路,這顯然不是一個很好的做法。那正確的方法應該是怎樣的呢?

還是需要我們對問題進行深入分析,正向思考感覺好像沒什麼頭緒,我們可以反向思考。這也是解題常用的套路,假設我們已經知道了target這個数字存在矩陣當中,並且它的行號是i,列號是j。那麼根據題目當中的條件,我們能夠得出什麼結論呢?

我們分析一下元素的大小關係,可以得出行號小於i的所有元素都小於它,行號大於i的所有元素都大於它。同行的元素列號小於j的元素小於它,列號大於j的元素大於它。

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

也就是說,行號i就是一條隱形的分界線,將matrix分成了兩個部分,i上面的小於target,i下方的大於target。所以我們能不能通過二分找到這個i呢?

想到這裏就很簡單了,我們可以通過每行的最後一個元素來找到i。對於一個二維數組而言,每行的最後一個元素連起來就是一個一維的數組,就可以很簡單地進行二分了。

找到了行號i之後,我們再如法炮製,在i行當中進行二分來查找j的位置。找到了之後,再判斷matrix[i][j]是否等於target,如果相等,那麼說明元素在矩陣當中。

整個的思路應該很好理解,但是實現的時候有一個小小的問題,就是我們查找行的時候,找的是大於等於target的第一行的位置。也就是說我們查找的是右端點,那麼二分的時候維護的是一個左開右閉的區間。在邊界的處理上和平常使用的左閉右開的寫法相反,注意了這點,就可以很順利地實現算法了:

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        n = len(matrix)
        if n == 0:
            return False
        
        m = len(matrix[0])
        if m == 0:
            return False
        
        # 初始化,左開右閉,所以設置成-1, n-1
        l, r = -1, n-1
        
        while l+1 < r:
            mid = (l + r) >> 1
            # 小於target的時候移動左邊界
            if matrix[mid][m-1] < target:
                l = mid
            else:
                r = mid
                
        row = r
        
        # 正常的左閉右開的二分
        l, r = 0, m
        
        while l+1 < r:
            mid = (l + r) >> 1
            if matrix[row][mid] <= target:
                l = mid
            else:
                r = mid
                
        return matrix[row][l] == target

我們用了兩次二分,查找到了結果,每一次二分都是一個O(logN)的算法,所以整體也是log級的算法。

優化

上面的算法沒有問題,但是我們進行了兩次二分,感覺有些麻煩,能不能減少一次,只使用一次二分呢?

如果想要只使用一次二分就找到答案,也就是說我們能找到某個方法來切分整個數組,並且切分出來的數組也存在大小關係。這個條件是使用二分的基礎,必須要滿足。

我們很容易在數組當中找到這樣的切分屬性,就是元素的位置。在矩陣元素的問題當中,我們經常用到的一種方法就是對矩陣當中的元素進行編號。比如說一個點處於i行j列,那麼它的編號就是i * m + j,這裏的m是每行的元素個數。這個編號其實就是將二維數組壓縮到一維之後元素的下標。

我們可以直接對這個編號進行二分,編號的取值範圍是確定的,是[0, mn)。我們有了編號之後,可以還原出它的行號和列號。而且根據題目中的信息,我們可以確定這個矩陣當中的元素按照編號也存在遞增順序。所以我們可以大膽地使用二分了:

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        n = len(matrix)
        if n == 0:
            return False
        
        m = len(matrix[0])
        if m == 0:
            return False
        
        l, r = 0, m*n
        
        while l+1 < r:
            mid = (l + r) >> 1
            # 還原行號和列號
            x, y = mid // m, mid % m
            if matrix[x][y] <= target:
                l = mid
            else:
                r = mid
        return matrix[l // m][l % m] == target

這樣一來我們的代碼大大簡化,並且代碼運行的效率也提升了,要比使用兩次二分的方法更快。

總結

這道題到這裏就結束了,這題難度並不大,想出答案來還是不難的。但是如果在面試當中碰到,想要第一時間想到最優解法還是不太容易。這一方面需要我們積累經驗,看到題目大概有一個猜測應該使用什麼類型的算法,另一方面也需要我們對問題有足夠的理解和分析,從而讀到題目當中的隱藏信息

關於這題還有一個變種,就是去掉其中每行的第一個元素大於上一行最後一個元素的限制。那麼矩陣當中元素按照編號順序遞增的性質就不存在了,對於這樣的情況, 我們該怎麼樣運用二分呢?這個問題是LeetCode的240題,感興趣的話可以去試着做一下這題,看看究竟解法有多大的變化。

如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

數據結構:用實例分析ArrayList與LinkedList的讀寫性能_包裝設計

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

目錄

  • 背景
  • ArrayList
  • LinkedList
  • 實例分析
    • 1、增加數據
    • 2、插入數據
    • 3、遍曆數據
      • 3.1、LinkedList遍歷改進
  • 總結

背景

ArrayList與LinkedList是Java編程中經常會用到的兩種基本數據結構,在書本上一般會說明以下兩個特點:

  • 對於需要快速隨機訪問元素,應該使用ArrayList
  • 對於需要快速插入,刪除元素,應該使用LinkedList

該文通過實際的例子分析這兩種數據的讀寫性能。

ArrayList

ArrayList是實現了基於動態數組的數據結構:

private static final int DEFAULT_CAPACITY = 10;
...
transient Object[] elementData;
...
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

LinkedList

LinkedList是基於鏈表的數據結構。

private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
...    
transient Node<E> first;
transient Node<E> last;
...
private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

實例分析

  • 通過對兩個數據結構分別增加、插入、遍歷進行讀寫性能分析
1、增加數據
public class ArrayListAndLinkList {
    public final static int COUNT=100000;
    public static void main(String[] args) {

        // ArrayList插入
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        Long start = System.currentTimeMillis();
        System.out.println("ArrayList插入開始時間:" + sdf.format(start));

        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 0; i < COUNT; i++) {
            arrayList.add(i);
        }

        Long end = System.currentTimeMillis();
        System.out.println("ArrayList插入結束時間:" + sdf.format(end));
        System.out.println("ArrayList插入" + (end - start) + "毫秒");


        // LinkedList插入
        start = System.currentTimeMillis();
        System.out.println("LinkedList插入開始時間:" + sdf.format(start));
        LinkedList<Integer> linkedList = new LinkedList<>();
        for (int i = 0; i < COUNT; i++) {
            linkedList.add(i);
        }
        end = System.currentTimeMillis();
        System.out.println("LinkedList插入結束時間:" + sdf.format(end));
        System.out.println("LinkedList插入結束時間" + (end - start) + "毫秒");
     }
}

輸出如下:
兩者寫入的性能相差不大!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

2、插入數據

在原有增加的數據上,在index:100的位置上再插入10萬條數據。

public class ArrayListAndLinkList {
    public final static int COUNT=100000;
    public static void main(String[] args) {

        // ArrayList插入
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        Long start = System.currentTimeMillis();
        System.out.println("ArrayList插入開始時間:" + sdf.format(start));

        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 0; i < COUNT; i++) {
            arrayList.add(i);
        }
        for (int i = 0; i < COUNT; i++) {
            arrayList.add(100,i);
        }

        Long end = System.currentTimeMillis();
        System.out.println("ArrayList插入結束時間:" + sdf.format(end));
        System.out.println("ArrayList插入" + (end - start) + "毫秒");


        // LinkedList插入
        start = System.currentTimeMillis();
        System.out.println("LinkedList插入開始時間:" + sdf.format(start));
        LinkedList<Integer> linkedList = new LinkedList<>();
        for (int i = 0; i < COUNT; i++) {
            linkedList.add(i);
        }
        for (int i = 0; i < COUNT; i++) {
            linkedList.add(100,i);
        }
        end = System.currentTimeMillis();
        System.out.println("LinkedList插入結束時間:" + sdf.format(end));
        System.out.println("LinkedList插入結束時間" + (end - start) + "毫秒");
     }
}

輸出如下:
ArrayList的性能明顯比LinkedList的性能差了很多。

看下原因:
ArrayList的插入源碼:

  public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

ArrayList的插入原理:在index位置上插入后,在index後續的數據上需要做逐一複製。

LinkedList的插入源碼:

public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
 }
 ...
  void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

LinkedList的插入原理:在原來相互鏈接的兩個節點(Node)斷開,把新的結點插入到這兩個節點中間,根本不存在複製這個過程。

3、遍曆數據

在增加和插入的基礎上,利用get方法進行遍歷。

public class ArrayListAndLinkList {
    public final static int COUNT=100000;
    public static void main(String[] args) {

        // ArrayList插入
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        Long start = System.currentTimeMillis();
        System.out.println("ArrayList插入開始時間:" + sdf.format(start));

        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 0; i < COUNT; i++) {
            arrayList.add(i);
        }
        for (int i = 0; i < COUNT; i++) {
            arrayList.add(100,i);
        }

        Long end = System.currentTimeMillis();
        System.out.println("ArrayList插入結束時間:" + sdf.format(end));
        System.out.println("ArrayList插入" + (end - start) + "毫秒");


        // LinkedList插入
        start = System.currentTimeMillis();
        System.out.println("LinkedList插入開始時間:" + sdf.format(start));
        LinkedList<Integer> linkedList = new LinkedList<>();
        for (int i = 0; i < COUNT; i++) {
            linkedList.add(i);
        }
        for (int i = 0; i < COUNT; i++) {
            linkedList.add(100,i);
        }
        end = System.currentTimeMillis();
        System.out.println("LinkedList插入結束時間:" + sdf.format(end));
        System.out.println("LinkedList插入結束時間" + (end - start) + "毫秒");

        // ArrayList遍歷
        start = System.currentTimeMillis();
        System.out.println("ArrayList遍歷開始時間:" + sdf.format(start));
        for (int i = 0; i < 2*COUNT; i++) {
            arrayList.get(i);
        }
        end = System.currentTimeMillis();
        System.out.println("ArrayList遍歷開始時間:" + sdf.format(end));
        System.out.println("ArrayList遍歷開始時間" + (end - start) + "毫秒");

        // LinkedList遍歷
        start = System.currentTimeMillis();
        System.out.println("LinkedList遍歷開始時間:" + sdf.format(start));
        for (int i = 0; i < 2*COUNT; i++) {
            linkedList.get(i);
        }
        end = System.currentTimeMillis();
        System.out.println("LinkedList遍歷開始時間:" + sdf.format(end));
        System.out.println("LinkedList遍歷開始時間" + (end - start) + "毫秒");

    }
}

輸出如下:

兩者的差異巨大:
我們看一下LInkedList的get方法:從頭遍歷或從尾部遍歷結點

public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
 ...
 Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
3.1、LinkedList遍歷改進

我們採用迭代器對LinkedList的遍歷進行改進:

		...
		// LinkedList遍歷
        start = System.currentTimeMillis();
        System.out.println("LinkedList遍歷開始時間:" + sdf.format(start));
        Iterator<Integer> iterator = linkedList.iterator();
        while(iterator.hasNext()){
            iterator.next();
        }
        end = System.currentTimeMillis();
        System.out.println("LinkedList遍歷開始時間:" + sdf.format(end));
        System.out.println("LinkedList遍歷開始時間" + (end - start) + "毫秒");

再看下結果:
兩者的遍歷性能接近。

總結

  • List使用首選ArrayList。對於個別插入刪除非常多的可以使用LinkedList。
  • LinkedList,遍歷建議使用Iterator迭代器,尤其是數據量較大時LinkedList避免使用get遍歷。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。