欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關于使用Lambda表達式簡化Comparator的使用問題

 更新時間:2023年04月06日 09:16:01   作者:CrazyDragon_King  
這篇文章主要介紹了關于使用Lambda表達式簡化Comparator的使用問題,文中圖文講解了Comparator對象的方法,需要的朋友可以參考下

Comparator 接口

使用集合時,如果需要實現(xiàn)集合元素排序的話,通常有兩種選擇,元素本身實現(xiàn) Comparable 接口或者集合使用 Comparator 對象實現(xiàn)排序。這里來介紹一個 Comparator 這個類。

接口簡介

Comparator 是一個函數(shù)式接口,這個可以從它的定義上看出來。它具有這個注解:@FunctionalInterface。

這個注解標注此接口屬于函數(shù)式接口,意味著只能有一個抽象方法,但是帶你進去看,你會發(fā)現(xiàn)兩個抽象方法!

int compare(T o1, T o2);
boolean equals(Object obj);

這并不是定義錯誤,而是上面那個注解(@FunctionalInterface)的文檔里有說明:如果接口聲明了一個覆蓋了 java.lang.Object 的全局方法之一的抽象方法,那么它不會計入接口的抽象方法數(shù)量中,因為接口的任何實現(xiàn)都將具有 java.lang.Object 或者其它地方的實現(xiàn)。 因此,它確實是只有一個抽象方法:

int compare(T o1, T o2);

定義一個示例類用來演示:Dog類

package com.dragon;

public class Dog {
	private String name;
	private int age;
	private double weight;
	
	public Dog(String name, int age, double weight) {
		super();
		this.name = name;
		this.age = age;
		this.weight = weight;
	}
	//省略 getter 和 setter 方法,使用 IDE 自動生成比較方便。
	//下面兩個方法,也都可以自動生成。

	@Override
	public String toString() {
		return "Dog [name=" + name + ", age=" + age + ", weight=" + weight + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		long temp;
		temp = Double.doubleToLongBits(weight);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Dog other = (Dog) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (Double.doubleToLongBits(weight) != Double.doubleToLongBits(other.weight))
			return false;
		return true;
	}
}

接口方法介紹

這個接口雖然是一個函數(shù)式接口,但是它的方法可不少!所以,它可以實現(xiàn)非常豐富的排序功能!

直接使用接口的抽象方法創(chuàng)建 Comparator 對象

**排序規(guī)則是按照年齡升序。我這里使用的表達式為:

o1.getAge()-o2.getAge();

如果想要實現(xiàn)反序,調(diào)換 o1和o2的位置即可,但是我們不使用這種方式。下面會使用更加方便的方式。
**

1.使用原始的匿名內(nèi)部類方式,實現(xiàn) Comparator 對象。

package com.dragon;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class ComparatorTest {
	public static void main(String[] args) {
		//測試使用的集合,下面不再提供,只提供方法的實現(xiàn)。
		List<Dog> dogList = new ArrayList<>();
		dogList.add(new Dog("小黑", 3, 37.0));
		dogList.add(new Dog("二哈", 2, 40.0));
		dogList.add(new Dog("泰迪", 1, 8.0));
		dogList.add(new Dog("大黃", 4, 55.0));
		rawComparator(dogList);
	}
	
	/**
	 * 原始的實現(xiàn)比較器的方法,使用匿名類
	 * */
	static void rawComparator(List<? extends Dog> dogList) {
		dogList.sort(new Comparator<Dog>() {
			@Override
			public int compare(Dog o1, Dog o2) {
				return o1.getAge()-o2.getAge();
			}
		});
		dogList.forEach(System.out::println);
	}
}

說明:這樣顯得較為繁瑣,不夠體現(xiàn)代碼的簡介,下面使用Java8的 lambda 表達式來改寫。

運行結果:

在這里插入圖片描述

2.使用Java8 的lambda 表達式來簡化代碼

/**
* 使用lambda的寫法
* */
static void lambda(List<? extends Dog> dogList) {
	Comparator<Dog> c = (dog1, dog2)->dog1.getAge() - dog2.getAge();
	dogList.sort(c);
	dogList.forEach(System.out::println);
}

3.舍去中間變量 c,進一步簡化代碼

/**
 * 舍去中間變量 c 的寫法
 * */
static void lambda2(List<? extends Dog> dogList) {
	dogList.sort((dog1, dog2)->dog1.getAge() - dog2.getAge());
	dogList.forEach(System.out::println);
}

總結:基本上,我們第一次接觸 lambda 的話,都會去學習寫這個表達式,感覺使用起來特別的方便,達到了簡化代碼的目的。

接口中的靜態(tài)方法和默認創(chuàng)建 Comparator 對象

comparing 方法(靜態(tài))

接口中有一個靜態(tài)方法 comparing,使用起來也特別的方便,基本上可以代替上面的那種方式了,它的參數(shù)為:Function<? super T, ? extends U> keyExtractor,這需要傳入一個 lambda 表達式。雖然這些方法的定義很復雜,但是使用起來卻感覺很簡單,復雜的事情都被別人做了。

comparing 方法源碼

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
{
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}

1.使用 comparing方法創(chuàng)建 Comparator 對象

/**
 * 使用 Java 8 提供的靜態(tài)方法 comparing 方法,
 * 再配合方法引用,寫法更加簡潔了。但是看這個
 * 方法,我們可能會疑問排序順序到底是正序還是逆序呢?
 * */
static void lambda3(List<? extends Dog> dogList) {
	dogList.sort(Comparator.comparing(Dog::getAge));
	dogList.forEach(System.out::println);
}

說明: 通過上面的源碼可以看到,c1 和 c2 的位置是固定的(排序是固定的升序方式),它是通過 Function 接口,調(diào)用apply方法,生成一個對象,然后調(diào)用 compareTo 方法進行比較的。(例如,我們傳進去的是age,類型為int,但是通過apply會返回 Integer類型。因為包裝類型和 String 類都實現(xiàn)了 Comparable 接口。)

注意: 如果不使用方法引用的話,那么 Dog::getAge 應該被替換為:

(dog1, dog2)->dog1.getAge() - dog2.getAge()

不過,這樣做顯然就是去了簡介性。

2.使用重載的 comparing 方法創(chuàng)建 Comparator 對象

comparing 方法源碼

 public static <T, U> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
 {
     Objects.requireNonNull(keyExtractor);
     Objects.requireNonNull(keyComparator);
     return (Comparator<T> & Serializable)
         (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                           keyExtractor.apply(c2));
 }

說明: 它比上面的 comparing 方法多了一個參數(shù),意味著它可以實現(xiàn)更豐富的比較操作。而且,這個參數(shù)也是一個 Comparator 對象。

好了,下面使用這個方法,來實現(xiàn)按照年齡逆序排序。

/**
 * 解決 lambda3 中的疑問,關于排序順序的問題。
 * 上面那個方法是一個簡便方法,它的排序是默認的正序,
 * 而我們有時會希望逆序排序。所以我們需要使用它的一個重載方法了。
 * */
static void lambda4(List<? extends Dog> dogList) {
	//它的第二個參數(shù),可能會引起困惑,第二個參數(shù)的類型就是第一個參數(shù)指定的類型(如果是基本類型,則為對應的包裝類)
	dogList.sort(Comparator.comparing(Dog::getAge, (age1, age2)->age2-age1));
	dogList.forEach(System.out::println);
}

注意: 這里的第二個參數(shù)中的 age1 和 age2 的實際類型為 Integer而不是 int,可以直接相減的原因是因為自動拆箱機制,所以這里推薦更換為:

說明: 這樣看起來,似乎不夠簡潔,下面將使用更加簡潔的方式來實現(xiàn)逆序排序。

(age1, age2)->age2.compareTo(age1)

運行結果:

在這里插入圖片描述

3.使用comparing方法的更加簡潔形式
Comparator 具有一個靜態(tài)的方法,它的功能很簡單就是逆序。

/**
 * 相信看完 lambda4 都會感覺還沒有 lambda2 的方式簡潔呢,
 * 但是因為正序和逆序只是一個變換順序的問題,所以它也提
 * 供了簡潔的實現(xiàn)。當然了,這也與我這里的使用的Dog對象,比較簡單有關,
 * 只看這里的話, 和上面 lambda2 進行比較,優(yōu)勢不太明顯。
 * */
static void lambda5(List<? extends Dog> dogList) {
	dogList.sort(Comparator.comparing(Dog::getAge, Comparator.reverseOrder()));
	dogList.forEach(System.out::println);
}

這樣,代碼就顯得簡潔多了,當然了,還可以使用一個默認方法當?shù)酵瑯拥哪康摹?/p>

/**
 * 這樣也可以
 * */
static void lambda55(List<? extends Dog> dogList) {
	dogList.sort(Comparator.comparing(Dog::getAge).reversed());
	dogList.forEach(System.out::println);
}

thenComparing 方法(默認)

thenComparing 方法源碼:

default <U extends Comparable<? super U>> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor)
{
    return thenComparing(comparing(keyExtractor));
}

有時候,會碰到這樣的需求,需要使用多種排序方法,而不是單純的一種。例如使用:姓名、年齡、體重進行排序。這時就需要使用 thenComparing 方法了。

/**
 * 實現(xiàn)按照多個標準排序:姓名、年齡、體重
 * 全部按照自然排序(升序)的順序
 * */
static void lambda7(List<? extends Dog> dogList) {
	dogList.sort(Comparator
			.comparing(Dog::getName)
			.thenComparing(Dog::getAge)
			.thenComparing(Dog::getWeight));
	dogList.forEach(System.out::println);
}

說明1: 這里按照三個條件排序是指如果姓名相同了,再按照下一個排序,以此類推,所以你可能看不出來差別(這個結果和按照姓名排序一樣,主要是排序的數(shù)據(jù)不太適合,但我不想換了。)。

說明2: 你仍然可以繼續(xù)添加更多的排序規(guī)則,因為 thenComparing 方法也有重載的方法。

運行結果:

在這里插入圖片描述

thenComparing 方法的重載方法源碼:

 default <U> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
 {
     return thenComparing(comparing(keyExtractor, keyComparator));
 }

它的第二個方法,也和上面的 comparing 方法作用相同,是自己實現(xiàn)一個key的比較器,這里就不再說明了。

適用于 Int、long 和 double 類型的 thenComapring 方法

default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
    return thenComparing(comparingInt(keyExtractor));
}
  
default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) {
    return thenComparing(comparingLong(keyExtractor));
}  

default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) {
    return thenComparing(comparingDouble(keyExtractor));
}

說明:這幾個方法和上面的 thenComparing 方法作用基本相同,但是更加適合處理 int、long和double類型。如果需要排序的類型為這幾個,使用這些方法很好,但是我還是喜歡通用的 thenComparing 方法,這里只演示一個 thenComparingDouble 方法:

static void thenComparingDouble() {
	List<Dog> dogList = new ArrayList<>();
	dogList.add(new Dog("小黑", 3, 37.0));
	dogList.add(new Dog("二哈", 2, 55.0));
	dogList.add(new Dog("泰迪", 1, 8.0));
	dogList.add(new Dog("大黃", 2, 40.0));
	
	dogList.sort(Comparator
			.comparing(Dog::getAge)
			.thenComparingDouble(Dog::getWeight));
	dogList.forEach(System.out::println);
}

運行結果:

在這里插入圖片描述

如果去掉 thenComparingDouble 方法,運行結果為:
注意和上面的結果對比。

在這里插入圖片描述

適用于 Int、long 和 double 類型的 comapring 方法

這三個方法,也是專門用于處理 int、long 和double類型的,和使用 comparing方法差不多。

public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}

 public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
     Objects.requireNonNull(keyExtractor);
     return (Comparator<T> & Serializable)
         (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
 }

public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) {
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
}

這里演示 comparingIntcomparingDouble 兩個方法的用法:
我感覺沒什么區(qū)別,可能是我這個測試用例太簡單了吧。

/**
 * comparingToInt
 * */
static void comparingToInt(List<? extends Dog> dogList) {
	dogList.sort(Comparator.comparingInt(Dog::getAge));
	dogList.forEach(System.out::println);
}

/**
 * comparingToDouble
 * */
static void comparingToDouble(List<? extends Dog> dogList) {
	dogList.sort(Comparator.comparingDouble(Dog::getWeight));
	dogList.forEach(System.out::println);
}

對于集合中含有 null 值元素的排序

static void nullSort() {
		List<String> strList = new ArrayList<>();
		strList.add("dog");
		strList.add("cat");
		strList.add(null);
		strList.add("Bird");
		strList.add(null);
		
		strList.sort(Comparator.comparing(String::length));
		strList.forEach(System.out::println);
	}

運行上面的代碼,結果為:

在這里插入圖片描述

說明:null 值是一個很頭疼的問題,所以 Comparator接口也專門提供了處理null值得方法,它們都是對 null 值友好的方法(null-friendly)。

//null 值在前面。
//Returns a null-friendly comparator that considers null to be less than non-null.
 public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
     return new Comparators.NullComparator<>(true, comparator);
 }

//null 值在后面。
//Returns a null-friendly comparator that considers null to be greater than non-null.
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
    return new Comparators.NullComparator<>(false, comparator);
}

因此,對于含有null值的元素進行排序,可以這樣做:

/**
 * 含有 null 值得元素排序
 * */
public static void nullValueSort() {
	List<String> strList = new ArrayList<>();
	strList.add("dog");
	strList.add("cat");
	strList.add(null);
	strList.add("Bird");
	strList.add(null);
	//我一開始以為是一個字符常量呢?但是一想不對勁,原來是一個靜態(tài)常量比較器。
	//這個是 String 類的比較器:CASE_INSENSITIVE_ORDER
	//null 值在前排序
	strList.sort(Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER));
	strList.forEach(System.out::println);
	System.out.println("===================分隔符====================");
	//null 值在后排序
	strList.sort(Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER));
	strList.forEach(System.out::println);
}

運行結果:
注:擺脫了,煩人的NullPointerException,哈哈。

在這里插入圖片描述

接口中其它方法

reversed方法

in other words, it returns a comparator that imposes the reverse of the natural ordering on a collection of objects that implement the Comparable interface。
換言之,它返回一個比較器,該比較器對實現(xiàn)可比較接口的對象集合施加與自然順序相反的順序。

說明: 由于它是默認方法,所以必須由比較器對象本身來調(diào)用,正好可以實現(xiàn)逆序操作??梢栽趧?chuàng)建比較器后繼續(xù)調(diào)用這個方法,就可以實現(xiàn)逆序了。但是要注意它調(diào)用的順序,它和下面這個 reverseOrder 方法還是不一樣的,下面這個方法是靜態(tài)方法,可以通過類直接調(diào)用。注意,用法上的區(qū)別就是了。

 default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
 }

reverseOrder 方法

public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
    return Collections.reverseOrder();
}

如果直接使用這個方法,創(chuàng)建比較器對象的話,那么集合里面的元素必須使用 Comparable 接口。

static void reverseSort() {
	List<String> strList = new ArrayList<>();
	strList.add("dog");
	strList.add("cat");
	strList.add("Bird");
	
	strList.sort(Comparator.reverseOrder());
	strList.forEach(System.out::println);
}

注意:這里有一個很有趣的地方,這個方法Comparator.reverseOrder()無法使用方法引用改寫:Comparator::reverseOrder,具體原因我看了,但是不是太理解,就不說了。

運行結果:

在這里插入圖片描述

補充: 晚上思考了一下,結合別人的答案,這里其實也是不難理解的。自所以不能使用方法引用,是因為它根本就不是 lambda 表達式。Lambda 表達式需要依賴一個函數(shù)式接口,也就是 Comparator 接口。它的作用就是一個簡化,所以它的需要的參數(shù)就是 int compare(T o1, T o2); 的方法中的參數(shù)。

所以,如果這樣寫的話,會報一個錯誤。
The type Comparator does not define reverseOrder(String, String) that is applicable here

strList.sort(Comparator::reverseOrder);

因此,上面這個寫法就是錯誤的了。它并不能使用lambda的形式改寫。

reverseOrder 和 reversed聯(lián)合使用

	static void reverseSort() {
		List<String> strList = new ArrayList<>();
		strList.add("dog");
		strList.add("cat");
		strList.add("Bird");
		
		Comparator c = Comparator.reverseOrder().reversed();
		strList.sort(c);
		strList.forEach(System.out::println);
	}

說明:上面這個例子我不會添加泛型了,我無論怎么添加都是錯誤的,但是如果不添加泛型的話,那么編譯就能通過了。但是這個東西的泛型似乎很奇怪,我也不太明白了,但是這個方法很有趣,反序的反序又是正序了。(這里存粹是娛樂一下,但是好像發(fā)現(xiàn)了好玩的東西。)

運行結果:

在這里插入圖片描述

naturalOrder

定制排序里面居然有一個方法名叫做自然排序,這個方法感覺很有趣。但是使用的話,需要抑制一下 unchecked 警告。

@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
    return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
}

方法注釋里面說明了:

@param  <T> the {@link Comparable} type of element to be compared。

參數(shù)必須是 Comparable類型的,即實現(xiàn) Comparable 接口。

自然排序:

/**
 * Comparator 實現(xiàn)自然排序
 * */
@SuppressWarnings("unchecked")
static void lambda6(List<? extends Dog> dogList) {
	dogList.sort((Comparator<Dog>) Comparator.naturalOrder());
	dogList.forEach(System.out::println);
}

如果直接運行這個方法會產(chǎn)生問題,必須要先實現(xiàn) Comparable接口才行,并重寫 compareTo方法。

@Override
public int compareTo(Dog o) {
	return age-o.age;
}

運行結果:

在這里插入圖片描述

說明:不太明白,安排這個方法的目的何在,感覺很奇怪。

總結

大致介紹了一下 Comparator 接口中的方法,并寫了很多演示方法。寫這篇博客的起因是我用到 Comparator 接口的時候,感覺似乎有很多豐富的方法,似乎怎么寫都行(哈哈),所以干脆一勞永逸,抽時間看一看這個 Comparator 到底怎么寫,發(fā)現(xiàn)確實是很有趣的。

到此這篇關于關于使用Lambda表達式簡化Comparator的使用問題的文章就介紹到這了,更多相關Lambda表達式簡化Comparator內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java中的snowflake算法詳解

    Java中的snowflake算法詳解

    這篇文章主要介紹了Java中的snowflake算法詳解,Snowflake算法產(chǎn)生是為了滿足Twitter每秒上萬條消息的請求,每條消息都必須分配一條唯一的id,這些id還需要一些大致的順序,并且在分布式系統(tǒng)中不同機器產(chǎn)生的id必須不同,需要的朋友可以參考下
    2023-08-08
  • swagger注解@ApiModelProperty失效情況的解決

    swagger注解@ApiModelProperty失效情況的解決

    這篇文章主要介紹了swagger注解@ApiModelProperty失效情況的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Java 十大排序算法之插入排序刨析

    Java 十大排序算法之插入排序刨析

    插入排序(InsertionSort),一般也被稱為直接插入排序。對于少量元素的排序,它是一個有效的算法。插入排序是一種最簡單的排序方法,它的基本思想是將一個記錄插入到已經(jīng)排好序的有序表中,從而一個新的、記錄數(shù)增 1 的有序表
    2021-11-11
  • Java線程安全的計數(shù)器簡單實現(xiàn)代碼示例

    Java線程安全的計數(shù)器簡單實現(xiàn)代碼示例

    這篇文章主要介紹了Java線程安全的計數(shù)器簡單實現(xiàn)代碼示例,具有一定參考價值,需要的朋友可以了解下。
    2017-10-10
  • 關于Java?中?Future?的?get?方法超時問題

    關于Java?中?Future?的?get?方法超時問題

    這篇文章主要介紹了Java?中?Future?的?get?方法超時,最常見的理解就是,“超時以后,當前線程繼續(xù)執(zhí)行,線程池里的對應線程中斷”,真的是這樣嗎?本文給大家詳細介紹,需要的朋友參考下吧
    2022-06-06
  • Java反射機制在Spring IOC中的應用詳解

    Java反射機制在Spring IOC中的應用詳解

    這篇文章主要介紹了Java反射機制在Spring IOC中的應用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • java實現(xiàn)發(fā)送email小案例

    java實現(xiàn)發(fā)送email小案例

    這篇文章主要為大家詳細介紹了java實現(xiàn)發(fā)送email小案例,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • Java多線程——之一創(chuàng)建線程的四種方法

    Java多線程——之一創(chuàng)建線程的四種方法

    這篇文章主要介紹了Java創(chuàng)建線程方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • Spring實戰(zhàn)之ResourceLoader接口資源加載用法示例

    Spring實戰(zhàn)之ResourceLoader接口資源加載用法示例

    這篇文章主要介紹了Spring實戰(zhàn)之ResourceLoader接口資源加載用法,結合實例形式分析了Spring使用ResourceLoader接口加載資源的相關配置與使用技巧,需要的朋友可以參考下
    2020-01-01
  • IDEA 端口占用的解決方法(推薦)

    IDEA 端口占用的解決方法(推薦)

    這篇文章主要介紹了IDEA 端口占用的解決方法,本文通過兩種方法給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10

最新評論