由百度基礎面面試想到的和總結的 TCP和UDP的優缺點及區別 java中建立線程的三種方法以及區別

一。排序算法:html

快排:partition過程:產生一個隨機數,這個隨機數是在start到end下標中間,即:index=(Math.random*(end-start+1))+startjava

swap(array,start,index)交換第一個數和這個隨機數。mysql

quicksort裏面包含這個partition過程,partition以後獲得第一個數的下標,左邊和右邊分別是小於和大於這個數的兩部分,而後左邊的這部分執行partition過程,右邊一樣。程序員

public Class QuickSortTest{
public void quickSort(int array[],int start,int end){
if(start>end)
return;
int index=partition(array,start,end);
quickSort(array,start,index-1);
quickSort(array,index+1,end);
}
public int partition(int array[],start,end){
int index=(Math.random*(end-start+1))+start;
swap(array,index,start);
while(start<end){
while(array[end]>array[start])
end--;
if(start<end){
swap(array,start,end);
start++;
}
while(array[start<array[end])
start++;
if(start<end){
swap(array,start,end);
end--;
}
}
return start;
}
public void swap(int array[],index1,index2){
int temp=array[index1];
array[index1]=array[index2];
array[index2]=temp;
}
}

快排類似問題:

劍指offer29題面試

29.題目描述:輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,

import java.util.*;
    public class Solution {
        public static ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
            ArrayList<Integer> list = new ArrayList<>();
            if(input == null || k <=0 || input.length < k)
                return list;
            int start = 0;
            int end = input.length -1;
            int index = partition(input, start, end);
            while(index != k-1){
                if(index > k-1){
                    end = index-1;
                    index = partition(input,start,end);
                }else{
                    start = index + 1;
                    index = partition(input,start,end);
                }
            }
            for(int i=0; i<k; i++)
                list.add(input[i]);
            return list;
        }
        public static int partition(int[] array, int start, int end){
            if(array == null || array.length <= 0 || start< 0 || end >= array.length)
                throw new RuntimeException("輸入不正確");
            int index = (int)(Math.random() *(end-start +1)) + start;//獲取隨機數
            swap(array,index,start);
            while(start < end){
                while(array[end] > array[start])
                    end --;
                if(start < end){
                    swap(array, start, end);
                    start ++;
                }
                while(array[start] < array[end])
                    start ++;
                if(start < end){
                    swap(array,start, end);
                    end--;
                }
            }
            return start;
        }
        public static void swap(int[] array, int index, int start){//交換數組位置
            int tmp = array[index];
            array[index] = array[start];
            array[start] = tmp;
        }
    }

 荷蘭國旗問題:算法

 1 public class NetherLandFlag {
 2     public static int[] partition(int arr[],int index,int r,int num){
 3         int less=index-1;
 4         int more=r+1;
 5         while (index<more){
 6             if (arr[index]<num){
 7                 swap(arr,++less,index++);
 8             }else if (arr[index]>num){
 9                 swap(arr,--more,index);
10             }else {
11                 index++;
12             }
13         }
14         return new int[]{less+1,more-1};
15     }
16 
17     private static void swap(int[] arr, int i, int j) {
18         int temp=arr[i];
19         arr[i]=arr[j];
20         arr[j]=temp;
21     }
22 
23     public static void main(String[] args) {
24         int arr[]={1,2,3,4,5,5,6,6,6,7,9};
25         int num=6;
26         int a[]=partition(arr,0,arr.length-1,num);
27         for (int i=0;i<a.length;i++) {
28             System.out.println(a[i]);
29         }
30     }
31 }

用荷蘭國旗問題改進快排sql

 1 public static int[] partition(int arr[],int index,int r){
 2         int less=index-1;
 3         int num=arr[r];
 4         int more=r+1;
 5         while (index<more){
 6             if (arr[index]<num){
 7                 swap(arr,++less,index++);
 8             }else if (arr[index]>num){
 9                 swap(arr,--more,index);
10             }else {
11                 index++;
12             }
13         }
14         return new int[]{less+1,more-1};
15     }
16 
17     private static void swap(int[] arr, int i, int j) {
18         int temp=arr[i];
19         arr[i]=arr[j];
20         arr[j]=temp;
21     }
22     public static void quickSort(int arr[],int l,int r){
23         if (l<r){
24             swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
25             int a[]=partition(arr,l,r);
26             quickSort(arr,l,a[0]-1);
27             quickSort(arr,a[1]+1,r);
28         }
29 
30     }

 

歸併排序寫一遍:數據庫

public static void mergeSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		mergeSort(arr, 0, arr.length - 1);
	}

	public static void mergeSort(int[] arr, int l, int r) {
		if (l == r) {
			return;
		}
		int mid = l + ((r - l) >> 1);
		mergeSort(arr, l, mid);
		mergeSort(arr, mid + 1, r);
		merge(arr, l, mid, r);
	}

	public static void merge(int[] arr, int l, int m, int r) {
		int[] help = new int[r - l + 1];
		int i = 0;
		int p1 = l;
		int p2 = m + 1;
		while (p1 <= m && p2 <= r) {
			help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		while (p1 <= m) {
			help[i++] = arr[p1++];
		}
		while (p2 <= r) {
			help[i++] = arr[p2++];
		}
		for (i = 0; i < help.length; i++) {
			arr[l + i] = help[i];
		}
	}

  

歸併類似問題:

劍指offer35題編程

35.題目描述:在數組中的兩個數字,若是前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007

 1 public class Solution {
 2     int count;
 3  
 4     public int InversePairs(int[] array) {
 5         count = 0;
 6         if (array != null)
 7             sort(array, 0, array.length - 1);
 8         return count;
 9     }
10  
11     /*
12      * 歸併排序(從上往下)
13      */
14     public void sort(int[] a, int start, int end) {
15         if (start >= end)
16             return;
17         int mid = (start + end) >> 1;
18  //或者改成int類型 return sort(a,start,mid)+sort(a,mid+1,end)+merge(a,start,mid,end);
19         sort(a, start, mid);
20         sort(a, mid + 1, end);
21  
22         merge(a, start, mid, end);
23     }
24  
25     /*
26      * 將一個數組中的兩個相鄰有序區間合併成一個
27      */
28     public void merge(int[] a, int start, int mid, int end) {
29         int[] tmp = new int[end - start + 1];
30  
31         int i = start, j = mid + 1, k = 0;
32         while (i <= mid && j <= end) {
33             if (a[i] <= a[j])
34                 tmp[k++] = a[i++];
35             else {
36                 tmp[k++] = a[j++];
37                 count += mid - i + 1;
38                 count=count%1000000007;
39             }
40         }
41  
42         while (i <= mid)
43             tmp[k++] = a[i++];
44         while (j <= end)
45             tmp[k++] = a[j++];
46         for (k = 0; k < tmp.length; k++)
47             a[start + k] = tmp[k];
48     }
49 }

小和問題:api

 

 

public class SmallSum {
    public static int smallSum(int array[]) {
        if (array == null || array.length < 2)
            return 0;
        return mergeSort(array, 0, array.length - 1);
    }

    private static int mergeSort(int[] arr, int l, int r) {
        if (l == r)
        {
            return 0;
        }
        int mid = l + ((r - l) >> 1);
        return mergeSort(arr, l, mid) + mergeSort(arr, mid + 1, r) + merge(arr, l, mid, r);
    }

    public static int merge(int arr[], int l, int m, int r) {
        int p1 = l;
        int p2 = m + 1;
        int i = 0;
        int help[] = new int[r - l + 1];
        int res = 0;
        while (p1 <= m && p2 <= r) {
            res += arr[p1] < arr[p2] ? (r - p2 + 1) * arr[p1] : 0;
            help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
        }
        while (p1 <= m) {
            help[i++] = arr[p1++];
        }
        while (p2 <= r) {
            help[i++] = arr[p2++];
        }
        for (i = 0; i < help.length; i++) {
            arr[l + i] = help[i];
        }
        return res;
    }

    public static void main(String[] args) {
        int arr[] = {1, 3, 4, 2, 5};
        System.out.println(smallSum(arr));

    }
}

 

堆排序:(大根堆)

 1 /**
 2  * Created by imqsl in  15:09 2018-7-10
 3  * description:大根堆
 4  */
 5 public class HeapSort {
 6     /**
 7      * description:從上往下走
 8      */
 9     public static void heapInsert(int arr[], int index) {
10         while (arr[index] > arr[(index - 1) / 2]) {
11             swap(arr, index, (index - 1) / 2);
12             index = (index - 1) / 2;
13         }
14     }
15 
16     /**
17      * description:從下往上走
18      */
19     public static void heapify(int arr[], int index, int size) {
20         int left = index * 2 + 1;
21         while (left < size) {
22             int larger = (left + 1 < size && arr[left] < arr[left + 1]) ? left + 1 : left;
23             larger = arr[larger] > arr[index] ? larger:index;
24             if (larger == index)
25                 break;
26             swap(arr, index, larger);
27             index = larger;
28             left = index * 2 + 1;
29         }
30     }
31 
32     public static void heapSort(int arr[]) {
33         if (arr == null)
34             return;
35         for (int i = 0; i < arr.length; i++) {
36             heapInsert(arr, i);
37         }
38         int size = arr.length;
39         swap(arr, 0, --size);
40         while (size > 0) {
41             heapify(arr, 0, size);
42             swap(arr, 0, --size);
43         }
44 
45     }
46 
47     private static void swap(int[] arr, int i, int j) {
48         int temp = arr[i];
49         arr[i] = arr[j];
50         arr[j] = temp;
51     }
52 
53     public static void main(String[] args) {
54         int example[] = {5, 4, 6, 2,8, 1, 8, 9};
55         heapSort(example);
56         for (int i = 0; i < example.length; i++) {
57             System.out.print(example[i] + "|");
58         }
59     }
60 }

堆的應用:劍指offer63題

63.題目描述:如何獲得一個數據流中的中位數?若是從數據流中讀出奇數個數值,那麼中位數就是全部數值排序以後位於中間的數值。若是從數據流中讀出偶數個數值,那麼中位數就是全部數值排序以後中間兩個數的平均值。

 1 import java.util.*;
 2     public class Solution {
 3         int count = 0;
 4         private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
 5         private PriorityQueue<Integer> maxHeap = new PriorityQueue<>(15,new Comparator<Integer>() {
 6             @Override
 7             public int compare(Integer o1, Integer o2) {
 8                 return o2 - o1;
 9             }
10         });
11         public void Insert(Integer num) {
12             if((count & 1) == 0){
13                 if(!maxHeap.isEmpty() && maxHeap.peek() > num) {
14                     maxHeap.offer(num);
15                     num = maxHeap.poll();
16                 }
17                 minHeap.offer(num);
18             }else {
19                 if(!minHeap.isEmpty() && minHeap.peek() < num){
20                     minHeap.offer(num);
21                     num = minHeap.poll();
22                 }
23                 maxHeap.offer(num);
24             }
25             ++count;
26         }
27         public Double GetMedian() {
28             if((count & 1) == 1)
29                 return Double.valueOf(minHeap.peek());
30             else 
31                 return Double.valueOf(minHeap.peek() + maxHeap.peek())/ 2;
32         }
33     }

希爾排序瞭解一下,

基數排序瞭解一下。

二。網絡問題:

(1)七層模型

 

 

 

TCP和UDP的區別:

TCP和UDP的優缺點及區別

 

TCP的優勢: 可靠,穩定 TCP的可靠體如今TCP在傳遞數據以前,會有三次握手來創建鏈接,並且在數據傳遞時,有確認、窗口、重傳、擁塞控制機制,在數據傳完後,還會斷開鏈接用來節約系統資源。 TCP的缺點: 慢,效率低,佔用系統資源高,易被攻擊 TCP在傳遞數據以前,要先建鏈接,這會消耗時間,並且在數據傳遞時,確認機制、重傳機制、擁塞控制機制等都會消耗大量的時間,並且要在每臺設備上維護全部的傳輸鏈接,事實上,每一個鏈接都會佔用系統的CPU、內存等硬件資源。 並且,由於TCP有確認機制、三次握手機制,這些也致使TCP容易被人利用,實現DOS、DDOS、CC等攻擊。

UDP的優勢: 快,比TCP稍安全 UDP沒有TCP的握手、確認、窗口、重傳、擁塞控制等機制,UDP是一個無狀態的傳輸協議,因此它在傳遞數據時很是快。沒有TCP的這些機制,UDP較TCP被攻擊者利用的漏洞就要少一些。但UDP也是沒法避免攻擊的,好比:UDP Flood攻擊…… UDP的缺點: 不可靠,不穩定 由於UDP沒有TCP那些可靠的機制,在數據傳遞時,若是網絡質量很差,就會很容易丟包。 基於上面的優缺點,那麼: 何時應該使用TCP: 當對網絡通信質量有要求的時候,好比:整個數據要準確無誤的傳遞給對方,這每每用於一些要求可靠的應用,好比HTTP、HTTPS、FTP等傳輸文件的協議,POP、SMTP等郵件傳輸的協議。 在平常生活中,常見使用TCP協議的應用以下: 瀏覽器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件傳輸 ………… 何時應該使用UDP: 當對網絡通信質量要求不高的時候,要求網絡通信速度能儘可能的快,這時就可使用UDP。 好比,平常生活中,常見使用UDP協議的應用以下: QQ語音 QQ視頻 TFTP ……

有些應用場景對可靠性要求不高會用到UPD,好比長視頻,要求速率

小結TCP與UDP的區別:

1.基於鏈接與無鏈接;
2.對系統資源的要求(TCP較多,UDP少);
3.UDP程序結構較簡單;
4.流模式與數據報模式 ;

5.TCP保證數據正確性,UDP可能丟包,TCP保證數據順序,UDP不保證。

 

tcp協議和udp協議的差異 
TCP UDP
是否鏈接 面向鏈接 面向非鏈接
傳輸可靠性 可靠 不可靠
應用場合 傳輸大量數據 少許數據
速度 慢 快

TCP與UDP區別總結:

一、TCP面向鏈接(如打電話要先撥號創建鏈接);UDP是無鏈接的,即發送數據以前不須要創建鏈接

二、TCP提供可靠的服務。也就是說,經過TCP鏈接傳送的數據,無差錯,不丟失,不重複,且按序到達;UDP盡最大努力交付,即不保證可靠交付
三、TCP面向字節流,其實是TCP把數據當作一連串無結構的字節流;UDP是面向報文的
UDP沒有擁塞控制,所以網絡出現擁塞不會使源主機的發送速率下降(對實時應用頗有用,如IP電話,實時視頻會議等)
四、每一條TCP鏈接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通訊
五、TCP首部開銷20字節;UDP的首部開銷小,只有8個字節

六、TCP的邏輯通訊信道是全雙工的可靠信道,UDP則是不可靠信道。

能夠參考:https://blog.csdn.net/IOT_SHUN/article/details/79900476

https://www.cnblogs.com/xiaomayizoe/p/5258754.html

可靠是經過什麼實現的:超時重傳

三。線程和進程的區別?併發?

一:線程與進程

1.概念

線程:是程序執行流的最小單元,是系統獨立調度和分配CPU(獨立運行)的基本單位。

進程:是資源分配的基本單位。一個進程包括多個線程。

2.區別:

1.線程與資源分配無關,它屬於某一個進程,並與進程內的其餘線程一塊兒共享進程的資源。

2.每一個進程都有本身一套獨立的資源(數據),供其內的全部線程共享。

3.不管是大小,開銷線程要更「輕量級」

4.一個進程內的線程通訊比進程之間的通訊更快速,有效。(由於共享變量)

 

  • 進程是資源分配的最小單位,線程是程序執行的最小單位。

  • 進程有本身的獨立地址空間,每啓動一個進程,系統就會爲它分配地址空間,創建數據表來維護代碼段、堆棧段和數據段,這種操做很是昂貴。而線程是共享進程中的數據的,使用相同的地址空間,所以CPU切換一個線程的花費遠比進程要小不少,同時建立一個線程的開銷也比進程要小不少。

  • 線程之間的通訊更方便,同一進程下的線程共享全局變量、靜態變量等數據,而進程之間的通訊須要以通訊的方式(IPC)進行。不過如何處理好同步與互斥是編寫多線程程序的難點。

  • 可是多進程程序更健壯,多線程程序只要有一個線程死掉,整個進程也死掉了,而一個進程死掉並不會對另一個進程形成影響,由於進程有本身獨立的地址空間。

二.多線程與多進程

多線程:同一時刻執行多個線程。用瀏覽器一邊下載,一邊聽歌,一邊看視頻,一邊看網頁。。。

多進程:同時執行多個程序。如,同事運行YY,QQ,以及各類瀏覽器。

 

三.併發與並行

併發當有多個線程在操做時,若是系統只有一個CPU,則它根本不可能真正同時進行一個以上的線程,它只能把CPU運行時間劃分紅若干個時間段,再將時間 段分配給各個線程執行,在一個時間段的線程代碼運行時,其它線程處於掛起狀。.這種方式咱們稱之爲併發(Concurrent)。


並行:當系統有一個以上CPU時,則線程的操做有可能非併發。當一個CPU執行一個線程時,另外一個CPU能夠執行另外一個線程,兩個線程互不搶佔CPU資源,能夠同時進行,這種方式咱們稱之爲並行(Parallel)。

四。mybatis和hibernate的應用場景,不一樣。

先比較下jdbc編程和 hibernate編程各自的優缺點。
   JDBC:
   咱們平時使用jdbc進行編程,大體須要下面幾個步驟:
   1,使用jdbc編程須要鏈接 數據庫,註冊驅動和數據庫信息
   2,操做Connection,打開Statement對象
   3,經過Statement對象執行SQL,返回結果到ResultSet對象
   4,使用ResultSet讀取數據,而後經過代碼轉化爲具體的POJO對象
   5,關閉數據庫相關的資源
  jdbc的缺點:
  一:工做量比較大,須要鏈接,而後處理jdbc底層事務,處理數據類型,還須要操做Connection,Statement對象和ResultSet對象去拿數據並關閉他們。
   二:咱們對jdbc編程可能產生的異常進行捕捉處理並正確關閉資源
 
   因爲JDBC存在的缺陷,在實際工做中咱們不多直接使用jdbc進行編程,用的更多的是ORM對象關係模型來操做數據庫,Hibernate就是一個ORM模型
     Hibernate:
    Hibernate是創建在若干POJO經過xml映射文件(或註解)提供的規則映射到數據庫表上的。咱們能夠經過POJO直接操做數據庫的數據,他提供的是一種全表映射的模型。相對而言,Hibernate對JDBC的封裝程度仍是比較高的,咱們已經不須要寫SQL,只要使用HQL語言就能夠了。
    使用Hibernate進行編程有如下好處:
    1,消除了代碼的映射規則,它所有分離到了xml或者註解裏面去配置。
    2,無需在管理數據庫鏈接,它也配置到xml裏面了。
    3,一個會話中不須要操做多個對象,只須要操做Session對象。
    4,關閉資源只須要關閉一個Session即可。
    這就是Hibernate的優點,在配置了映射文件和數據庫鏈接文件後,Hibernate就能夠經過Session操做,很是容易,消除了jdbc帶來的大量代碼,大大提升了編程的簡易性和可讀性。Hibernate還提供了級聯,緩存,映射,一對多等功能。Hibernate是全表映射,經過HQL去操做pojo進而操做數據庫的數據。
 
    Hibernate的缺點:
    1,全表映射帶來的不便,好比更新時須要發送全部的字段。
    2,沒法根據不一樣的條件組裝不一樣的SQL。
    3,對多表關聯和複雜的sql查詢支持較差,須要本身寫sql,返回後,須要本身將數據封裝爲pojo。
    4,不能有效的支持存儲過程。
    5,雖然有HQL,可是性能較差,大型互聯網系統每每須要優化sql,而hibernate作不到。
   
 
Mybatis:
    爲了解決Hibernate的不足,Mybatis出現了,Mybatis是半自動的框架。之因此稱它爲半自動,是由於它須要手工匹配提供POJO,sql和映射關係,而全表映射的Hibernate只須要提供pojo和映射關係便可。
   Mybatis須要提供的映射文件包含了一下三個部分:sql,映射規則,pojo。在Mybatis裏面你須要本身編寫sql,雖然比Hibernate配置多,可是Mybatis能夠配置動態sql,解決了hibernate表名根據時間變化,不一樣條件下列不同的問題,同時你也能夠對sql進行優化,經過配置決定你的sql映射規則,也能支持存儲過程,因此對於一些複雜和須要優化性能的sql查詢它就更加方便。Mybatis幾乎能夠作到jdbc全部能作到的事情。
 
何時使用Hibernate,Mybatis
    Hibernate做爲留下的 Java orm框架,它確實編程簡易,須要咱們提供映射的規則,徹底能夠經過IDE生成,同時無需編寫sql確實開發效率優於Mybatis。此外Hibernate還提供了緩存,日誌,級聯等強大的功能,可是Hibernate的缺陷也是十分明顯,多表關聯複雜sql,數據系統權限限制,根據條件變化的sql,存儲過程等場景使用Hibernate十分不方便,而性能又難以經過sql優化,因此註定了Hibernate只適用於在場景不太複雜,要求性能不太苛刻的時候使用。
    若是你須要一個靈活的,能夠動態生成映射關係的框架,那麼Mybatis確實是一個最好的選擇。它幾乎能夠替代jdbc,擁有動態列,動態表名,存儲過程支持,同時提供了簡易的緩存,日誌,級聯。可是它的缺陷是須要你提供映射規則和sql,因此開發工做量比hibernate要大些。
 
 
1)從層次上看,JDBC是較底層的持久層操做方式,而Hibernate和MyBatis都是在JDBC的基礎上進行了封裝使其更加方便程序員對持久層的操做。
2)從功能上看,JDBC就是簡單的創建 數據庫鏈接,而後建立statement,將sql語句傳給statement去執行,若是是有返回結果的查詢語句,會將查詢結果放到ResultSet對象中,經過對ResultSet對象的遍歷操做來獲取數據;Hibernate是將數據庫中的數據表映射爲持久層的 Java對象,對sql語句進行修改和優化比較困難;MyBatis是將sql語句中的輸入參數和輸出參數映射爲java對象,sql修改和優化比較方便.
3)從使用上看,若是進行底層編程,並且對性能要求極高的話,應該採用JDBC的方式;若是要對數據庫進行完整性控制的話建議使用Hibernate;若是要靈活使用sql語句的話建議採用MyBatis框架。
五。建立線程的三種方式

java中建立線程的三種方法以及區別

 

Java使用Thread類表明線程,全部的線程對象都必須是Thread類或其子類的實例。Java能夠用三種方式來建立線程,以下所示:

1)繼承Thread類建立線程

2)實現Runnable接口建立線程

3)使用Callable和Future建立線程

下面讓咱們分別來看看這三種建立線程的方法。

 

------------------------繼承Thread類建立線程---------------------

 

經過繼承Thread類來建立並啓動多線程的通常步驟以下

1】d定義Thread類的子類,並重寫該類的run()方法,該方法的方法體就是線程須要完成的任務,run()方法也稱爲線程執行體。

2】建立Thread子類的實例,也就是建立了線程對象

3】啓動線程,即調用線程的start()方法

代碼實例

public class MyThread extends Thread{//繼承Thread類

  public void run(){

  //重寫run方法

  }

}

public class Main {

  public static void main(String[] args){

    new MyThread().start();//建立並啓動線程

  }

}

------------------------實現Runnable接口建立線程---------------------

經過實現Runnable接口建立並啓動線程通常步驟以下:

1】定義Runnable接口的實現類,同樣要重寫run()方法,這個run()方法和Thread中的run()方法同樣是線程的執行體

2】建立Runnable實現類的實例,並用這個實例做爲Thread的target來建立Thread對象,這個Thread對象纔是真正的線程對象

3】第三部依然是經過調用線程對象的start()方法來啓動線程

代碼實例:

public class MyThread2 implements Runnable {//實現Runnable接口

  public void run(){

  //重寫run方法

  }

}

public class Main {

  public static void main(String[] args){

    //建立並啓動線程

    MyThread2 myThread=new MyThread2();

    Thread thread=new Thread(myThread);

    thread().start();

    //或者    new Thread(new MyThread2()).start();

  }

}

------------------------使用Callable和Future建立線程---------------------

和Runnable接口不同,Callable接口提供了一個call()方法做爲線程執行體,call()方法比run()方法功能要強大。

》call()方法能夠有返回值

》call()方法能夠聲明拋出異常

Java5提供了Future接口來表明Callable接口裏call()方法的返回值,而且爲Future接口提供了一個實現類FutureTask,這個實現類既實現了Future接口,還實現了Runnable接口,所以能夠做爲Thread類的target。在Future接口裏定義了幾個公共方法來控制它關聯的Callable任務。

>boolean cancel(boolean mayInterruptIfRunning):視圖取消該Future裏面關聯的Callable任務

>V get():返回Callable裏call()方法的返回值,調用這個方法會致使程序阻塞,必須等到子線程結束後纔會獲得返回值

>V get(long timeout,TimeUnit unit):返回Callable裏call()方法的返回值,最多阻塞timeout時間,通過指定時間沒有返回拋出TimeoutException

>boolean isDone():若Callable任務完成,返回True

>boolean isCancelled():若是在Callable任務正常完成前被取消,返回True

介紹了相關的概念以後,建立並啓動有返回值的線程的步驟以下:

1】建立Callable接口的實現類,並實現call()方法,而後建立該實現類的實例(從java8開始能夠直接使用Lambda表達式建立Callable對象)。

2】使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了Callable對象的call()方法的返回值

3】使用FutureTask對象做爲Thread對象的target建立並啓動線程(由於FutureTask實現了Runnable接口)

4】調用FutureTask對象的get()方法來得到子線程執行結束後的返回值

代碼實例:

public class Main {

  public static void main(String[] args){

   MyThread3 th=new MyThread3();

   //使用Lambda表達式建立Callable對象

     //使用FutureTask類來包裝Callable對象

   FutureTask<Integer> future=new FutureTask<Integer>(

    (Callable<Integer>)()->{

      return 5;

    }

    );

   new Thread(task,"有返回值的線程").start();//實質上仍是以Callable對象來建立並啓動線程

    try{

    System.out.println("子線程的返回值:"+future.get());//get()方法會阻塞,直到子線程執行結束才返回

    }catch(Exception e){

    ex.printStackTrace();

   }

  }

}

--------------------------------------三種建立線程方法對比--------------------------------------

實現Runnable和實現Callable接口的方式基本相同,不過是後者執行call()方法有返回值,後者線程執行體run()方法無返回值,所以能夠把這兩種方式歸爲一種這種方式與繼承Thread類的方法之間的差異以下:

一、線程只是實現Runnable或實現Callable接口,還能夠繼承其餘類。

二、這種方式下,多個線程能夠共享一個target對象,很是適合多線程處理同一份資源的情形。

三、可是編程稍微複雜,若是須要訪問當前線程,必須調用Thread.currentThread()方法。

四、繼承Thread類的線程類不能再繼承其餘父類(Java單繼承決定)。

注:通常推薦採用實現接口的方式來建立多線程

六。微服務的理解

 

 

 

七。海量數據統計字符串出現的個數

 

第一部分、十道海量數據處理面試題

一、海量日誌數據,提取出某日訪問百度次數最多的那個IP。

首先是這一天,而且是訪問百度的日誌中的IP取出來,逐個寫入到一個大文件中。注意到IP是32位的,最多有個2^32個IP。一樣能夠採用映射的方法, 好比模1000,把整個大文件映射爲1000個小文件,再找出每一個小文中出現頻率最大的IP(能夠採用hash_map進行頻率統計,而後再找出頻率最大 的幾個)及相應的頻率。而後再在這1000個最大的IP中,找出那個頻率最大的IP,即爲所求。

或者以下闡述(雪域之鷹):
算法思想:分而治之+Hash
1.IP地址最多有2^32=4G種取值狀況,因此不能徹底加載到內存中處理;
2.能夠考慮採用「分而治之」的思想,按照IP地址的Hash(IP)%1024值,把海量IP日誌分別存儲到1024個小文件中。這樣,每一個小文件最多包含4MB個IP地址;
3.對於每個小文件,能夠構建一個IP爲key,出現次數爲value的Hash map,同時記錄當前出現次數最多的那個IP地址;
4.能夠獲得1024個小文件中的出現次數最多的IP,再依據常規的排序算法獲得整體上出現次數最多的IP;

二、搜索引擎會經過日誌文件把用戶每次檢索使用的全部檢索串都記錄下來,每一個查詢串的長度爲1-255字節。
    假設目前有一千萬個記錄(這些查詢串的重複度比較高,雖然總數是1千萬,但若是除去重複後,不超過3百萬個。一個查詢串的重複度越高,說明查詢它的用戶越多,也就是越熱門。),請你統計最熱門的10個查詢串,要求使用的內存不能超過1G。

典型的Top K算法,仍是在這篇文章裏頭有所闡述,詳情請參見:11、從頭至尾完全解析Hash表算法。

文中,給出的最終算法是:
第一步、先對這批海量數據預處理,在O(N)的時間內用Hash表完成統計(以前寫成了排序,特此訂正。July、2011.04.27);
第二步、藉助堆這個數據結構,找出Top K,時間複雜度爲N‘logK。
即,藉助堆結構,咱們能夠在log量級的時間內查找和調整/移動。所以,維護一個K(該題目中是10)大小的小根堆,而後遍歷300萬的Query,分別 和根元素進行對比因此,咱們最終的時間複雜度是:O(N) + N’*O(logK),(N爲1000萬,N’爲300萬)。ok,更多,詳情,請參考原文。

或者:採用trie樹,關鍵字域存該查詢串出現的次數,沒有出現爲0。最後用10個元素的最小推來對出現頻率進行排序。
三、有一個1G大小的一個文件,裏面每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M。返回頻數最高的100個詞。

方案:順序讀文件中,對於每一個詞x,取hash(x)%5000,而後按照該值存到5000個小文件(記爲x0,x1,…x4999)中。這樣每一個文件大概是200k左右。

若是其中的有的文件超過了1M大小,還能夠按照相似的方法繼續往下分,直到分解獲得的小文件的大小都不超過1M。
對每一個小文件,統計每一個文件中出現的詞以及相應的頻率(能夠採用trie樹/hash_map等),並取出出現頻率最大的100個詞(能夠用含100個結 點的最小堆),並把100個詞及相應的頻率存入文件,這樣又獲得了5000個文件。下一步就是把這5000個文件進行歸併(相似與歸併排序)的過程了。
四、有10個文件,每一個文件1G,每一個文件的每一行存放的都是用戶的query,每一個文件的query均可能重複。要求你按照query的頻度排序。

仍是典型的TOP K算法,解決方案以下:
方案1:
順序讀取10個文件,按照hash(query)%10的結果將query寫入到另外10個文件(記爲)中。這樣新生成的文件每一個的大小大約也1G(假設hash函數是隨機的)。

找一臺內存在2G左右的機器,依次對用hash_map(query, query_count)來統計每一個query出現的次數。利用快速/堆/歸併排序按照出現次數進行排序。將排序好的query和對應的 query_cout輸出到文件中。這樣獲得了10個排好序的文件(記爲)。

對這10個文件進行歸併排序(內排序與外排序相結合)。

方案2:
通常query的總量是有限的,只是重複的次數比較多而已,可能對於全部的query,一次性就能夠加入到內存了。這樣,咱們就能夠採用trie樹/hash_map等直接來統計每一個query出現的次數,而後按出現次數作快速/堆/歸併排序就能夠了。

方案3:
與方案1相似,但在作完hash,分紅多個文件後,能夠交給多個文件來處理,採用分佈式的架構來處理(好比MapReduce),最後再進行合併。
五、 給定a、b兩個文件,各存放50億個url,每一個url各佔64字節,內存限制是4G,讓你找出a、b文件共同的url?

方案1:能夠估計每一個文件安的大小爲5G×64=320G,遠遠大於內存限制的4G。因此不可能將其徹底加載到內存中處理。考慮採起分而治之的方法。

遍歷文件a,對每一個url求取hash(url)%1000,而後根據所取得的值將url分別存儲到1000個小文件(記爲a0,a1,…,a999)中。這樣每一個小文件的大約爲300M。

遍歷文件b,採起和a相同的方式將url分別存儲到1000小文件(記爲b0,b1,…,b999)。這樣處理後,全部可能相同的url都在對應的小 文件(a0vsb0,a1vsb1,…,a999vsb999)中,不對應的小文件不可能有相同的url。而後咱們只要求出1000對小文件中相同的 url便可。

求每對小文件中相同的url時,能夠把其中一個小文件的url存儲到hash_set中。而後遍歷另外一個小文件的每一個url,看其是否在剛纔構建的hash_set中,若是是,那麼就是共同的url,存到文件裏面就能夠了。

方案2:若是容許有必定的錯誤率,可使用Bloom filter,4G內存大概能夠表示340億bit。將其中一個文件中的url使用Bloom filter映射爲這340億bit,而後挨個讀取另一個文件的url,檢查是否與Bloom filter,若是是,那麼該url應該是共同的url(注意會有必定的錯誤率)。

Bloom filter往後會在本BLOG內詳細闡述。
六、在2.5億個整數中找出不重複的整數,注,內存不足以容納這2.5億個整數。

方案1:採用2-Bitmap(每一個數分配2bit,00表示不存在,01表示出現一次,10表示屢次,11無心義)進行,共需內存2^32 * 2 bit=1 GB內存,還能夠接受。而後掃描這2.5億個整數,查看Bitmap中相對應位,若是是00變01,01變10,10保持不變。所描完過後,查看 bitmap,把對應位是01的整數輸出便可。

方案2:也可採用與第1題相似的方法,進行劃分小文件的方法。而後在小文件中找出不重複的整數,並排序。而後再進行歸併,注意去除重複的元素。
七、騰訊面試題:給40億個不重複的unsigned int的整數,沒排過序的,而後再給一個數,如何快速判斷這個數是否在那40億個數當中?

與上第6題相似,個人第一反應時快速排序+二分查找。如下是其它更好的方法:
方案1:oo,申請512M的內存,一個bit位表明一個unsigned int值。讀入40億個數,設置相應的bit位,讀入要查詢的數,查看相應bit位是否爲1,爲1表示存在,爲0表示不存在。

dizengrong:
方案2:這個問題在《編程珠璣》裏有很好的描述,你們能夠參考下面的思路,探討一下:
又由於2^32爲40億多,因此給定一個數可能在,也可能不在其中;
這裏咱們把40億個數中的每個用32位的二進制來表示
假設這40億個數開始放在一個文件中。

而後將這40億個數分紅兩類:
1.最高位爲0
2.最高位爲1
並將這兩類分別寫入到兩個文件中,其中一個文件中數的個數<=20億,而另外一個>=20億(這至關於折半了);
與要查找的數的最高位比較並接着進入相應的文件再查找

再而後把這個文件爲又分紅兩類:
1.次最高位爲0
2.次最高位爲1

並將這兩類分別寫入到兩個文件中,其中一個文件中數的個數<=10億,而另外一個>=10億(這至關於折半了);
與要查找的數的次最高位比較並接着進入相應的文件再查找。
…….
以此類推,就能夠找到了,並且時間複雜度爲O(logn),方案2完。

附:這裏,再簡單介紹下,位圖方法:
使用位圖法判斷整形數組是否存在重複
判斷集合中存在重複是常見編程任務之一,當集合中數據量比較大時咱們一般但願少進行幾回掃描,這時雙重循環法就不可取了。

位圖法比較適合於這種狀況,它的作法是按照集合中最大元素max建立一個長度爲max+1的新數組,而後再次掃描原數組,遇到幾就給新數組的第幾位置上 1,如遇到5就給新數組的第六個元素置1,這樣下次再遇到5想置位時發現新數組的第六個元素已是1了,這說明此次的數據確定和之前的數據存在着重複。這 種給新數組初始化時置零其後置一的作法相似於位圖的處理方法故稱位圖法。它的運算次數最壞的狀況爲2N。若是已知數組的最大值即能事先給新數組定長的話效 率還能提升一倍。

歡迎,有更好的思路,或方法,共同交流。
八、怎麼在海量數據中找出重複次數最多的一個?

方案1:先作hash,而後求模映射爲小文件,求出每一個小文件中重複次數最多的一個,並記錄重複次數。而後找出上一步求出的數據中重複次數最多的一個就是所求(具體參考前面的題)。
九、上千萬或上億數據(有重複),統計其中出現次數最多的錢N個數據。

方案1:上千萬或上億的數據,如今的機器的內存應該能存下。因此考慮採用hash_map/搜索二叉樹/紅黑樹等來進行統計次數。而後就是取出前N個出現次數最多的數據了,能夠用第2題提到的堆機制完成。
十、一個文本文件,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞,請給出思想,給出時間複雜度分析。

方案1:這題是考慮時間效率。用trie樹統計每一個詞出現的次數,時間複雜度是O(n*le)(le表示單詞的平準長度)。而後是找出出現最頻繁的前10 個詞,能夠用堆來實現,前面的題中已經講到了,時間複雜度是O(n*lg10)。因此總的時間複雜度,是O(n*le)與O(n*lg10)中較大的哪一 個。
附、100w個數中找出最大的100個數。

方案1:在前面的題中,咱們已經提到了,用一個含100個元素的最小堆完成。複雜度爲O(100w*lg100)。

方案2:採用快速排序的思想,每次分割以後只考慮比軸大的一部分,知道比軸大的一部分在比100多的時候,採用傳統排序算法排序,取前100個。複雜度爲O(100w*100)。

方案3:採用局部淘汰法。選取前100個元素,並排序,記爲序列L。而後一次掃描剩餘的元素x,與排好序的100個元素中最小的元素比,若是比這個最小的 要大,那麼把這個最小的元素刪除,並把x利用插入排序的思想,插入到序列L中。依次循環,知道掃描了全部的元素。複雜度爲O(100w*100)。

致謝:http://www.cnblogs.com/youwang/

 

第二部分、十個海量數據處理方法大總結

ok,看了上面這麼多的面試題,是否有點頭暈。是的,須要一個總結。接下來,本文將簡單總結下一些處理海量數據問題的常見方法,而往後,本BLOG內會具體闡述這些方法。

下面的方法所有來自http://hi.baidu.com/yanxionglu/blog/博客,對海量數據的處理方法進行了一個通常性的總結,固然這些方法可能並不能徹底覆蓋全部的問題,可是這樣的一些方法也基本能夠處理絕大多數遇到的問題。下面的一些問題基本直接來源於公司的面試筆試題目,方法不必定最優,若是你有更好的處理方法,歡迎討論。

1、Bloom filter

適用範圍:能夠用來實現數據字典,進行數據的判重,或者集合求交集

基本原理及要點:
對於原理來講很簡單,位數組+k個獨立hash函數。將 hash函數對應的值的位數組置1,查找時若是發現全部hash函數對應位都是1說明存在,很明顯這個過程並不保證查找的結果是100%正確的。同時也不 支持刪除一個已經插入的關鍵字,由於該關鍵字對應的位會牽動到其餘的關鍵字。因此一個簡單的改進就是 counting Bloom filter,用一個counter數組代替位數組,就能夠支持刪除了。

還有一個比較重要的問題,如何根據輸入元素個數n,肯定位數組m的大小及hash函數 個數。當hash函數個數k=(ln2)*(m/n)時錯誤率最小。在錯誤率不大於E的狀況下,m至少要等於n*lg(1/E)才能表示任意n個元素的集 合。但m還應該更大些,由於還要保證bit數組裏至少一半爲0,則m應該>=nlg(1/E)*lge 大概就是nlg(1/E)1.44倍(lg表示以2爲底的對數)。

舉個例子咱們假設錯誤率爲0.01,則此時m應大概是n的13倍。這樣k大概是8個。

注意這裏m與n的單位不一樣,m是bit爲單位,而n則是以元素個數爲單位(準確的說是不一樣元素的個數)。一般單個元素的長度都是有不少bit的。因此使用bloom filter內存上一般都是節省的。

擴展:
Bloom filter將集合中的元素映射到位數組中,用k(k爲哈希函數個數)個映射位是否全1表示元素在不在這個集合中。Counting bloom filter(CBF)將位數組中的每一位擴展爲一個counter,從而支持了元素的刪除操做。Spectral Bloom Filter(SBF)將其與集合元素的出現次數關聯。SBF採用counter中的最小值來近似表示元素的出現頻率。

問題實例:給你A,B兩個文件,各存放50億條URL,每條URL佔用64字節,內存限制是4G,讓你找出A,B文件共同的URL。若是是三個乃至n個文件呢?

根據這個問題咱們來計算下內存的佔用,4G=2^32大概是40億*8大概是340 億,n=50億,若是按出錯率0.01算須要的大概是650億個bit。如今可用的是340億,相差並很少,這樣可能會使出錯率上升些。另外若是這些 urlip是一一對應的,就能夠轉換成ip,則大大簡單了。
2、Hashing

適用範圍:快速查找,刪除的基本數據結構,一般須要總數據量能夠放入內存

基本原理及要點:
hash函數選擇,針對字符串,整數,排列,具體相應的hash方法。
碰撞處理,一種是open hashing,也稱爲拉鍊法;另外一種就是closed hashing,也稱開地址法,opened addressing。

擴展:
d-left hashing中的d是多個的意思,咱們先簡化這個問題,看一看2-left hashing。2-left hashing指的是將一個哈希表分紅長度相等的兩半,分別叫作T1和T2,給T1和T2分別配備一個哈希函數,h1和h2。在存儲一個新的key時,同 時用兩個哈希函數進行計算,得出兩個地址h1[key]和h2[key]。這時須要檢查T1中的h1[key]位置和T2中的h2[key]位置,哪個 位置已經存儲的(有碰撞的)key比較多,而後將新key存儲在負載少的位置。若是兩邊同樣多,好比兩個位置都爲空或者都存儲了一個key,就把新key 存儲在左邊的T1子表中,2-left也由此而來。在查找一個key時,必須進行兩次hash,同時查找兩個位置。

問題實例:
1).海量日誌數據,提取出某日訪問百度次數最多的那個IP。
IP的數目仍是有限的,最多2^32個,因此能夠考慮使用hash將ip直接存入內存,而後進行統計。
3、bit-map

適用範圍:可進行數據的快速查找,判重,刪除,通常來講數據範圍是int的10倍如下

基本原理及要點:使用bit數組來表示某些元素是否存在,好比8位電話號碼

擴展:bloom filter能夠看作是對bit-map的擴展

問題實例:
1)已知某個文件內包含一些電話號碼,每一個號碼爲8位數字,統計不一樣號碼的個數。
8位最多99 999 999,大概須要99m個bit,大概10幾m字節的內存便可。
2)2.5億個整數中找出不重複的整數的個數,內存空間不足以容納這2.5億個整數。

將bit-map擴展一下,用2bit表示一個數便可,0表示未出現,1表示出現一次,2表示出現2次及以上。或者咱們不用2bit來進行表示,咱們用兩個bit-map便可模擬實現這個2bit-map。
4、堆

適用範圍:海量數據前n大,而且n比較小,堆能夠放入內存

基本原理及要點:最大堆求前n小,最小堆求前n大。方法,好比求前n小,咱們比較當前 元素與最大堆裏的最大元素,若是它小於最大元素,則應該替換那個最大元素。這樣最後獲得的n個元素就是最小的n個。適合大數據量,求前n小,n的大小比較 小的狀況,這樣能夠掃描一遍便可獲得全部的前n元素,效率很高。

擴展:雙堆,一個最大堆與一個最小堆結合,能夠用來維護中位數。

問題實例:
1)100w個數中找最大的前100個數。
用一個100個元素大小的最小堆便可。

 

5、雙層桶劃分—-其實本質上就是【分而治之】的思想,重在「分」的技巧上!

適用範圍:第k大,中位數,不重複或重複的數字
基本原理及要點:由於元素範圍很大,不能利用直接尋址表,因此經過屢次劃分,逐步肯定範圍,而後最後在一個能夠接受的範圍內進行。能夠經過屢次縮小,雙層只是一個例子。

擴展:
問題實例:
1).2.5億個整數中找出不重複的整數的個數,內存空間不足以容納這2.5億個整數。
有點像鴿巢原理,整數個數爲2^32,也就是,咱們能夠將這2^32個數,劃分爲2^8個區域(好比用單個文件表明一個區域),而後將數據分離到不一樣的區域,而後不一樣的區域在利用bitmap就能夠直接解決了。也就是說只要有足夠的磁盤空間,就能夠很方便的解決。

2).5億個int找它們的中位數。
這個例子比上面那個更明顯。首先咱們 將int劃分爲2^16個區域,而後讀取數據統計落到各個區域裏的數的個數,以後咱們根據統計結果就能夠判斷中位數落到那個區域,同時知道這個區域中的第 幾大數恰好是中位數。而後第二次掃描咱們只統計落在這個區域中的那些數就能夠了。

實際上,若是不是int是int64,咱們能夠通過3次這樣的劃分便可下降到能夠接受 的程度。便可以先將int64分紅2^24個區域,而後肯定區域的第幾大數,在將該區域分紅2^20個子區域,而後肯定是子區域的第幾大數,而後子區域裏 的數的個數只有2^20,就能夠直接利用direct addr table進行統計了。
6、數據庫索引

適用範圍:大數據量的增刪改查

基本原理及要點:利用數據的設計實現方法,對海量數據的增刪改查進行處理。
7、倒排索引(Inverted index)

適用範圍:搜索引擎,關鍵字查詢

基本原理及要點:爲什麼叫倒排索引?一種索引方法,被用來存儲在全文搜索下某個單詞在一個文檔或者一組文檔中的存儲位置的映射。

以英文爲例,下面是要被索引的文本:
T0 = 「it is what it is」
T1 = 「what is it」
T2 = 「it is a banana」

咱們就能獲得下面的反向文件索引:

「a」:      {2}
「banana」: {2}
「is」:     {0, 1, 2}
「it」:     {0, 1, 2}
「what」:   {0, 1}

檢索的條件」what」,」is」和」it」將對應集合的交集。

正向索引開發出來用來存儲每一個文檔的單詞的列表。正向索引的查詢每每知足每一個文檔有序 頻繁的全文查詢和每一個單詞在校驗文檔中的驗證這樣的查詢。在正向索引中,文檔佔據了中心的位置,每一個文檔指向了一個它所包含的索引項的序列。也就是說文檔 指向了它包含的那些單詞,而反向索引則是單詞指向了包含它的文檔,很容易看到這個反向的關係。

擴展:
問題實例:文檔檢索系統,查詢那些文件包含了某單詞,好比常見的學術論文的關鍵字搜索。
8、外排序

適用範圍:大數據的排序,去重

基本原理及要點:外排序的歸併方法,置換選擇敗者樹原理,最優歸併樹

擴展:

問題實例:
1).有一個1G大小的一個文件,裏面每一行是一個詞,詞的大小不超過16個字節,內存限制大小是1M。返回頻數最高的100個詞。

這個數據具備很明顯的特色,詞的大小爲16個字節,可是內存只有1m作hash有些不夠,因此能夠用來排序。內存能夠當輸入緩衝區使用。
9、trie樹

適用範圍:數據量大,重複多,可是數據種類小能夠放入內存

基本原理及要點:實現方式,節點孩子的表示方式

擴展:壓縮實現。

問題實例:
1).有10個文件,每一個文件1G,每一個文件的每一行都存放的是用戶的query,每一個文件的query均可能重複。要你按照query的頻度排序。
2).1000萬字符串,其中有些是相同的(重複),須要把重複的所有去掉,保留沒有重複的字符串。請問怎麼設計和實現?
3).尋找熱門查詢:查詢串的重複度比較高,雖然總數是1千萬,但若是除去重複後,不超過3百萬個,每一個不超過255字節。
10、分佈式處理 mapreduce

適用範圍:數據量大,可是數據種類小能夠放入內存

基本原理及要點:將數據交給不一樣的機器去處理,數據劃分,結果歸約。

擴展:
問題實例:
1).The canonical example application of MapReduce is a process to count the appearances of
each different word in a set of documents:
2).海量數據分佈在100臺電腦中,想個辦法高效統計出這批數據的TOP10。
3).一共有N個機器,每一個機器上有N個數。每一個機器最多存O(N)個數並對它們操做。如何找到N^2個數的中數(median)?
經典問題分析
上千萬or億數據(有重複),統計其中出現次數最多的前N個數據,分兩種狀況:可一次讀入內存,不可一次讀入。

可用思路:trie樹+堆,數據庫索引,劃分子集分別統計,hash,分佈式計算,近似統計,外排序

所謂的是否能一次讀入內存,實際上應該指去除重複後的數據量。若是去重後數據能夠放入 內存,咱們能夠爲數據創建字典,好比經過 map,hashmap,trie,而後直接進行統計便可。固然在更新每條數據的出現次數的時候,咱們能夠利用一個堆來維護出現次數最多的前N個數據,當 然這樣致使維護次數增長,不如徹底統計後在求前N大效率高。

若是數據沒法放入內存。一方面咱們能夠考慮上面的字典方法可否被改進以適應這種情形,能夠作的改變就是將字典存放到硬盤上,而不是內存,這能夠參考數據庫的存儲方法。

固然還有更好的方法,就是能夠採用分佈式計算,基本上就是map-reduce過程, 首先能夠根據數據值或者把數據hash(md5)後的值,將數據按照範圍劃分到不一樣的機子,最好可讓數據劃分後能夠一次讀入內存,這樣不一樣的機子負責處 理各類的數值範圍,實際上就是map。獲得結果後,各個機子只需拿出各自的出現次數最多的前N個數據,而後彙總,選出全部的數據中出現次數最多的前N個數 據,這實際上就是reduce過程。

實際上可能想直接將數據均分到不一樣的機子上進行處理,這樣是沒法獲得正確的解的。由於 一個數據可能被均分到不一樣的機子上,而另外一個則可能徹底彙集到一個機子上,同時還可能存在具備相同數目的數據。好比咱們要找出現次數最多的前100個,我 們將1000萬的數據分佈到10臺機器上,找到每臺出現次數最多的前 100個,歸併以後這樣不能保證找到真正的第100個,由於好比出現次數最多的第100個可能有1萬個,可是它被分到了10臺機子,這樣在每臺上只有1千 個,假設這些機子排名在1000個以前的那些都是單獨分佈在一臺機子上的,好比有1001個,這樣原本具備1萬個的這個就會被淘汰,即便咱們讓每臺機子選 出出現次數最多的1000個再歸併,仍然會出錯,由於可能存在大量個數爲1001個的發生彙集。所以不能將數據隨便均分到不一樣機子上,而是要根據hash 後的值將它們映射到不一樣的機子上處理,讓不一樣的機器處理一個數值範圍。

而外排序的方法會消耗大量的IO,效率不會很高。而上面的分佈式方法,也能夠用於單機版本,也就是將總的數據根據值的範圍,劃分紅多個不一樣的子文件,而後逐個處理。處理完畢以後再對這些單詞的及其出現頻率進行一個歸併。實際上就能夠利用一個外排序的歸併過程。

另外還能夠考慮近似計算,也就是咱們能夠經過結合天然語言屬性,只將那些真正實際中出現最多的那些詞做爲一個字典,使得這個規模能夠放入內存。

相關文章
相關標籤/搜索