主頁 > 後端開發 > Java8特性詳解 lambda運算式(一):使用篇

Java8特性詳解 lambda運算式(一):使用篇

2021-12-14 06:12:55 後端開發

?

在 Java 8之前,一個實作了只有一個抽象方法的介面的匿名類看起來更像Lambda 運算式,下面的代碼中,anonymousClass方法呼叫waitFor方法,引數是一個實作介面的Condition類,實作的功能為,當滿足某些條件,Server 就會關閉, 下面的代碼是典型的匿名類的使用,

void anonymousClass() {
    final Server server = new HttpServer();
    waitFor(new Condition() {
        @Override
        public Boolean isSatisfied() {
            return !server.isRunning();
        }
    }
復制代碼

下面的代碼用 Lambda 運算式實作相同的功能:

void closure() { 
     Server server = new HttpServer();
     waitFor(() -> !server.isRunning()); 
 }
復制代碼

其實,上面的waitFor方法,更接近于下面的代碼的描述:

class WaitFor {
    static void waitFor(Condition condition) throws   
    InterruptedException {
        while (!condition.isSatisfied())
            Thread.sleep(250);
    }
}
復制代碼

一些理論上的區別 實際上,上面的兩種方法的實作都是閉包,后者的實作就是Lambda 表示式,這就意味著兩者都需要持有運行時的環境,在 Java 8 之前,這就需要把匿名類所需要的一切復制給它,在上面的例子中,就需要把 server 屬性復制給匿名類,

因為是復制,變數必須宣告為 final 型別,以保證在獲取和使用時不會被改變,Java 使用了優雅的方式保證了變數不會被更新,所以我們不用顯式地把變數加上 final 修飾,

Lambda 運算式則不需要拷貝變數到它的運行環境中,從而 Lambda 運算式被當做是一個真正的方法來對待,而不是一個類的實體,

Lambda 運算式不需要每次都要被實體化,對于 Java 來說,帶來巨大的好處,不像實體化匿名類,對記憶體的影響可以降到最小,

總體來說,匿名方法和匿名類存在以下區別:

類必須實體化,而方法不必; 當一個類被新建時,需要給物件分配記憶體; 方法只需要分配一次記憶體,它被存盤在堆的永久區內; 物件作用于它自己的資料,而方法不會; 靜態類里的方法類似于匿名方法的功能,

一些具體的區別 匿名方法和匿名類有一些具體的區別,主要包括獲取語意和覆寫變數,

獲取語意 this 關鍵字是其中的一個語意上的區別,在匿名類中,this 指的是匿名類的實體,例如有了內部類為 Foo$InnerClass,當你參考內部類閉包的作用域時,像Foo.this.x的代碼看起來就有些奇怪, 在 Lambda 運算式中,this 指的就是閉包作用域,事實上,Lambda 運算式就是一個作用域,這就意味著你不需要從超類那里繼承任何名字,或是引入作用域的層級,你可以在作用域里直接訪問屬性,方法和區域變數, 例如,下面的代碼中,Lambda 運算式可以直接訪問firstName變數,

public class Example {
    private String firstName = "Tom";

    public void example() {
        Function<String, String> addSurname = surname -> {
            // equivalent to this.firstName
            return firstName + " " + surname;  // or even,   
        };
    }
}
復制代碼

這里的firstName就是this.firstName的簡寫, 但是在匿名類中,你必須顯式地呼叫firstName,

public class Example {
    private String firstName = "Jerry";

    public void anotherExample() {
        Function<String, String> addSurname = new Function<String,  
        String>() {
            @Override
            public String apply(String surname) {
                return Example.this.firstName + " " + surname;   
            }
        };
    }
}

1.lambda運算式

Java8最值得學習的特性就是Lambda運算式和Stream API,如果有python或者javascript的語言基礎,對理解Lambda運算式有很大幫助,因為Java正在將自己變的更高(Sha)級(Gua),更人性化,--------可以這么說lambda運算式其實就是實作SAM介面的語法糖,

lambda寫的好可以極大的減少代碼冗余,同時可讀性也好過冗長的內部類,匿名類,

先列舉兩個常見的簡化(簡單的代碼同樣好理解)

  • 創建執行緒

?

  • 排序

?

lambda運算式配合Java8新特性Stream API可以將業務功能通過函式式編程簡潔的實作,(為后面的例子做鋪墊)

例如:

?

這段代碼就是對一個字串的串列,把其中包含的每個字串都轉換成全小寫的字串,注意代碼第四行的map方法呼叫,這里map方法就是接受了一個lambda運算式,

1.1lambda運算式語法

1.1.1lambda運算式的一般語法

(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
  statment1;
  statment2;
  //.............
  return statmentM;
}

這是lambda運算式的完全式語法,后面幾種語法是對它的簡化,

1.1.2單引數語法

param1 -> {
  statment1;
  statment2;
  //.............
  return statmentM;
}

當lambda運算式的引數個數只有一個,可以省略小括號

例如:將串列中的字串轉換為全小寫

List proNames = Arrays.asList(new String[]{"Ni","Hao","Lambda"});
List lowercaseNames1 = proNames.stream().map(name -> {return name.toLowerCase();}).collect(Collectors.toList());

1.1.3單陳述句寫法

param1 -> statment

當lambda運算式只包含一條陳述句時,可以省略大括號、return和陳述句結尾的分號

例如:將串列中的字串轉換為全小寫

List proNames = Arrays.asList(new String[]{"Ni","Hao","Lambda"});

List lowercaseNames2 = proNames.stream().map(name -> name.toLowerCase()).collect(Collectors.toList());

1.1.4方法參考寫法

(方法參考和lambda一樣是Java8新語言特性,后面會講到)

Class or instance :: method

例如:將串列中的字串轉換為全小寫

List proNames = Arrays.asList(new String[]{"Ni","Hao","Lambda"});

List lowercaseNames3 = proNames.stream().map(String::toLowerCase).collect(Collectors.toList());

1.2lambda運算式可使用的變數

先舉例:

//將為串列中的字串添加前綴字串
String waibu = "lambda :";
List proStrs = Arrays.asList(new String[]{"Ni","Hao","Lambda"});
ListexecStrs = proStrs.stream().map(chuandi -> {
Long zidingyi = System.currentTimeMillis();
return waibu + chuandi + " -----:" + zidingyi;
}).collect(Collectors.toList());
execStrs.forEach(System.out::println);

輸出:

lambda :Ni -----:1474622341604
lambda :Hao -----:1474622341604
lambda :Lambda -----:1474622341604

變數waibu :外部變數

變數chuandi :傳遞變數

變數zidingyi :內部自定義變數

lambda運算式可以訪問給它傳遞的變數,訪問自己內部定義的變數,同時也能訪問它外部的變數,

不過lambda運算式訪問外部變數有一個非常重要的限制:變數不可變(只是參考不可變,而不是真正的不可變),

當在運算式內部修改waibu = waibu + " ";時,IDE就會提示你:

Local variable waibu defined in an enclosing scope must be final or effectively final

編譯時會報錯,因為變數waibu被lambda運算式參考,所以編譯器會隱式的把其當成final來處理,

以前Java的匿名內部類在訪問外部變數的時候,外部變數必須用final修飾,現在java8對這個限制做了優化,可以不用顯示使用final修飾,但是編譯器隱式當成final來處理,

1.3lambda運算式中的this概念

在lambda中,this不是指向lambda運算式產生的那個SAM物件,而是宣告它的外部物件,

例如:

public class WhatThis {

 public void whatThis(){
       //轉全小寫
       List<String> proStrs = Arrays.asList(new String[]{"Ni","Hao","Lambda"});
       List<String> execStrs = proStrs.stream().map(str -> {
             System.out.println(this.getClass().getName());
             return str.toLowerCase();
       }).collect(Collectors.toList());
       execStrs.forEach(System.out::println);
 }

 public static void main(String[] args) {
       WhatThis wt = new WhatThis();
       wt.whatThis();
 }

}

輸出:

com.wzg.test.WhatThis
com.wzg.test.WhatThis
com.wzg.test.WhatThis
ni
hao
lambda

2.方法參考和構造器參考

本人認為是進一步簡化lambda運算式的宣告的一種語法糖,

前面的例子中已有使用到: execStrs.forEach(System.out::println);

2.1方法參考

objectName::instanceMethod

ClassName::staticMethod

ClassName::instanceMethod

前兩種方式類似,等同于把lambda運算式的引數直接當成instanceMethod|staticMethod的引數來呼叫,比如System.out::println等同于x->System.out.println(x);Math::max等同于(x, y)->Math.max(x,y),

最后一種方式,等同于把lambda運算式的第一個引數當成instanceMethod的目標物件,其他剩余引數當成該方法的引數,比如String::toLowerCase等同于x->x.toLowerCase(),

可以這么理解,前兩種是將傳入物件當引數執行方法,后一種是呼叫傳入物件的方法,

2.2構造器參考

構造器參考語法如下:ClassName::new,把lambda運算式的引數當成ClassName構造器的引數 ,例如BigDecimal::new等同于x->new BigDecimal(x),

3.Stream語法

兩句話理解Stream:

1.Stream是元素的集合,這點讓Stream看起來用些類似Iterator;
2.可以支持順序和并行的對原Stream進行匯聚的操作;

大家可以把Stream當成一個裝飾后的Iterator,原始版本的Iterator,用戶只能逐個遍歷元素并對其執行某些操作;包裝后的Stream,用戶只要給出需要對其包含的元素執行什么操作,比如“過濾掉長度大于10的字串”、“獲取每個字串的首字母”等,具體這些操作如何應用到每個元素上,就給Stream就好了!原先是人告訴計算機一步一步怎么做,現在是告訴計算機做什么,計算機自己決定怎么做,當然這個“怎么做”還是比較弱的,

例子:

//Lists是Guava中的一個工具類
List nums = Lists.newArrayList(1,null,3,4,null,6);
nums.stream().filter(num -> num != null).count();

上面這段代碼是獲取一個List中,元素不為null的個數,這段代碼雖然很簡短,但是卻是一個很好的入門級別的例子來體現如何使用Stream,正所謂“麻雀雖小五臟俱全”,我們現在開始深入解刨這個例子,完成以后你可能可以基本掌握Stream的用法!

?

圖片就是對于Stream例子的一個決議,可以很清楚的看見:原本一條陳述句被三種顏色的框分割成了三個部分,紅色框中的陳述句是一個Stream的生命開始的地方,負責創建一個Stream實體;綠色框中的陳述句是賦予Stream靈魂的地方,把一個Stream轉換成另外一個Stream,紅框的陳述句生成的是一個包含所有nums變數的Stream,進過綠框的filter方法以后,重新生成了一個過濾掉原nums串列所有null以后的Stream;藍色框中的陳述句是豐收的地方,把Stream的里面包含的內容按照某種演算法來匯聚成一個值,例子中是獲取Stream中包含的元素個數,如果這樣決議以后,還不理解,那就只能動用“核武器”–圖形化,一圖抵千言!

?

使用Stream的基本步驟:

1.創建Stream;
2.轉換Stream,每次轉換原有Stream物件不改變,回傳一個新的Stream物件(可以有多次轉換);
3.對Stream進行聚合(Reduce)操作,獲取想要的結果;

3.1怎么得到Stream

最常用的創建Stream有兩種途徑:

1.通過Stream介面的靜態工廠方法(注意:Java8里介面可以帶靜態方法);
2.通過Collection介面的默認方法(默認方法:Default method,也是Java8中的一個新特性,就是介面中的一個帶有實作的方法)–stream(),把一個Collection物件轉換成Stream

3.1.1 使用Stream靜態方法來創建Stream

1. of方法:有兩個overload方法,一個接受變長引數,一個介面單一值

Stream integerStream = Stream.of(1, 2, 3, 5);
Stream stringStream = Stream.of("taobao");

2. generator方法:生成一個無限長度的Stream,其元素的生成是通過給定的Supplier(這個介面可以看成一個物件的工廠,每次呼叫回傳一個給定型別的物件)

Stream.generate(new Supplier() {
@Override
public Double get() {
return Math.random();
}
});

Stream.generate(() -> Math.random());
Stream.generate(Math::random);
三條陳述句的作用都是一樣的,只是使用了lambda運算式和方法參考的語法來簡化代碼,每條陳述句其實都是生成一個無限長度的Stream,其中值是隨機的,這個無限長度Stream是懶加載,一般這種無限長度的Stream都會配合Stream的limit()方法來用,

3. iterate方法:也是生成無限長度的Stream,和generator不同的是,其元素的生成是重復對給定的種子值(seed)呼叫用戶指定函式來生成的,其中包含的元素可以認為是:seed,f(seed),f(f(seed))無限回圈

Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println);
這段代碼就是先獲取一個無限長度的正整數集合的Stream,然后取出前10個列印,千萬記住使用limit方法,不然會無限列印下去,

3.1.2通過Collection子類獲取Stream

Collection介面有一個stream方法,所以其所有子類都都可以獲取對應的Stream物件,

public interface Collection extends Iterable {
//其他方法省略
default Stream stream() {
return StreamSupport.stream(spliterator(), false);
}
}

3.2轉換Stream

轉換Stream其實就是把一個Stream通過某些行為轉換成一個新的Stream,Stream介面中定義了幾個常用的轉換方法,下面我們挑選幾個常用的轉換方法來解釋,
1. distinct: 對于Stream中包含的元素進行去重操作(去重邏輯依賴元素的equals方法),新生成的Stream中沒有重復的元素;

?

2. filter: 對于Stream中包含的元素使用給定的過濾函式進行過濾操作,新生成的Stream只包含符合條件的元素;

?

3. map: 對于Stream中包含的元素使用給定的轉換函式進行轉換操作,新生成的Stream只包含轉換生成的元素,這個方法有三個對于原始型別的變種方法,分別是:mapToInt,mapToLong和mapToDouble,這三個方法也比較好理解,比如mapToInt就是把原始Stream轉換成一個新的Stream,這個新生成的Stream中的元素都是int型別,之所以會有這樣三個變種方法,可以免除自動裝箱/拆箱的額外消耗;

?

4. flatMap:和map類似,不同的是其每個元素轉換得到的是Stream物件,會把子Stream中的元素壓縮到父集合中;

?

flatMap給一段代碼理解:

Stream> inputStream = Stream.of(
 Arrays.asList(1),
 Arrays.asList(2, 3),
 Arrays.asList(4, 5, 6)
 );
Stream outputStream = inputStream.
flatMap((childList) -> childList.stream());

flatMap 把 input Stream 中的層級結構扁平化,就是將最底層元素抽出來放到一起,最終 output 的新 Stream 里面已經沒有 List 了,都是直接的數字,

5. peek: 生成一個包含原Stream的所有元素的新Stream,同時會提供一個消費函式(Consumer實體),新Stream每個元素被消費的時候都會執行給定的消費函式;

?

6. limit: 對一個Stream進行截斷操作,獲取其前N個元素,如果原Stream中包含的元素個數小于N,那就獲取其所有的元素;

?

7. skip: 回傳一個丟棄原Stream的前N個元素后剩下元素組成的新Stream,如果原Stream中包含的元素個數小于N,那么回傳空Stream;

?

整體呼叫例子:

List nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
System.out.println(“sum is:”+nums.stream().filter(num -> num != null).distinct().mapToInt(num -> num * 2).peek(System.out::println).skip(2).limit(4).sum());

這段代碼演示了上面介紹的所有轉換方法(除了flatMap),簡單解釋一下這段代碼的含義:給定一個Integer型別的List,獲取其對應的Stream物件,然后進行過濾掉null,再去重,再每個元素乘以2,再每個元素被消費的時候列印自身,在跳過前兩個元素,最后去前四個元素進行加和運算(解釋一大堆,很像廢話,因為基本看了方法名就知道要做什么了,這個就是宣告式編程的一大好處!),大家可以參考上面對于每個方法的解釋,看看最終的輸出是什么,

可能會有這樣的疑問:在對于一個Stream進行多次轉換操作,每次都對Stream的每個元素進行轉換,而且是執行多次,這樣時間復雜度就是一個for回圈里把所有操作都做掉的N(轉換的次數)倍啊,其實不是這樣的,轉換操作都是lazy的,多個轉換操作只會在匯聚操作(見下節)的時候融合起來,一次回圈完成,我們可以這樣簡單的理解,Stream里有個操作函式的集合,每次轉換操作就是把轉換函式放入這個集合中,在匯聚操作的時候回圈Stream對應的集合,然后對每個元素執行所有的函式,

3.3匯聚(Reduce)Stream

匯聚操作(也稱為折疊)接受一個元素序列為輸入,反復使用某個合并操作,把序列中的元素合并成一個匯總的結果,比如查找一個數字串列的總和或者最大值,或者把這些數字累積成一個List物件,Stream介面有一些通用的匯聚操作,比如reduce()和collect();也有一些特定用途的匯聚操作,比如sum(),max()和count(),注意:sum方法不是所有的Stream物件都有的,只有IntStream、LongStream和DoubleStream是實體才有,

下面會分兩部分來介紹匯聚操作:

可變匯聚:把輸入的元素們累積到一個可變的容器中,比如Collection或者StringBuilder;
其他匯聚:除去可變匯聚剩下的,一般都不是通過反復修改某個可變物件,而是通過把前一次的匯聚結果當成下一次的入參,反復如此,比如reduce,count,allMatch;

3.3.1可變匯聚

可變匯聚對應的只有一個方法:collect,正如其名字顯示的,它可以把Stream中的要有元素收集到一個結果容器中(比如Collection),先看一下最通用的collect方法的定義(還有其他override方法):

R collect(Supplier supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
先來看看這三個引數的含義:Supplier supplier是一個工廠函式,用來生成一個新的容器;BiConsumer accumulator也是一個函式,用來把Stream中的元素添加到結果容器中;BiConsumer combiner還是一個函式,用來把中間狀態的多個結果容器合并成為一個(并發的時候會用到),看暈了?來段代碼!

List nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
List numsWithoutNull = nums.stream().filter(num -> num != null).
collect(() -> new ArrayList(),
(list, item) -> list.add(item),
(list1, list2) -> list1.addAll(list2));
上面這段代碼就是對一個元素是Integer型別的List,先過濾掉全部的null,然后把剩下的元素收集到一個新的List中,進一步看一下collect方法的三個引數,都是lambda形式的函式,

第一個函式生成一個新的ArrayList實體;
第二個函式接受兩個引數,第一個是前面生成的ArrayList物件,二個是stream中包含的元素,函式體就是把stream中的元素加入ArrayList物件中,第二個函式被反復呼叫直到原stream的元素被消費完畢;
第三個函式也是接受兩個引數,這兩個都是ArrayList型別的,函式體就是把第二個ArrayList全部加入到第一個中;
但是上面的collect方法呼叫也有點太復雜了,沒關系!我們來看一下collect方法另外一個override的版本,其依賴[Collector](Collector (Java Platform SE 8 )),

<R, A> R collect(Collector<? super T, A, R> collector);
這樣清爽多了!Java8還給我們提供了Collector的工具類–[Collectors](Collectors (Java Platform SE 8 )),其中已經定義了一些靜態工廠方法,比如:Collectors.toCollection()收集到Collection中, Collectors.toList()收集到List中和Collectors.toSet()收集到Set中,這樣的靜態方法還有很多,這里就不一一介紹了,大家可以直接去看JavaDoc,下面看看使用Collectors對于代碼的簡化:

List numsWithoutNull = nums.stream().filter(num -> num != null).
collect(Collectors.toList());

3.3.2其他匯聚

– reduce方法:reduce方法非常的通用,后面介紹的count,sum等都可以使用其實作,reduce方法有三個override的方法,本文介紹兩個最常用的,先來看reduce方法的第一種形式,其方法定義如下:

Optional reduce(BinaryOperator accumulator);
接受一個BinaryOperator型別的引數,在使用的時候我們可以用lambda運算式來,

List ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream().reduce((sum, item) -> sum + item).get());
可以看到reduce方法接受一個函式,這個函式有兩個引數,第一個引數是上次函式執行的回傳值(也稱為中間結果),第二個引數是stream中的元素,這個函式把這兩個值相加,得到的和會被賦值給下次執行這個函式的第一個引數,要注意的是:第一次執行的時候第一個引數的值是Stream的第一個元素,第二個引數是Stream的第二個元素,這個方法回傳值型別是Optional,這是Java8防止出現NPE的一種可行方法,后面的文章會詳細介紹,這里就簡單的認為是一個容器,其中可能會包含0個或者1個物件,
這個程序可視化的結果如圖:

?

reduce方法還有一個很常用的變種:

T reduce(T identity, BinaryOperator accumulator);
這個定義上上面已經介紹過的基本一致,不同的是:它允許用戶提供一個回圈計算的初始值,如果Stream為空,就直接回傳該值,而且這個方法不會回傳Optional,因為其不會出現null值,下面直接給出例子,就不再做說明了,

List ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream().reduce(0, (sum, item) -> sum + item));
– count方法:獲取Stream中元素的個數,比較簡單,這里就直接給出例子,不做解釋了,

List ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream().count());

– 搜索相關
– allMatch:是不是Stream中的所有元素都滿足給定的匹配條件
– anyMatch:Stream中是否存在任何一個元素滿足匹配條件
– findFirst: 回傳Stream中的第一個元素,如果Stream為空,回傳空Optional
– noneMatch:是不是Stream中的所有元素都不滿足給定的匹配條件
– max和min:使用給定的比較器(Operator),回傳Stream中的最大|最小值
下面給出allMatch和max的例子,剩下的方法讀者當成練習,

查看源代碼列印幫助
List<Integer> ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println(ints.stream().allMatch(item -> item < 100));
ints.stream().max((o1, o2) -> o1.compareTo(o2)).ifPresent(System.out::println);

參考文章

Java 中的 Lambda 運算式 - 掘金

Java8特性詳解 lambda運算式 Stream - aoeiuv - 博客園

?
微信公眾號【程式員黃小斜】作者是前螞蟻金服Java工程師,專注分享Java技術干貨和求職成長心得,不限于BAT面試,演算法、計算機基礎、資料庫、分布式、spring全家桶、微服務、高并發、JVM、Docker容器,ELK、大資料等,關注后回復【book】領取精選20本Java面試必備精品電子書,

?

微信公眾號【程式員黃小斜】作者是前螞蟻金服Java工程師,專注分享Java技術干貨和求職成長心得,不限于BAT面試,演算法、計算機基礎、資料庫、分布式、spring全家桶、微服務、高并發、JVM、Docker容器,ELK、大資料等,關注后回復【book】領取精選20本Java面試必備精品電子書,

?

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/380173.html

標籤:Java

上一篇:專案開發中,真的有必要定義VO,BO,PO,DO,DTO這些嗎?

下一篇:最新介面api插件 Swagger3 更新配置詳解

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more