1. 程式人生 > 其它 >用C++的random庫生成更好的隨機數

用C++的random庫生成更好的隨機數

技術標籤:java設計模式大資料redis面試

享元模式

1 概述

定義:

​ 運用共享技術來有效地支援大量細粒度物件的複用。它通過共享已經存在的物件來大幅度減少需要建立的物件數量、避免大量相似物件的開銷,從而提高系統資源的利用率。

2 結構

享元(Flyweight )模式中存在以下兩種狀態:

  1. 內部狀態,即不會隨著環境的改變而改變的可共享部分。
  2. 外部狀態,指隨環境改變而改變的不可以共享的部分。享元模式的實現要領就是區分應用中的這兩種狀態,並將外部狀態外部化。

享元模式的主要有以下角色:

  • 抽象享元角色(Flyweight):通常是一個介面或抽象類,在抽象享元類中聲明瞭具體享元類公共的方法,這些方法可以向外界提供享元物件的內部資料(內部狀態),同時也可以通過這些方法來設定外部資料(外部狀態)。
  • 具體享元(Concrete Flyweight)角色 :它實現了抽象享元類,稱為享元物件;在具體享元類中為內部狀態提供了儲存空間。通常我們可以結合單例模式來設計具體享元類,為每一個具體享元類提供唯一的享元物件。
  • 非享元(Unsharable Flyweight)角色 :並不是所有的抽象享元類的子類都需要被共享,不能被共享的子類可設計為非共享具體享元類;當需要一個非共享具體享元類的物件時可以直接通過例項化建立。
  • 享元工廠(Flyweight Factory)角色 :負責建立和管理享元角色。當客戶物件請求一個享元物件時,享元工廠檢査系統中是否存在符合要求的享元物件,如果存在則提供給客戶;如果不存在的話,則建立一個新的享元物件。

程式碼如下:

/**
 * @author WGR
 * @create 2021/1/13 -- 21:11
 */
public  abstract class Animal {

    public abstract String getName();

    public void getInfo(Integer age){
        System.out.println("age:"+age+",name:"+getName());
    }
}


/**
 * @author WGR
 * @create 2021/1/13 -- 21:18
 */
public class Cat extends Animal {
    @Override
    public String getName() {
        return "貓";
    }
}


/**
 * @author WGR
 * @create 2021/1/13 -- 21:20
 */
public class Dog extends Animal  {
    @Override
    public String getName() {
        return "狗";
    }
}


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author WGR
 * @create 2021/1/13 -- 21:25
 */
public class AnimalFactory {

    private static Map<String,Animal> animals = new ConcurrentHashMap<>();

    public static Animal getAnimal(String key){
        Animal animal;
        if(!animals.containsKey(key)){
            if("貓".equals(key)){
                animal = new Cat();
            }else{
                animal = new Dog();
            }
            animals.put(key, animal);
        }
        return animals.get(key);

    };
}

測試程式碼:

/**
 * @author WGR
 * @create 2021/1/13 -- 21:34
 */
public class Test {

    public static void main(String[] args) {
        Animal cat1 = AnimalFactory.getAnimal("貓");
        Animal cat2 = AnimalFactory.getAnimal("貓");
        Animal dog = AnimalFactory.getAnimal("狗");
        cat1.getInfo(13);
        cat2.getInfo(12);
        dog.getInfo(11);
        System.out.println(cat1 == cat2);
    }
}

image-20210113215941217

3 優缺點和使用場景

1,優點

  • 極大減少記憶體中相似或相同物件數量,節約系統資源,提供系統性能
  • 享元模式中的外部狀態相對獨立,且不影響內部狀態

2,缺點:

為了使物件可以共享,需要將享元物件的部分狀態外部化,分離內部狀態和外部狀態,使程式邏輯複雜

3,使用場景:

  • 一個系統有大量相同或者相似的物件,造成記憶體的大量耗費。
  • 物件的大部分狀態都可以外部化,可以將這些外部狀態傳入物件中。
  • 在使用享元模式時需要維護一個儲存享元物件的享元池,而這需要耗費一定的系統資源,因此,應當在需要多次重複使用享元物件時才值得使用享元模式。

4 JDK原始碼解析

Integer類使用了享元模式。我們先看下面的例子:

/**
 * @author WGR
 * @create 2021/1/13 -- 22:00
 */
public class Demo {
    public static void main(String[] args) {
        Integer i1 = 127;
        Integer i2 = 127;

        System.out.println("i1和i2物件是否是同一個物件?" + (i1 == i2));

        Integer i3 = 128;
        Integer i4 = 128;

        System.out.println("i3和i4物件是否是同一個物件?" + (i3 == i4));
    }
}

image-20210113220118900

為什麼第一個輸出語句輸出的是true,第二個輸出語句輸出的是false?通過反編譯軟體進行反編譯,程式碼如下:

public class Demo {
    public static void main(String[] args) {
        Integer i1 = Integer.valueOf((int)127);
        Integer i2 Integer.valueOf((int)127);
        System.out.println((String)new StringBuilder().append((String)"i1\u548ci2\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i1 == i2)).toString());
        Integer i3 = Integer.valueOf((int)128);
        Integer i4 = Integer.valueOf((int)128);
        System.out.println((String)new StringBuilder().append((String)"i3\u548ci4\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i3 == i4)).toString());
    }
}

上面程式碼可以看到,直接給Integer型別的變數賦值基本資料型別資料的操作底層使用的是 valueOf() ,所以只需要看該方法即可

public final class Integer extends Number implements Comparable<Integer> {
    
	public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                }
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
}

可以看到 Integer 預設先建立並快取 -128 ~ 127 之間數的 Integer 物件,當呼叫 valueOf 時如果引數在 -128 ~ 127 之間則計算下標並從快取中返回,否則建立一個新的 Integer 物件。