循序漸進VUE+Element 前端應用開發(7)— 介紹一些常規的JS處理函數

在我們使用VUE+Element 處理界面的時候,往往碰到需要利用JS集合處理的各種方法,如Filter、Map、reduce等方法,也可以涉及到一些對象屬性賦值等常規的處理或者遞歸的處理方法,以前對於這些不是很在意,但往往真正使用的時候,需要了解清楚,否則很容易腦袋出現短路的情況。本篇隨筆列出一些在VUE+Element 前端開發中經常碰到的JS處理場景,供參考學習。

1、常規集合的filter、map、reduce處理方法

filter函數的主要用途是對數組元素進行過濾,並返回一個符合條件的元素的數組

const nums = [10,20,30,111,222,333]
let newNums=nums.filter(function(n){
    return n<100
})

輸出:[10,20,30]

 

map函數是對數組每個元素的映射操作,並返回一個新數組,原數組不會改變將newNums中每個数字乘2

const nums = [10,20,30,111,222,333]
let newNums=nums.filter(function(n){
    return n*2
})

輸出:[20,40,60,222,666]

 

reduce函數主要用於對數組所有元素的匯總操作,如全部相加、相乘等

const nums = [10,20,30,111,222,333]
let newNums=nums.reduce(function(preValue,n){
    return PreValue+n
},0)

輸出:726

 

有時候可以結合幾種處理方式一起,如下綜合案例所示。

const nums = [10,20,30,111,222,333]
let newNums=nums.filter(function(n){
    return n<100
}).map(function(n){
    return n*2
}).reduce(function(preValue,n){
    return preValue+n
},0)

結果:120

 

另外還有一個數組集合的find方法,和filter方法類似。

find()方法主要用來返回數組中符合條件的第一個元素(沒有的話,返回undefined)

 var Array = [1,2,3,4,5,6,7];
 var result = Array.find(function(value){
     return value > 5;   //條件
 });
 console.log(result);//6
 console.log(Array);//[1,2,3,4,5,6,7]

 

同樣我們也可以在vue裏面,利用require.context的處理機制,遍歷文件進行處理,也需要用到了filter,如下代碼所示。

下面代碼是我對某個文件夾裏面的文件進行一個過濾處理操作

const req = require.context('vue-awesome/icons', true, /\.js$/)
const requireAll = requireContext => requireContext.keys()

const re = /\.\/(.*)\.js/

const vueAwesomeIcons = requireAll(req).filter((key) => { return key.indexOf('index.js') < 0 }).map(i => {
  return i.match(re)[1]
})

export default vueAwesomeIcons

 

2、遞歸處理

有時候,我們需要從一個JSON集合裏面,由於集合是嵌套的,如children裏面還有chilren集合,根據某個關鍵屬性進行查詢,這種處理方式就要用到遞歸了。

例如我定義的一個菜單集合裏面,就是這樣一個嵌套的結構,需要根據名稱來獲得對應的對象的時候,就涉及到了一個遞歸處理函數。

首先我們來看看菜單的JSON集合。

// 此菜單數據一般由服務器端返回
export const asyncMenus = [
  {
    id: '1',
    pid: '-1',
    text: '首頁',
    icon: 'dashboard',
    name: 'dashboard'
  },
  {
    id: '2',
    pid: '-1',
    text: '產品信息',
    icon: 'table',
    children: [
      {
        id: '2-1',
        pid: '2',
        text: '產品展示',
        name: 'product-show',
        icon: 'table'
      }]
  },
  {
    id: '3',
    pid: '-1',
    text: '雜項管理',
    icon: 'example',
    children: [
      {
        id: '3-1',
        pid: '3',
        text: '圖標管理',
        name: 'icon',
        icon: 'example'
      },
      {
        id: '3-3',
        pid: '3',
        text: '樹功能展示',
        name: 'tree',
        icon: 'tree'
      },
      {
        id: '3-2',
        pid: '3',
        text: '二級菜單2',
        icon: 'tree',
        children: [
          {
            id: '3-2-2',
            pid: '3-2',
            text: '三級菜單2',
            name: 'menu1-1',
            icon: 'form'
          }
        ]
      }
    ]
  }
]

如果我們需要根據ID來遍歷查詢,就是一個典型的遞歸查詢處理。

    // 根據菜單id來獲取對應菜單對象
    FindMenuById(menuList, menuid) {
      for (var i = 0; i < menuList.length; i++) {
        var item = menuList[i];
        if (item.id && item.id === menuid) {
          return item
        } else if (item.children) {
          var foundItem = this.FindMenuById(item.children, menuid)
          if (foundItem) { // 只有找到才返回
            return foundItem
          }
        }
      }
    }

這裏值得注意的是,不能在遞歸的時候,使用下面直接返回


return this.FindMenuById(item.children, menuid)

而需要判斷是否有結果在進行返回,否則嵌套遞歸就可能返回undefined類型

  var foundItem = this.FindMenuById(item.children, menuid)
  if (foundItem) { // 只有找到才返回
    return foundItem
  }

 

3、forEach遍歷集合處理

在很多場合,我們也需要對集合進行一個forEach的遍歷處理,如下根據它的鍵值進行處理,註冊全局過濾器的處理操作

// 導入全局過濾器
import * as filters from './filters'
// 註冊全局過濾器
Object.keys(filters).forEach(key => {
  Vue.filter(key, filters[key])
})

或者我們在通過API方式獲取數據后,對集合進行處理的操作

    // 獲取產品類型,用於綁定字典等用途
    GetProductType().then(data => {
      if (data) {
        this.treedata = [];// 樹列表清空
        data.forEach(item => {
          this.productTypes.set(item.id, item.name)
          this.typeList.push({ key: item.id, value: item.name })

          var node = { id: item.id, label: item.name }
          this.treedata.push(node)
        })

        // 獲取列表信息
        this.getlist()
      }
    });

又或者請求字典數據的時候,進行一個非空值的判斷處理。

      // 使用字典類型,從服務器請求數據
      GetDictData(this.typeName).then(data => {
        if (data) {
          data.forEach(item => {
            if (item && typeof (item.Value) !== 'undefined' && item.Value !== '') {
              that.dictItems.push(item)
            }
          });
        }
      })

forEach()方法也是用於對數組中的每一個元素執行一次回調函數,但它沒有返回值(或者說它的返回值為undefined,即便我們在回調函數中寫了return語句,返回值依然為undefined)

注意: 如果forEach里有兩個參數,則第一個參數為該集合里的元素,第二個參數為集合的索引;

 

4、Object.assign賦值方法

在有些場合,我們需要把全新的集合,複製到另一個對象上,替換原來對象的屬性值,那麼我們可以利用Object對象的assign方法。

如在編輯界面展示的時候,把請求到的對象屬性複製到表單對象上。

      var param = { id: id }
      GetProductDetail(param).then(data => {
        Object.assign(this.editForm, data);
      })

或者查詢的時候,獲得查詢條件,進行部分替換

      // 構造常規的分頁查詢條件
      var param = {
        type: this.producttype === 'all' ? '' : this.producttype,
        pageindex: this.pageinfo.pageindex,
        pagesize: this.pageinfo.pagesize
      };

      // 把SearchForm的條件加入到param裏面,進行提交查詢
      param.type = this.searchForm.ProductType // 轉換為對應屬性
      Object.assign(param, this.searchForm);

 

5、slice() 方法

slice() 方法可從已有的數組中返回選定的元素。

語法如下所示。

arrayObject.slice(start,end)

如下案例所示。

let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16)

或者我們結合filter函數對圖標集合進行獲取部分處理

vueAwesomeIconsFiltered: function() {
  const that = this
  var list = that.vueAwesomeIcons.filter(item => { return item.indexOf(that.searchForm.label) >= 0 })
  if (that.searchForm.pagesize > 0) {
    return list.slice(0, that.searchForm.pagesize)
  } else {
    return list;
  }
}

 

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

【其他文章推薦】

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

※台北網頁設計公司全省服務真心推薦

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

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

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

※別再煩惱如何寫文案,掌握八大原則!

四、歸併排序 && 快速排序

一、歸併排序 Merge Sort

1.1、實現原理

  • 如果要排序一個數組,我們先把數組從中間分成前後兩部分,然後對前後兩部分分別排序,再將排好序的兩部分合併在一起,這樣整個數組就都有序了。
  • 歸併排序使用的就是分治思想。分治,顧名思義,就是分而治之,將一個大問題分解成小的子問題來解決。小的子問題解決了,大問題也就解決了。
  • 分治思想跟遞歸思想很像。分治算法一般都是用遞歸來實現的。 分治是一種解決問題的處理思想,遞歸是一種編程技巧,這兩者並不衝突。
  • 寫遞歸代碼的技巧就是,分析得出遞推公式,然後找到終止條件,最後將遞推公式翻譯成遞歸代碼。所以,要想寫出歸併排序的代碼,我們先寫出歸併排序的遞推公式。
  • 遞推公式:erge_sort(p…r) = merge(merge_sort(p…q), merge_sort(q+1…r))
  • 終止條件:p >= r 不用再繼續分解
  • merge_sort(p…r)表示,給下標從 p 到 r 之間的數組排序。
  • 我們將這個排序問題轉化為了兩個子問題, merge_sort(p…q) 和 merge_sort(q+1…r),其中下標 q 等於 p 和 r 的中間位置,也就是 (p+r)/2。
  • 當下標從 p 到 q 和從 q+1 到 r 這兩個子數組都排好序之後,我們再將兩個有序的子數組合併在一起,這樣下標從 p 到 r 之間的數據就也排好序了。
  • 實現思路如下:
/**
 * 歸併排序
 * @param arr 排序數據
 * @param n   數組大小
 */
public static void merge_sort(int[] arr, int n) {
    merge_sort_c(arr, 0, n - 1);
}

// 遞歸調用函數
public static void merge_sort_c(int[] arr, int p, int r) {
    // 遞歸終止條件
    if (p >= r) {
        return;
    }
    // 取p到r之間的中間位置q
    int q = (p + r) / 2;

    // 分治遞歸
    merge_sort_c(arr, p, q);
    merge_sort_c(arr, q + 1, r);
    // 將 arr[p...q] 和 arr[q+1...r] 合併為 arr[p...r]
    merge(arr[p...r],arr[p...q],arr[q + 1...r]);
}
  • merge(arr[p…r], arr[p…q], arr[q + 1…r]) 這個函數的作用就是,將已經有序的 arr[p…q] 和 arr[q+1…r] 合併成一個有序的數組,並且放入 arr[p…r]。
  • 如下圖所示,我們申請一個臨時數組 tmp,大小與 arr[p…r] 相同。
  • 我們用兩個游標 i 和 j,分別指向 arr[p…q] 和 arr[q+1…r] 的第一個元素。
  • 比較這兩個元素 arr[i] 和 arr[j],如果 arr[i] <= arr[j],我們就把 arr[i] 放入到臨時數組 tmp,並且 i 后移一位,否則將 arr[j] 放入到數組 tmp,j 后移一位。
  • 繼續上述比較過程,直到其中一個子數組中的所有數據都放入臨時數組中,再把另一個數組中的數據依次加入到臨時數組的末尾,這個時候,臨時數組中存儲的就是兩個子數組合併之後的結果了。
  • 最後再把臨時數組 tmp 中的數據拷貝到原數組 arr[p…r] 中。
/**
 * merge 合併函數
 * @param arr 數組
 * @param p   數組頭
 * @param q   數組中間位置
 * @param r   數組尾
 */
public static void merge(int[] arr, int p, int q, int r) {
    if (r <= p) return;

    // 初始化變量i j k
    int i = p;
    int j = q + 1;
    int k = 0;

    // 申請一個大小跟A[p...r]一樣的臨時數組
    int[] tmp = new int[r - p + 1];

    // 比較排序移動到臨時數組
    while ((i <= q) && (j <= r)) {
        if (arr[i] <= arr[j]) {
            tmp[k++] = arr[i++];
        } else {
            tmp[k++] = arr[j++];
        }
    }

    // 判斷哪個子數組中有剩餘的數據
    int start = i, end = q;
    if (j <= r) {
        start = j;
        end = r;
    }

    // 將剩餘的數據拷貝到臨時數組tmp
    while (start <= end) {
        tmp[k++] = arr[start++];
    }

    // 將tmp中的數組拷貝回 arr[p...r]
    for (int a = 0; a <= r - p; a++) {
        arr[p + a] = tmp[a];
    }
}

1.2、性能分析

  • 歸併排序穩不穩定關鍵要看 merge() 函數,也就是兩個有序子數組合併成一個有序數組的那部分代碼。
  • 在合併的過程中,如果 arr[p…q] 和 arr[q+1…r] 之間有值相同的元素,那我們可以像偽代碼中那樣,先把 arr[p…q] 中的元素放入 tmp 數組。
  • 這樣就保證了值相同的元素,在合併前後的先後順序不變。所以,歸併排序是一個穩定的排序算法
  • 其時間複雜度是非常穩定的,不管是最好情況、最壞情況,還是平均情況,時間複雜度都是 O(nlogn)
  • 歸併排序的合併函數,在合併兩個有序數組為一個有序數組時,需要藉助額外的存儲空間。
  • 儘管每次合併操作都需要申請額外的內存空間,但在合併完成之後,臨時開闢的內存空間就被釋放掉了。在任意時刻,CPU 只會有一個函數在執行,也就只會有一個臨時的內存空間在使用。
  • 臨時內存空間最大也不會超過 n 個數據的大小,所以空間複雜度是 O(n),不是原地排序算法。

二、快速排序 Quicksort

2.1、實現原理

  • 快排的思想是:如果要排序數組中下標從 p 到 r 之間的一組數據,可以選擇 p 到 r 之間的任意一個數據作為 pivot(分區點)。
  • 遍歷 p 到 r 之間的數據,將小於 pivot 的放到左邊,將大於 pivot 的放到右邊,將 pivot 放到中間。
  • 經過這一步驟之後,數組 p 到 r 之間的數據就被分成了三個部分,前面 p 到 q-1 之間都是小於 pivot 的,中間是 pivot,後面的 q+1 到 r 之間是大於 pivot 的。
  • 根據分治、遞歸的處理思想,可以用遞歸排序下標從 p 到 q-1 之間的數據和下標從 q+1 到 r 之間的數據,直到區間縮小為 1,就說明所有的數據都有序了。
  • 用遞推公式來將上面的過程寫出來的話,就是這樣:quick_sort(p…r) = quick_sort(p…q-1) + quick_sort(q+1, r)。
  • 終止條件:p >= r
/**
 * 快速排序
 * @param arr 排序數組
 * @param p 數組頭
 * @param r 數組尾
 */
public static void quickSort(int[] arr, int p, int r) {
    if (p >= r) 
        return;
    // 獲取分區點 並移動數據
    int q = partition(arr, p, r);
    quickSort(arr, p, q - 1);
    quickSort(arr, q + 1, r);
}

partition() 分區函數:

  • 是隨機選擇一個元素作為 pivot(一般情況下,可以選擇 p 到 r 區間的最後一個元素),然後對 arr[p…r] 分區,並將小於 pivot 的放右邊,大於的放左邊,函數返回 pivot 的下標。

partition() 的實現有兩種方式:

  • 一種是不考慮空間消耗,此時非常簡單。

    • 申請兩個臨時數組 X 和 Y,遍歷 arr[p…r],將小於 pivot 的元素都拷貝到臨時數組 X,將大於 pivot 的元素都拷貝到臨時數組 Y,最後再將數組 X 和數組 Y 中數據順序拷貝到arr[p…r]。
    /**
     * 分區函數方式一
     *
     * @param arr 數組
     * @param p   上標
     * @param r   下標
     * @return 函數返回 pivot 的下標
     */
    public static int partition1(int[] arr, int p, int r) {
        int[] xArr = new int[r - p + 1];
        int x = 0;
    
        int[] yArr = new int[r - p + 1];
        int y = 0;
    
        int pivot = arr[r];
    
        // 將小於 pivot 的元素都拷貝到臨時數組 X,將大於 pivot 的元素都拷貝到臨時數組 Y
        for (int i = p; i < r; i++) {
            // 小於 pivot 的存入 xArr 數組
            if (arr[i] < pivot) {
                xArr[x++] = arr[i];
            }
            // 大於 pivot 的存入 yArr 數組
            if (arr[i] > pivot) {
                yArr[y++] = arr[i];
            }
        }
    
        int q = x + p;
        // 再將數組 X 和數組 Y 中數據順序拷貝到 arr[p…r]
        for (int i = 0; i < x; i++) {
            arr[p + i] = xArr[i];
        }
        arr[q] = pivot;
        for (int i = 0; i < y; i++) {
            arr[q + 1 + i] = yArr[i];
        }
    
        return q;
    }
    
  • 另外一種有點類似選擇排序。

    • 我們通過游標 i 把 arr[p…r-1] 分成兩部分。arr[p…i-1] 的元素都是小於 pivot 的,我們暫且叫它“已處理區間”,arr[i…r-1] 是“未處理區間”。
    • 我們每次都從未處理的區間 arr[i…r-1] 中取一個元素 arr[j],與 pivot 對比,如果小於 pivot,則將其加入到已處理區間的尾部,也就是 arr[i]的位置。
    • 在數組某個位置插入元素,需要搬移數據,非常耗時。此時可以採用交換,在 O(1) 的時間複雜度內完成插入操作。需要將 arr[i] 與 arr[j] 交換,就可以在 O(1)時間複雜度內將 arr[j] 放到下標為 i 的位置。
    /**
     * 分區函數方式二
     * @param arr 數組
     * @param p   上標
     * @param r   下標
     * @return 函數返回pivot的下標
     */
    public static int partition2(int[] arr, int p, int r) {
        int pivot = arr[r];
        int i = p;
        for (int j = p; j < r; j++) {
            if (arr[j] < pivot) {
                if (i == j) {
                    ++i;
                } else {
                    int tmp = arr[i];
                    arr[i++] = arr[j];
                    arr[j] = tmp;
                }
            }
        }
        int tmp = arr[i];
        arr[i] = arr[r];
        arr[r] = tmp;
        return i;
    }
    

2.2、性能分析

  • 因為分區的過程涉及交換操作,如果數組中有兩個相同的元素,比如序列 6, 8, 7, 6, 3, 5, 9, 4,在經過第一次分區操作之後,兩個 6 的相對先後順序就會改變。所以,快速排序並不是穩定的排序算法
  • 按照上面的第二種分區方式,快速排序只涉及交換操作,所以空間複雜度為 Q(1),是原地排序算法
  • 時間複雜度為 Q(nlogn),最差為Q(n²)

三、兩者對比

歸併排序 快速排序
排序思想 處理過程由下到上,先處理子問題,然後在合併 由上到下,先分區,在處理子問題
穩定性
空間複雜度 Q(n) Q(1) 原地排序算法
時間複雜度 都為 O(nlogn) 平均為 O(nlogn),最差為 O(n²)
  • 歸併之所以是非原地排序算法,主要原因是合併函數無法在原地執行。快速排序通過設計巧妙的原地分區函數,可以實現原地排序,解決了歸併排序佔用太多內存的問題。
  • 歸併排序算法是一種在任何情況下時間複雜度都比較穩定的排序算法,這也使它存在致命的缺點,即歸併排序不是原地排序算法,空間複雜度比較高,是 O(n)。正因為此,它也沒有快排應用廣泛。

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

【其他文章推薦】

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

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

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

※別再煩惱如何寫文案,掌握八大原則!

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

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

全球最大煤業投資者貝萊德倒戈 宣布投資組合將排除煤

摘錄自2020年2月4日科技新報報導

許多投資機構在這樣的潮流下宣示不再投資碳排放量高的產業,其中以煤業為指標。貝萊德(BlackRock)原本是全球投資煤業規模最大的投資機構,如今倒戈加入全球 117 個宣示投資組合排除煤業的大型投資機構行列,宣示投資煤業不僅可能造成商譽受損,更可能直接造成財務風險。

貝萊德管理 7 兆美元資產,在煤業有 170 億美元,如今覺得煤業資產越來越像燙手山芋,2020 年 1 月,執行長  Larry Fink 撰寫公開信,表示氣候變遷已成為企業長期表現的決定因素,過去市場對此反應緩慢,但如今投資者越來越開始認知到氣候變遷對投資的影響。

在這樣的壓力下,貝萊德順應客戶,進行投資組合調整,將有相當龐大的資本重新配置,貝萊德已於 2020 年 1 月初加入「氣候行動百強」(Climate Action 100+)宣示加入綠色投資的潮流,如今更明確提出減碳投資方案,首先就是針對煤。

貝萊德轉向顯示,綠色投資成主流,不進行綠色投資、迴避高碳排資產的投資機構,將面臨客戶與市場越來越高的質疑,造成資金成本提升,直接造成財務風險。

環境經濟
循環經濟
國際新聞
氣候變遷
綠色金融
煤炭

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

「一帶一路」再受挫 泰國喊停中國湄公河開發計畫

摘錄自2020年2月6日聯合新聞網報導

泰國宣布停止由中國主導的湄公河開發計畫,理由是當地民眾反對,且北京不願提高經費在要開發的地區進行進一步調查。泰國政府副發言人特薩拉薩納庫爾(Trisulee Trisaranakul)表示,泰國政府內閣會議4日決定放棄這個項目。

路透報導,中國在2001年提出計畫,對湄公河進行疏浚,以方便大型貨船從中國的雲南省沿湄公河把商品運往泰國、老撾的港口和東南亞其他地區。

但這個計畫遭到湄公河沿岸的泰國社區民眾和環保人士的反對。他們擔心疏浚計畫會影響環境,而且只能讓中國獲利。一份泰國政府內閣的文件顯示,中國去年通知湄公河流沿岸的國家說,中國不再計畫繼續推動這個項目,可老撾和緬甸境內的疏浚工作依然繼續進行。

土地水文
生物多樣性
土地利用
國際新聞
泰國
湄公河
一帶一路
商業開發
水文

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

※台北網頁設計公司全省服務真心推薦

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

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

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

※別再煩惱如何寫文案,掌握八大原則!

特斯拉達成「第100萬輛」電動車里程碑 單季產能也意外曝光

摘錄自2020年3月11日ETtoday報導

自2003年創立的美國電動車廠特斯拉(Tesla),近日在一輛紅色Model Y從產線離開後,正式達成量產第100萬台電動車的里程碑。儘管目前Model Y電動休旅車尚未進入交車階段,但執行長Elon Musk也提前在推特上特別慶祝一番。

實際來看,特斯拉達成量產100萬輛的里程碑並不讓人意外,因為除了美國Fremont工廠的每年50萬輛的產能外,特斯拉上海工廠也在去年正式完工, 並預計在今年生產15萬輛新車,若非新冠病毒(COVID-19)疫情影響,這項紀錄將會更早達成。

從特斯拉Fremont工廠、上海工廠的年產量來看,預計很快就會迎來第200萬輛的紀錄。

生活環境
能源轉型
國際新聞
美國
特斯拉
電動車
交通運輸

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

※回頭車貨運收費標準

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

※別再煩惱如何寫文案,掌握八大原則!

防疫比環保重要?新罕布什爾州暫時重推塑膠袋

摘錄自2020年03月22日中央通訊社美國報導

美國新罕布什爾州長蘇努努今天(22日)敦促州內民眾,將可重複使用的環保袋留在家中暫時不用。他說,為對抗武漢肺炎,在此過渡期,購物宜用店家提供的新塑膠袋或紙袋。

此舉主要是愈發擔心賣場員工面對店內人潮擁擠,加上新型冠狀病毒疾病(COVID-19,武漢肺炎)具高度傳染性,且可能附著於各種物件表面,增加感染風險。州長蘇努努(Christopher Sununu)今天推文說:「由於確認(武漢肺炎病毒)社區傳染,顧及賣場裝袋人員、雜貨商和顧客潛在風險,購物者將環保袋暫留家中,這很重要。」

本週在新英格蘭醫學期刊(New England Journal of Medicine)發表的研究顯示,武漢肺炎病毒可在空氣中存活數小時,而在不同的物件表面甚至可存活數天之久。

公害污染
污染治理
國際新聞
美國
環保袋
武漢肺炎
疫情下的食衣住行
廢棄物

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

※教你寫出一流的銷售文案?

※超省錢租車方案

※回頭車貨運收費標準

疫情衝擊泰國觀光業 大象賺不到生活費恐餓死

摘錄自2020年3月31日中央社報導

隨著武漢肺炎大流行讓觀光客銳減,倡議人士警告,在泰國旅遊業工作的許多飢餓並遭長時間鍊住的大象,可能會因沒有收入而餓死、被賣給動物園,或轉入非法伐木業。

在疫情爆發前,在泰國旅遊業工作的約2000頭大象生活已不容易,時常傳出虐待情事,好馴化牠們提供乘載服務或在動物秀上表演雜耍賺錢。但因全球旅遊業停擺,大象現在甚至賺不到一天必須的300公斤食物錢。

大象生態營與保育人士警告,再不獲得緊急援助,象群即將面臨飢餓及新的剝削威脅。泰國大象聯盟協會(Thai Elephant Alliance Association)主席提拉帕(Theerapat Trungprakan)表示,隨著病毒重挫泰國觀光業,約2000頭大象現在失業了。

國際新聞
泰國
大象
觀光業
動物與大環境變遷
武漢肺炎
展示動物
動物福利

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

※別再煩惱如何寫文案,掌握八大原則!

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

※回頭車貨運收費標準

荷蘭研究:城市污水測得病毒 或可扮演疫情預警

摘錄自2020年3月31日中央社報導

荷蘭科學家可以在一個城市的2019冠狀病毒疾病(COVID-19,武漢肺炎)病例通報前,在都市污水中發現引發這種疾病的冠狀病毒,顯示這或許有可能成為新疫情早期預警系統。

所謂的SARS-CoV-2冠狀病毒通常會從感染者的糞便中排出。位於荷蘭中部城市尼沃海恩(Nieuwegein)的KWR水資源研究所(KWR Water Research Institute)的首席微生物學家麥德瑪(Gertjan Medema)和他的同僚今(30日)表示,儘管污水不太可能成為重要的傳播途徑。但病原體在社區裡不斷增加的循環會增加它流入下水道系統的數量。

他們3月5日在阿默斯福特(Amersfoort)一座污水處理廠發現武漢肺炎的遺傳物質,當時這個位於阿姆斯特丹東南方約50公里處的城市還沒傳出任何病例。荷蘭2月27日出現境內首起2019冠狀病毒疾病病例,幾天後南部的醫療人員感染生病,顯示疫情已在社區蔓延。

他們表示,即使在武漢肺炎盛行率還很低時,就可以在污水中偵測到引發這種疾病的冠狀病毒,顯示這可以當成監控這種病毒在人口中循環的高敏感度工具。

公害污染
污染治理
國際新聞
荷蘭
疫情
水污染

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

※教你寫出一流的銷售文案?

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

分解塑膠救星?科學家在垃圾場中發現吃聚氨酯的微生物

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

※別再煩惱如何寫文案,掌握八大原則!

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

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

Java多線程之深入解析ThreadLocal和ThreadLocalMap

ThreadLocal概述

ThreadLocal是線程變量,ThreadLocal中填充的變量屬於當前線程,該變量對其他線程而言是隔離的。ThreadLocal為變量在每個線程中都創建了一個副本,那麼每個線程可以訪問自己內部的副本變量。

它具有3個特性:

  1. 線程併發:在多線程併發場景下使用。
  2. 傳遞數據:可以通過ThreadLocal在同一線程,不同組件中傳遞公共變量。
  3. 線程隔離:每個線程變量都是獨立的,不會相互影響。

在不使用ThreadLocal的情況下,變量不隔離,得到的結果具有隨機性。

public class Demo {
    private String variable;

    public String getVariable() {
        return variable;
    }

    public void setVariable(String variable) {
        this.variable = variable;
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                demo.setVariable(Thread.currentThread().getName());
                System.out.println(Thread.currentThread().getName()+" "+demo.getVariable());
            }).start();
        }
    }
}

輸出結果:

Thread-2 Thread-2
Thread-4 Thread-4
Thread-1 Thread-2
Thread-0 Thread-2
Thread-3 Thread-3

View Code

在不使用ThreadLocal的情況下,變量隔離,每個線程有自己專屬的本地變量variable,線程綁定了自己的variable,只對自己綁定的變量進行讀寫操作。

public class Demo {
    private ThreadLocal<String> variable = new ThreadLocal<>();

    public String getVariable() {
        return variable.get();
    }

    public void setVariable(String variable) {
        this.variable.set(variable);
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                demo.setVariable(Thread.currentThread().getName());
                System.out.println(Thread.currentThread().getName()+" "+demo.getVariable());
            }).start();
        }
    }
}

輸出結果:

Thread-0 Thread-0
Thread-1 Thread-1
Thread-2 Thread-2
Thread-3 Thread-3
Thread-4 Thread-4

View Code

synchronized和ThreadLocal的比較

上述需求,通過synchronized加鎖同樣也能實現。但是加鎖對性能和併發性有一定的影響,線程訪問變量只能排隊等候依次操作。TreadLocal不加鎖,多個線程可以併發對變量進行操作。

public class Demo {
    private String variable;
    public String getVariable() {
        return variable;
    }

    public void setVariable(String variable) {
        this.variable = variable;
    }

    public static void main(String[] args) {
        Demo demo = new Demo1();
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                synchronized (Demo.class){
                    demo.setVariable(Thread.currentThread().getName());
                    System.out.println(Thread.currentThread().getName()+" "+demo.getVariable());
                }
            }).start();
        }
    }
}

ThreadLocal和synchronized都是用於處理多線程併發訪問資源的問題。ThreadLocal是以空間換時間的思路,每個線程都擁有一份變量的拷貝,從而實現變量隔離,互相不干擾。關注的重點是線程之間數據的相互隔離關係。synchronized是以時間換空間的思路,只提供一個變量,線程只能通過排隊訪問。關注的是線程之間訪問資源的同步性。ThreadLocal可以帶來更好的併發性,在多線程、高併發的環境中更為合適一些。

ThreadLocal使用場景

轉賬事務的例子

JDBC對於事務原子性的控制可以通過setAutoCommit(false)設置為事務手動提交,成功后commit,失敗后rollback。在多線程的場景下,在service層開啟事務時用的connection和在dao層訪問數據庫的connection應該要保持一致,所以併發時,線程只能隔離操作自已的connection。

解決方案1:service層的connection對象作為參數傳遞給dao層使用,事務操作放在同步代碼塊中。

存在問題:傳參提高了代碼的耦合程度,加鎖降低了程序的性能。

解決方案2:當需要獲取connection對象的時候,通過ThreadLocal對象的get方法直接獲取當前線程綁定的連接對象使用,如果連接對象是空的,則去連接池獲取連接,並通過ThreadLocal對象的set方法綁定到當前線程。使用完之後調用ThreadLocal對象的remove方法解綁連接對象。

ThreadLocal的優勢:

  1. 可以方便地傳遞數據:保存每個線程綁定的數據,需要的時候可以直接獲取,避免了傳參帶來的耦合。
  2. 可以保持線程間隔離:數據的隔離在併發的情況下也能保持一致性,避免了同步的性能損失。

ThreadLocal的原理

每個ThreadLocal維護一個ThreadLocalMap,Map的Key是ThreadLocal實例本身,value是要存儲的值。

每個線程內部都有一個ThreadLocalMap,Map裏面存放的是ThreadLocal對象和線程的變量副本。Thread內部的Map通過ThreadLocal對象來維護,向map獲取和設置變量副本的值。不同的線程,每次獲取變量值時,只能獲取自己對象的副本的值。實現了線程之間的數據隔離。

JDK1.8的設計相比於之前的設計(通過ThreadMap維護了多個線程和線程變量的對應關係,key是Thread對象,value是線程變量)的好處在於,每個Map存儲的Entry數量變少了,線程越多鍵值對越多。現在的鍵值對的數量是由ThreadLocal的數量決定的,一般情況下ThreadLocal的數量少於線程的數量,而且並不是每個線程都需要創建ThreadLocal變量。當Thread銷毀時,ThreadLocal也會隨之銷毀,減少了內存的使用,之前的方案中線程銷毀后,ThreadLocalMap仍然存在。

ThreadLocal源碼解析

set方法

首先獲取線程,然後獲取線程的Map。如果Map不為空則將當前ThreadLocal的引用作為key設置到Map中。如果Map為空,則創建一個Map並設置初始值。

get方法

首先獲取當前線程,然後獲取Map。如果Map不為空,則Map根據ThreadLocal的引用來獲取Entry,如果Entry不為空,則獲取到value值,返回。如果Map為空或者Entry為空,則初始化並獲取初始值value,然後用ThreadLocal引用和value作為key和value創建一個新的Map。

 

remove方法

刪除當前線程中保存的ThreadLocal對應的實體entry。

initialValue方法

該方法的第一次調用發生在當線程通過get方法訪問線程的ThreadLocal值時。除非線程先調用了set方法,在這種情況下,initialValue才不會被這個線程調用。每個線程最多調用依次這個方法。

該方法只返回一個null,如果想要線程變量有初始值需要通過子類繼承ThreadLocal的方式去重寫此方法,通常可以通過匿名內部類的方式實現。這個方法是protected修飾的,是為了讓子類覆蓋而設計的。

ThreadLocalMap源碼分析

ThreadLocalMap是ThreadLocal的靜態內部類,沒有實現Map接口,獨立實現了Map的功能,內部的Entry也是獨立實現的。

與HashMap類似,初始容量默認是16,初始容量必須是2的整數冪。通過Entry類的數據table存放數據。size是存放的數量,threshold是擴容閾值。

 Entry繼承自WeakReference,key是弱引用,其目的是將ThreadLocal對象的生命周期和線程生命周期解綁。

弱引用和內存泄漏

內存溢出:沒有足夠的內存供申請者提供

內存泄漏:程序中已動態分配的堆內存由於某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等驗證后溝。內存泄漏的堆積會導致內存溢出。

弱引用:垃圾回收器一旦發現了弱引用的對象,不管內存是否足夠,都會回收它的內存。

內存泄漏的根源是ThreadLocalMap和Thread的生命周期是一樣長的。

如果在ThreadLocalMap的key使用強引用還是無法完全避免內存泄漏,ThreadLocal使用完后,ThreadLocal Reference被回收,但是Map的Entry強引用了ThreadLocal,ThreadLocal就無法被回收,因為強引用鏈的存在,Entry無法被回收,最後會內存泄漏。

在實際情況中,ThreadLocalMap中使用的key為ThreadLocal的弱引用,value是強引用。如果ThreadLocal沒有被外部強引用的話,在垃圾回收的時候,key會被清理,value不會。這樣ThreadLocalMap就出現了為null的Entry。如果不做任何措施,value永遠不會被GC回收,就會產生內存泄漏。

ThreadLocalMap中考慮到這個情況,在set、get、remove操作后,會清理掉key為null的記錄(將value也置為null)。使用完ThreadLocal后最後手動調用remove方法(刪除Entry)。

也就是說,使用完ThreadLocal后,線程仍然運行,如果忘記調用remove方法,弱引用比強引用可以多一層保障,弱引用的ThreadLocal會被回收,對應的value會在下一次ThreadLocalMap調用get、set、remove方法的時候被清除,從而避免了內存泄漏。

Hash衝突的解決

ThreadLocalMap的構造方法

構造函數創建一個長隊為16的Entry數組,然後計算firstKey的索引,存儲到table中,設置size和threshold。

firstKey.threadLocalHashCode & (INITIAL_CAPACITY-1)用來計算索引,nextHashCode是Atomicinteger類型的,Atomicinteger類是提供原子操作的Integer類,通過線程安全的方式來加減,適合高併發使用。

每次在當前值上加上一個HASH_INCREMENT值,這個值和斐波拉契數列有關,主要目的是為了讓哈希碼可以均勻的分佈在2的n次方的數組裡,從而盡量的避免衝突。

當size為2的冪次的時候,hashCode & (size – 1)相當於取模運算hashCode % size,位運算比取模更高效一些。為了使用這種取模運算, 所有size必須是2的冪次。這樣一來,在保證索引不越界的情況下,減少衝突的次數。

ThreadLocalMap的set方法

ThreadLocalMao使用了線性探測法來解決衝突。線性探測法探測下一個地址,找到空的地址則插入,若整個空間都沒有空餘地址,則產生溢出。例如:長度為8的數組中,當前key的hash值是6,6的位置已經被佔用了,則hash值加一,尋找7的位置,7的位置也被佔用了,回到0的位置。直到可以插入為止,可以將這個數組看成一個環形數組

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

【其他文章推薦】

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

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

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

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

※回頭車貨運收費標準