美軍擬造機動微型核反應爐 解偏遠基地電力需求

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

美國軍方今(9日)表示,正在規畫部署微型核能反應爐,這種移動式發電廠可用貨車載運,且不受時間限制為偏遠地區的基地提供電力。

法新社報導,美國國防部已經為了這種移動式核反應爐跟三間企業簽訂合約,目標是產生1百萬瓦至5百萬瓦(megawatt)的電力。預計在兩年內,其中一間公司將獲選來生產反應爐的原型。國防部聲明表示,這項名為「裴雷」(Pele)的計畫是透過發展安全的、可移動的、先進的微型核子反應爐,以因應國防部多元的任務需求。

然而,美軍規畫部署微型核能發電設備,部分民間核能專家對此抱持懷疑態度。這些專家認為,若遭遇攻擊,反應爐被破壞後恐導致放射性燃料外洩,或是核物料落入敵人手中且被用來製造低階的髒彈(dirty bomb)。

能源議題
國際新聞
美國
核反應爐
移動

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

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

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

※試算大陸海運運費!

百度將在美國測試無人駕駛汽車 計畫2018年前推商用車型

日前,百度首席科學家吳恩達在接受國外媒體採訪時透露,百度將很快在美國測試無人駕駛汽車,並希望在2018年前推出一款可商用的車型。

百度無人駕駛車專案於2013年起步,由百度研究院主導研發,其技術核心是“百度汽車大腦”,包括高精度地圖、定位、感知、智慧決策與控制四大模組。2014年7月,百度首次對外證實啟動“百度無人駕駛汽車”研發計畫。

2015年12月10日,百度宣佈,百度無人駕駛車國內首次實現城市、環路及高速道路混合路況下的全自動駕駛。百度公佈的路測路線顯示,百度無人駕駛車從位於北京中關村軟體園的百度大廈附近出發,駛入G7京新高速公路,經五環路,抵達奧林匹克森林公園,並隨後按原路線返回。百度無人駕駛車往返全程均實現自動駕駛,並實現了多次跟車減速、變道、超車、上下匝道、調頭等複雜駕駛動作,完成了進入高速(匯入車流)到駛出高速(離開車流)的不同道路場景的切換。測試時最高速度達到100公里/小時。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

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

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

※試算大陸海運運費!

斯柯達混動/純電動汽車未來幾年將相繼上市

近日,據海外媒體報導,斯柯達在未來幾年計畫推出一系列新能源車,其中包括或將於2019年上市的插電式混動車型以及最早2020年才能推出的純電動版車型。

斯柯達未來的純電動車型就將基於大眾MEB電動車模組化平臺進行打造,目前在考慮推出晶銳和明銳的純電動版車型,不過即使推出的話,最早也要等到2020年,不知道那時候某些城市購買純電動車搖號的話會不會很費勁了。

在推出純電動車之前,斯柯達計畫先推出插電式混動車型,首先考慮的是旗艦車型速派,最早能在2019年正式推出。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

台中搬家,彰化搬家,南投搬家前需注意的眉眉角角,別等搬了再說!

flink 流式處理中如何集成mybatis框架

flink 中自身雖然實現了大量的connectors,如下圖所示,也實現了jdbc的connector,可以通過jdbc 去操作數據庫,但是flink-jdbc包中對數據庫的操作是以ROW來操作並且對數據庫事務的控制比較死板,有時候操作關係型數據庫我們會非常懷念在java web應用開發中的非常優秀的mybatis框架,那麼其實flink中是可以自己集成mybatis進來的。 我們這裏以flink 1.9版本為例來進行集成。

如下圖為flink內部自帶的flink-jdbc:

 

創建一個flink的流式處理項目,引入flink的maven依賴和mybatis依賴(注意這裏引入的是非spring版本,也就是mybatis的單機版):

<properties>

<flink.version>1.9.0</flink.version>
</properties>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>
<!-- flink java 包 -->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-streaming-java_2.11</artifactId>
    <version>${flink.version}</version>
</dependency>

maven依賴引入以後,那麼需要在resources下面定義mybatis-config.xml 配置:

mybatis-config.xml 需要定義如下配置:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias alias="BankBillPublic" type="xxxx.xx.xx.BankBillPublic" />
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://xx.xx.xx.xx:3306/hue?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&autoReconnect=true" />
                <property name="username" value="xxxx" />
                <property name="password" value="xxxx*123%" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/xxxxxMapper.xml" />
    </mappers>
</configuration>

typeAlias 標籤中為自定義的數據類型,然後在xxxxxMapper.xml 中parameterType或者resultType就可以直接用這種定義的數據類型。

dataSource type=”POOLED” 我們使用的是mybatis中的POOLED 類型,也就是連接池的方式去使用。默認支持如下這三種類型。

 我們也可以使用阿里巴巴開源的druid連接池,那麼就需要引入對應的maven依賴,如下所示:

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.14</version>
        </dependency>  

 然後定義一個對應的druid的DataSource,如下所示:

import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.datasource.DataSourceFactory;
import com.alibaba.druid.pool.DruidDataSource;

public class DruidDataSourceFactory implements DataSourceFactory {
    private Properties props;

    @Override
    public DataSource getDataSource() {
        DruidDataSource dds = new DruidDataSource();
        dds.setDriverClassName(this.props.getProperty("driver"));
        dds.setUrl(this.props.getProperty("url"));
        dds.setUsername(this.props.getProperty("username"));
        dds.setPassword(this.props.getProperty("password"));
        // 其他配置可以根據MyBatis主配置文件進行配置
        try {
            dds.init();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return dds;
    }

    @Override
    public void setProperties(Properties props) {
        this.props = props;
    }
}

之後就可以mybatis的配置中使用了,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias alias="BankBillPublic" type="xxxx.xx.xx.BankBillPublic" />
        <typeAlias alias="DRUID" 
 type="com.xx.mybatis.druid.utils.DruidDataSourceFactory" />
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="DRUID">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://xx.xx.xx.xx:3306/hue?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&autoReconnect=true" />
                <property name="username" value="xxxx" />
                <property name="password" value="xxxx*123%" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/xxxxxMapper.xml" />
    </mappers>
</configuration>

<mappers> 下面為定義的mybatis 的xxxxxMapper文件。裏面放置的都是sql語句。

本文作者張永清,轉載請註明出處:

xxxxxMapper.xml 中的sql示例:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xx.xx.bigdata.flink.xx.xx.mapper.UserRelaInfoMapper">
    <!--查詢關鍵字匹配 -->
    <select id="queryUserRelaInfo" parameterType="String" resultType="UserRelaInfo">
        SELECT id AS id,
        USER_NAME AS userName,
        APPL_IDCARD AS applIdCard,
        PEER_USER AS peerUser,
        RELA_TYPE AS relaType,
        CREATE_USER AS createUser,
        CREATE_TIME AS createTime
        FROM USER_RELA_INFO
        <where>
            <if test="applIdCard != null">
                APPL_IDCARD=#{applIdCard}
            </if>
            <if test="peerUser != null">
            AND PEER_USER=#{peerUser}
            </if>
        </where>
    </select>
</mapper>

 定義Mapper,一般可以定義一個interface ,和xxxxxMapper.xml中的namespace保持一致

注意傳入的參數一般加上@Param 註解,傳入的參數和xxxxxMapper.xml中需要的參數保持一致

public interface UserRelaInfoMapper {
    List<UserRelaInfo> queryUserRelaInfo(@Param("applIdCard")String applIdCard,@Param("peerUser") String peerUser);
}

定義SessionFactory工廠(單例模式):

/**
 *
 *  sqlsession factory 單例  事務設置為手動提交
 */
public class MybatisSessionFactory {
    private static final Logger LOG = LoggerFactory.getLogger(MybatisSessionFactory.class);
    private static SqlSessionFactory sqlSessionFactory;
    private MybatisSessionFactory(){
        super();
    }
    public synchronized static SqlSessionFactory getSqlSessionFactory(){
        if(null==sqlSessionFactory){
            InputStream inputStream=null;
            try{
                inputStream = MybatisSessionFactory.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            }
            catch (Exception e){
                LOG.error("create MybatisSessionFactory read mybatis-config.xml cause Exception",e);
            }
            if(null!=sqlSessionFactory){
                LOG.info("get Mybatis sqlsession sucessed....");
            }
            else {
                LOG.info("get Mybatis sqlsession failed....");
            }
        }
        return sqlSessionFactory;
    }
}

  

使用mybatis 對數據庫進行操作:

        SqlSession sqlSession = MybatisSessionFactory.getSqlSessionFactory().openSession();
        UserRelaInfoMapper  userRelaInfoMapper  = sqlSession.getMapper(UserRelaInfoMapper .class);
		//調用對應的方法
		userRelaInfoMapper.xxxx();
		//提交事務
		sqlSession.commit();
		//回滾事務,一般可以捕獲異常,在發生Exception的時候,事務進行回滾
		sqlSession.rollback();
		
		
		

這裏以mysql為示例,寫一個flink下mysql的sink示例,可以自己來靈活控制事務的提交:

public class MysqlSinkFunction<IN> extends RichSinkFunction {
    private static final Logger LOG = LoggerFactory.getLogger(MysqlSinkFunction.class);
    @Override
    public void invoke(Object value, Context context) throws Exception{
        SqlSession sqlSession = MybatisSessionFactory.getSqlSessionFactory().openSession();
        try{
                            //插入
                            LOG.info("MysqlSinkFunction start to do insert data...");
                            xxx.xxx();
							//更新
                            LOG.info("MysqlSinkFunction start to do update data...");
							xxx.xxx();
                            //刪除
                            LOG.info("MysqlSinkFunction start to do delete data...");
							xxx.xxx();

                    
                
                sqlSession.commit();
                LOG.info("MysqlSinkFunction commit transaction success...");
        }
        catch (Throwable e){
            sqlSession.rollback();
            LOG.error("MysqlSinkFunction cause Exception,sqlSession transaction rollback...",e);
        }
    }
}  
相信您如果以前在spring中用過mybatis的話,對上面的這些操作一定不會陌生。由此你也可以發現,在大數據中可以完美的集成mybatis,這樣可以發揮mybatis框架對數據庫操作的優勢,使用起來也非常簡單方便。
一旦集成了mybaitis后,在flink中就可以方便的對各種各樣的關係型數據庫進行操作了。

本文作者張永清,轉載請註明出處:

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

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

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

※試算大陸海運運費!

花了幾個小時總結了一些容易出錯的 Java 知識點!

本文已經收錄自 (61k+Star!【Java學習+面試指南】 一份涵蓋大部分Java程序員所需要掌握的核心知識。歡迎 Star!歡迎騷擾!)
原文地址:https://javaguide.cn/2019/08/20/java/java%E5%9F%BA%E7%A1%80/Java%E7%96%91%E9%9A%BE%E7%82%B9/

1. 基礎

1.1. 正確使用 equals 方法

Object的equals方法容易拋空指針異常,應使用常量或確定有值的對象來調用 equals。

舉個例子:

// 不能使用一個值為null的引用類型變量來調用非靜態方法,否則會拋出異常
String str = null;
if (str.equals("SnailClimb")) {
  ...
} else {
  ..
}

運行上面的程序會拋出空指針異常,但是我們把第二行的條件判斷語句改為下面這樣的話,就不會拋出空指針異常,else 語句塊得到執行。:

"SnailClimb".equals(str);// false 

不過更推薦使用 java.util.Objects#equals(JDK7 引入的工具類)。

Objects.equals(null,"SnailClimb");// false

我們看一下java.util.Objects#equals的源碼就知道原因了。

public static boolean equals(Object a, Object b) {
        // 可以避免空指針異常。如果a==null的話此時a.equals(b)就不會得到執行,避免出現空指針異常。
        return (a == b) || (a != null && a.equals(b));
    }

注意:

Reference:

  • 每種原始類型都有默認值一樣,如int默認值為 0,boolean 的默認值為 false,null 是任何引用類型的默認值,不嚴格的說是所有 Object 類型的默認值。
  • 可以使用 == 或者 != 操作來比較null值,但是不能使用其他算法或者邏輯操作。在Java中null == null將返回true。
  • 不能使用一個值為null的引用類型變量來調用非靜態方法,否則會拋出異常

1.2. 整型包裝類值的比較

所有整型包裝類對象值的比較必須使用equals方法。

先看下面這個例子:

Integer x = 3;
Integer y = 3;
System.out.println(x == y);// true
Integer a = new Integer(3);
Integer b = new Integer(3);
System.out.println(a == b);//false
System.out.println(a.equals(b));//true

當使用自動裝箱方式創建一個Integer對象時,當數值在-128 ~127時,會將創建的 Integer 對象緩存起來,當下次再出現該數值時,直接從緩存中取出對應的Integer對象。所以上述代碼中,x和y引用的是相同的Integer對象。

注意:如果你的IDE(IDEA/Eclipse)上安裝了阿里巴巴的p3c插件,這個插件如果檢測到你用 ==的話會報錯提示,推薦安裝一個這個插件,很不錯。

1.3. BigDecimal

1.3.1. BigDecimal 的用處

《阿里巴巴Java開發手冊》中提到:浮點數之間的等值判斷,基本數據類型不能用==來比較,包裝數據類型不能用 equals 來判斷。 具體原理和浮點數的編碼方式有關,這裏就不多提了,我們下面直接上實例:

float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
System.out.println(a);// 0.100000024
System.out.println(b);// 0.099999964
System.out.println(a == b);// false

具有基本數學知識的我們很清楚的知道輸出並不是我們想要的結果(精度丟失),我們如何解決這個問題呢?一種很常用的方法是:使用使用 BigDecimal 來定義浮點數的值,再進行浮點數的運算操作。

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);// 0.1
BigDecimal y = b.subtract(c);// 0.1
System.out.println(x.equals(y));// true 

1.3.2. BigDecimal 的大小比較

a.compareTo(b) : 返回 -1 表示小於,0 表示 等於, 1表示 大於。

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
System.out.println(a.compareTo(b));// 1

1.3.3. BigDecimal 保留幾位小數

通過 setScale方法設置保留幾位小數以及保留規則。保留規則有挺多種,不需要記,IDEA會提示。

BigDecimal m = new BigDecimal("1.255433");
BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN);
System.out.println(n);// 1.255

1.3.4. BigDecimal 的使用注意事項

注意:我們在使用BigDecimal時,為了防止精度丟失,推薦使用它的 BigDecimal(String) 構造方法來創建對象。《阿里巴巴Java開發手冊》對這部分內容也有提到如下圖所示。

1.3.5. 總結

BigDecimal 主要用來操作(大)浮點數,BigInteger 主要用來操作大整數(超過 long 類型)。

BigDecimal 的實現利用到了 BigInteger, 所不同的是 BigDecimal 加入了小數位的概念

1.4. 基本數據類型與包裝數據類型的使用標準

Reference:《阿里巴巴Java開發手冊》

  • 【強制】所有的 POJO 類屬性必須使用包裝數據類型。
  • 【強制】RPC 方法的返回值和參數必須使用包裝數據類型。
  • 【推薦】所有的局部變量使用基本數據類型。

比如我們如果自定義了一個Student類,其中有一個屬性是成績score,如果用Integer而不用int定義,一次考試,學生可能沒考,值是null,也可能考了,但考了0分,值是0,這兩個表達的狀態明顯不一樣.

說明 :POJO 類屬性沒有初值是提醒使用者在需要使用時,必須自己顯式地進行賦值,任何 NPE 問題,或者入庫檢查,都由使用者來保證。

正例 : 數據庫的查詢結果可能是 null,因為自動拆箱,用基本數據類型接收有 NPE 風險。

反例 : 比如显示成交總額漲跌情況,即正負 x%,x 為基本數據類型,調用的 RPC 服務,調用不成功時,返回的是默認值,頁面显示為 0%,這是不合理的,應該显示成中劃線。所以包裝數據類型的 null 值,能夠表示額外的信息,如:遠程調用失敗,異常退出。

2. 集合

2.1. Arrays.asList()使用指南

最近使用Arrays.asList()遇到了一些坑,然後在網上看到這篇文章: 感覺挺不錯的,但是還不是特別全面。所以,自己對於這塊小知識點進行了簡單的總結。

2.1.1. 簡介

Arrays.asList()在平時開發中還是比較常見的,我們可以使用它將一個數組轉換為一個List集合。

String[] myArray = { "Apple", "Banana", "Orange" }; 
List<String> myList = Arrays.asList(myArray);
//上面兩個語句等價於下面一條語句
List<String> myList = Arrays.asList("Apple","Banana", "Orange");

JDK 源碼對於這個方法的說明:

/**
 *返回由指定數組支持的固定大小的列表。此方法作為基於數組和基於集合的API之間的橋樑,與           Collection.toArray()結合使用。返回的List是可序列化並實現RandomAccess接口。
 */ 
public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

2.1.2. 《阿里巴巴Java 開發手冊》對其的描述

Arrays.asList()將數組轉換為集合后,底層其實還是數組,《阿里巴巴Java 開發手冊》對於這個方法有如下描述:

2.1.3. 使用時的注意事項總結

傳遞的數組必須是對象數組,而不是基本類型。

Arrays.asList()是泛型方法,傳入的對象必須是對象數組。

int[] myArray = { 1, 2, 3 };
List myList = Arrays.asList(myArray);
System.out.println(myList.size());//1
System.out.println(myList.get(0));//數組地址值
System.out.println(myList.get(1));//報錯:ArrayIndexOutOfBoundsException
int [] array=(int[]) myList.get(0);
System.out.println(array[0]);//1

當傳入一個原生數據類型數組時,Arrays.asList() 的真正得到的參數就不是數組中的元素,而是數組對象本身!此時List 的唯一元素就是這個數組,這也就解釋了上面的代碼。

我們使用包裝類型數組就可以解決這個問題。

Integer[] myArray = { 1, 2, 3 };

使用集合的修改方法:add()remove()clear()會拋出異常。

List myList = Arrays.asList(1, 2, 3);
myList.add(4);//運行時報錯:UnsupportedOperationException
myList.remove(1);//運行時報錯:UnsupportedOperationException
myList.clear();//運行時報錯:UnsupportedOperationException

Arrays.asList() 方法返回的並不是 java.util.ArrayList ,而是 java.util.Arrays 的一個內部類,這個內部類並沒有實現集合的修改方法或者說並沒有重寫這些方法。

List myList = Arrays.asList(1, 2, 3);
System.out.println(myList.getClass());//class java.util.Arrays$ArrayList

下圖是java.util.Arrays$ArrayList的簡易源碼,我們可以看到這個類重寫的方法有哪些。

  private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        ...

        @Override
        public E get(int index) {
          ...
        }

        @Override
        public E set(int index, E element) {
          ...
        }

        @Override
        public int indexOf(Object o) {
          ...
        }

        @Override
        public boolean contains(Object o) {
           ...
        }

        @Override
        public void forEach(Consumer<? super E> action) {
          ...
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) {
          ...
        }

        @Override
        public void sort(Comparator<? super E> c) {
          ...
        }
    }

我們再看一下java.util.AbstractListremove()方法,這樣我們就明白為啥會拋出UnsupportedOperationException

public E remove(int index) {
    throw new UnsupportedOperationException();
}

2.1.4. 如何正確的將數組轉換為ArrayList?

stackoverflow:https://dwz.cn/vcBkTiTW

1. 自己動手實現(教育目的)

//JDK1.5+
static <T> List<T> arrayToList(final T[] array) {
  final List<T> l = new ArrayList<T>(array.length);

  for (final T s : array) {
    l.add(s);
  }
  return (l);
}
Integer [] myArray = { 1, 2, 3 };
System.out.println(arrayToList(myArray).getClass());//class java.util.ArrayList

2. 最簡便的方法(推薦)

List list = new ArrayList<>(Arrays.asList("a", "b", "c"))

3. 使用 Java8 的Stream(推薦)

Integer [] myArray = { 1, 2, 3 };
List myList = Arrays.stream(myArray).collect(Collectors.toList());
//基本類型也可以實現轉換(依賴boxed的裝箱操作)
int [] myArray2 = { 1, 2, 3 };
List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList());

4. 使用 Guava(推薦)

對於不可變集合,你可以使用類及其與工廠方法:(參數不能為空)

List<String> il = ImmutableList.of("string", "elements");  // from varargs
List<String> il = ImmutableList.copyOf(aStringArray);      // from array

對於可變集合,你可以使用類及其工廠方法:

List<String> l1 = Lists.newArrayList(anotherListOrCollection);    // from collection
List<String> l2 = Lists.newArrayList(aStringArray);               // from array
List<String> l3 = Lists.newArrayList("or", "string", "elements"); // from varargs

5. 使用 Apache Commons Collections

List<String> list = new ArrayList<String>();
CollectionUtils.addAll(list, str);

2.2. Collection.toArray()方法使用的坑&如何反轉數組

該方法是一個泛型方法:<T> T[] toArray(T[] a); 如果toArray方法中沒有傳遞任何參數的話返回的是Object類型數組。

String [] s= new String[]{
    "dog", "lazy", "a", "over", "jumps", "fox", "brown", "quick", "A"
};
List<String> list = Arrays.asList(s);
Collections.reverse(list);
s=list.toArray(new String[0]);//沒有指定類型的話會報錯

由於JVM優化,new String[0]作為Collection.toArray()方法的參數現在使用更好,new String[0]就是起一個模板的作用,指定了返回數組的類型,0是為了節省空間,因為它只是為了說明返回的類型。詳見:

2.3. 不要在 foreach 循環里進行元素的 remove/add 操作

如果要進行remove操作,可以調用迭代器的 remove方法而不是集合類的 remove 方法。因為如果列表在任何時間從結構上修改創建迭代器之後,以任何方式除非通過迭代器自身remove/add方法,迭代器都將拋出一個ConcurrentModificationException,這就是單線程狀態下產生的 fail-fast 機制

fail-fast 機制 :多個線程對 fail-fast 集合進行修改的時,可能會拋出ConcurrentModificationException,單線程下也會出現這種情況,上面已經提到過。

java.util包下面的所有的集合類都是fail-fast的,而java.util.concurrent包下面的所有的類都是fail-safe的。

開源項目推薦

作者的其他開源項目推薦:

  1. :【Java學習+面試指南】 一份涵蓋大部分Java程序員所需要掌握的核心知識。
  2. : 適合新手入門以及有經驗的開發人員查閱的 Spring Boot 教程(業餘時間維護中,歡迎一起維護)。
  3. : 我覺得技術人員應該有的一些好習慣!
  4. :從零入門 !Spring Security With JWT(含權限驗證)後端部分代碼。

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

現代轉攻新能源汽車 計畫2020年之前推26款新車

日前,據海外媒體報導,現代汽車集團將在2020年之前推出26款新能源汽車,其中包括旗下現代和起亞兩大品牌。現代汽車集團此舉意在搶佔新能源汽車市場的領先地位,爭取在2020年達到僅次於豐田的行業第二水準。

現代汽車集團計畫推出的26款新車型包括12款混動車型、6款插電式混動車型、2款純電動車型以及2款氫燃料電池車型等。其中,起亞將推出包括Niro混動版、凱尊(參配、圖片、詢價) 混動版、SoulEV在內的11款新車,而現代則會推出插電式混動版、純電動版IONIQ與豐田普銳斯一決高下。據現代汽車集團預估,到2020年現代汽車集團在新能源汽車市場的年銷量有望達到30萬台。

為了節省新能源汽車的開發成本,現代汽車集團相關負責人透露,旗下新能源車型將採用共用零部件策略。雖然每款車的動力系統在動力輸出上可能有所不同,但電動機直徑、核心部件等將會遵循統一化標準。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

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

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

※試算大陸海運運費!

Model 3 預售量逾 27.6 萬輛,分析師預言 Tesla 無法準時交貨

電動車製造商 Tesla 宣布,新款電動車 Model 3 開放預售後,僅 36 小時內 Model 3 的預訂總量就超過了 27.6 萬輛,這是 Tesla 迄今為止推出的最廉價的電動車,Tesla CEO Elon Musk 表示,Model 3 將在 2017 年年底投產,首批新車至少需要等待 18 個月後才能發貨。

2016 年 4 月 1 日 Tesla 發表最新入門級電動車 Model 3,這款電動車起售價為 3.5 萬美元,續航里程大約 350 公里,計劃在 2017 年年底投產,Tesla 首先將交付北美的訂單,其次是歐洲、亞太等地區。   儘管這款新車需要等待非常長的時間,預售還是吸引了大量消費者,Model 3 訂單總量超過了 27.6 萬輛,訂單總額為 106 億美元。Tesla CEO Elon Musk 表示,Model 3 的基礎定價是 3.5 萬美元,加上運輸費和稅費,首批在加州生產的 Model 3 平均售價將達到了 5 萬至 6 萬美元。   Tesla 加州工廠正在為 Model 3 的生產線擴建,目標是在 2020 年將產能提升到每年 50 萬輛,以滿足市場需求,由於產能提升緩慢,Tesla 將需要很長的時間才能交付首批訂單,部分分析師認為 Tesla 無法完成首批訂單的生產,無法向消費者交貨 Model 3 不得不在 2020 年向預訂的消費者退還 1,000 美元的訂金。   分析師預期 2016 年 6 月 Model 3 的訂單將達到 25 萬輛到 30 萬輛,由於該車的預訂量超過預期,將推高 Tesla 的股價,便於該公司增發新股,募集資金投建新的工廠。自 2016 年 2 月 Tesla 股價創下新低後已累計反彈 60%。

(本文授權轉載自《》─〈〉)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

台中搬家,彰化搬家,南投搬家前需注意的眉眉角角,別等搬了再說!

【從今天開始好好學數據結構04】程序員你心中就沒點“樹”嗎?

目錄

前面我們講的都是線性表結構,棧、隊列等等。今天我們講一種非線性表結構,樹。樹這種數據結構比線性表的數據結構要複雜得多,內容也比較多,首先我們先從樹(Tree)開始講起。
@

樹(Tree)

樹型結構是一種非線性結構,它的數據元素之間呈現分支、分層的特點。

1.樹的定義

樹(Tree)是由n(n≥0)個結點構成的有限集合T,當n=0時T稱為空樹;否則,在任一非空樹T中:
(1)有且僅有一個特定的結點,它沒有前驅結點,稱其為根(Root)結點;
(2)剩下的結點可分為m(m≥0)個互不相交的子集T1,T2,…,Tm,其中每個子集本身又是一棵樹,並稱其為根的子樹(Subtree)。

注意:樹的定義具有遞歸性,即“樹中還有樹”。樹的遞歸定義揭示出了樹的固有特性

2.什麼是樹結構

什麼是“樹”?再好的定義,都沒有圖解來的直觀。所以我在圖中畫了幾棵“樹”。你來看看,這些“樹”都有什麼特徵?

你有沒有發現,“樹”這種數據結構真的很像我們現實生活中的“樹”

3.為什麼使用樹結構

在有序數組中,可以快速找到特定的值,但是想在有序數組中插入一個新的數據項,就必須首先找出新數據項插入的位置,然後將比新數據項大的數據項向後移動一位,來給新的數據項騰出空間,刪除同理,這樣移動很費時。顯而易見,如果要做很多的插入和刪除操作和刪除操作,就不該選用有序數組。另一方面,鏈表中可以快速添加和刪除某個數據項,但是在鏈表中查找數據項可不容易,必須從頭開始訪問鏈表的每一個數據項,直到找到該數據項為止,這個過程很慢。 樹這種數據結構,既能像鏈表那樣快速的插入和刪除,又能想有序數組那樣快速查找

4.樹的常用術語

結點——包含一個數據元素和若干指向其子樹的分支
度——結點擁有的子樹個數
樹的度——該樹中結點的最大度數
恭弘=叶 恭弘子——度為零的結點
分支結點(非終端結點)——度不為零的結點
孩子和雙親——結點的子樹的根稱為該結點的孩子,相應地,該結點稱為孩子的雙親
兄弟——同一個雙親的孩子
祖先和子孫——從根到該結點所經分支上的所有結點。相應地,以某一結點為根的子樹中的任一結點稱為該結點的子孫。
結點的層次——結點的層次從根開始定義,根結點的層次為1,其孩子結點的層次為2,……
堂兄弟——雙親在同一層的結點
樹的深度——樹中結點的最大層次
有序樹和無序樹——如果將樹中每個結點的各子樹看成是從左到右有次序的(即位置不能互換),則稱該樹為有序樹;否則稱為無序樹。
森林——m(m≥0)棵互不相交的樹的有限集合

到這裏,樹就講的差不多了,接下來講講二叉樹(Binary Tree)

二叉樹(Binary Tree)

樹結構多種多樣,不過我們最常用還是二叉樹,我們平時最常用的樹就是二叉樹。二叉樹的每個節點最多有兩個子節點,分別是左子節點和右子節點。二叉樹中,有兩種比較特殊的樹,分別是滿二叉樹和完全二叉樹。滿二叉樹又是完全二叉樹的一種特殊情況。

1.二叉樹的定義和特點

二叉樹的定義:
二叉樹(Binary Tree)是n(n≥0)個結點的有限集合BT,它或者是空集,或者由一個根結點和兩棵分別稱為左子樹和右子樹的互不相交的二叉樹組成 。
————————————
二叉樹的特點:
每個結點至多有二棵子樹(即不存在度大於2的結點);二叉樹的子樹有左、右之分,且其次序不能任意顛倒。

2.幾種特殊形式的二叉樹

1、滿二叉樹
定義:深度為k且有2k-1個結點的二叉樹,稱為滿二叉樹。
特點:每一層上的結點數都是最大結點數
2、完全二叉樹
定義:
深度為k,有n個結點的二叉樹當且僅當其每一個結點都與深度為k的滿二叉樹中編號從1至n的結點一一對應時,稱為完全二叉樹
特點:
特點一 : 恭弘=叶 恭弘子結點只可能在層次最大的兩層上出現;
特點二 : 對任一結點,若其右分支下子孫的最大層次為l,則其左分支下子孫的最大層次必為l 或l+1

建議看圖對應文字綜合理解

代碼創建二叉樹

首先,創建一個節點Node類

package demo5;
/*
 * 節(結)點類 
 */
public class Node {
    //節點的權
    int value;
    //左兒子(左節點)
    Node leftNode;
    //右兒子(右節點)
    Node rightNode;
    //構造函數,初始化的時候就給二叉樹賦上權值
    public Node(int value) {
        this.value=value;
    }
    
    //設置左兒子(左節點)
    public void setLeftNode(Node leftNode) {
        this.leftNode = leftNode;
    }
    //設置右兒子(右節點)
    public void setRightNode(Node rightNode) {
        this.rightNode = rightNode;
    }

接着創建一個二叉樹BinaryTree 類

package demo5;
/*
 * 二叉樹Class
 */
public class BinaryTree {
    //根節點root
    Node root;
    
    //設置根節點
    public void setRoot(Node root) {
        this.root = root;
    }
    
    //獲取根節點
    public Node getRoot() {
        return root;
    }
}

最後創建TestBinaryTree 類(該類主要是main方法用來測試)來創建一個二叉樹

package demo5;
public class TestBinaryTree {

    public static void main(String[] args) {
        //創建一顆樹
        BinaryTree binTree = new BinaryTree();
        //創建一個根節點
        Node root = new Node(1);
        //把根節點賦給樹
        binTree.setRoot(root);
        //創建一個左節點
        Node rootL = new Node(2);
        //把新創建的節點設置為根節點的子節點
        root.setLeftNode(rootL);
        //創建一個右節點
        Node rootR = new Node(3);
        //把新創建的節點設置為根節點的子節點
        root.setRightNode(rootR);
        //為第二層的左節點創建兩個子節點
        rootL.setLeftNode(new Node(4));
        rootL.setRightNode(new Node(5));
        //為第二層的右節點創建兩個子節點
        rootR.setLeftNode(new Node(6));
        rootR.setRightNode(new Node(7));
    }

}

下面將會講的遍歷、查找節點、刪除節點都將圍繞這三個類開展

不難看出創建好的二叉樹如下(畫的不好,還望各位見諒):

3.二叉樹的兩種存儲方式

二叉樹既可以用鏈式存儲,也可以用數組順序存儲。數組順序存儲的方式比較適合完全二叉樹,其他類型的二叉樹用數組存儲會比較浪費存儲空間,所以鏈式存儲更合適。

我們先來看比較簡單、直觀的鏈式存儲法

接着是基於數組的順序存儲法(該例子是一棵完全二叉樹)

上面例子是一棵完全二叉樹,所以僅僅“浪費”了一個下標為0的存儲位置。如果是非完全二叉樹,則會浪費比較多的數組存儲空間,如下。

還記得堆和堆排序嗎,堆其實就是一種完全二叉樹,最常用的存儲方式就是數組。

4.二叉樹的遍歷

前面我講了二叉樹的基本定義和存儲方法,現在我們來看二叉樹中非常重要的操作,二叉樹的遍歷。這也是非常常見的面試題。

經典遍歷的方法有三種,前序遍歷中序遍歷後序遍歷

前序遍歷是指,對於樹中的任意節點來說,先打印這個節點,然後再打印它的左子樹,最後打印它的右子樹。

中序遍歷是指,對於樹中的任意節點來說,先打印它的左子樹,然後再打印它本身,最後打印它的右子樹。

後序遍歷是指,對於樹中的任意節點來說,先打印它的左子樹,然後再打印它的右子樹,最後打印這個節點本身。

我想,睿智的你已經想到了二叉樹的前、中、後序遍歷就是一個遞歸的過程。比如,前序遍歷,其實就是先打印根節點,然後再遞歸地打印左子樹,最後遞歸地打印右子樹。

在之前創建好的二叉樹代碼之上,我們來使用這三種方法遍歷一下~

依舊是在Node節點類上添加方法:可以看出遍歷方法都是用的遞歸思想

package demo5;
/*
 * 節(結)點類 
 */
public class Node {
//===================================開始 遍歷========================================
    //前序遍歷
    public void frontShow() {
        //先遍歷當前節點的內容
        System.out.println(value);
        //左節點
        if(leftNode!=null) {
            leftNode.frontShow();
        }
        //右節點
        if(rightNode!=null) {
            rightNode.frontShow();
        }
    }

    //中序遍歷
    public void midShow() {
        //左子節點
        if(leftNode!=null) {
            leftNode.midShow();
        }
        //當前節點
        System.out.println(value);
        //右子節點
        if(rightNode!=null) {
            rightNode.midShow();
        }
    }

    //後序遍歷
    public void afterShow() {
        //左子節點
        if(leftNode!=null) {
            leftNode.afterShow();
        }
        //右子節點
        if(rightNode!=null) {
            rightNode.afterShow();
        }
        //當前節點
        System.out.println(value);
    }

}

然後依舊是在二叉樹BinaryTree 類上添加方法,並且添加的方法調用Node類中的遍歷方法

package demo5;
/*
 * 二叉樹Class
 */
public class BinaryTree {

    public void frontShow() {
        if(root!=null) {
            //調用節點類Node中的前序遍歷frontShow()方法
            root.frontShow();
        }
    }

    public void midShow() {
        if(root!=null) {
            //調用節點類Node中的中序遍歷midShow()方法
            root.midShow();
        }
    }

    public void afterShow() {
        if(root!=null) {
            //調用節點類Node中的後序遍歷afterShow()方法
            root.afterShow();
        }
    }

}

依舊是在TestBinaryTree類中測試

package demo5;

public class TestBinaryTree {

    public static void main(String[] args) {
        //前序遍歷樹
        binTree.frontShow();
        System.out.println("===============");
        //中序遍歷
        binTree.midShow();
        System.out.println("===============");
        //後序遍歷
        binTree.afterShow();
        System.out.println("===============");
        //前序查找
        Node result = binTree.frontSearch(5);
        System.out.println(result);
        
}

如果遞歸理解的不是很透,我可以分享一個學習的小方法:我建議各位可以這樣斷點調試,一步一步調,思維跟上,仔細推敲每一步的運行相信我,你會重新認識到遞歸!(像下面這樣貼個圖再一步一步斷點思維更加清晰)

貼一下我斷點對遞歸的分析,希望對你有一定的幫助~

二叉樹遍歷的遞歸實現思路自然、簡單,易於理解,但執行效率較低。為了提高程序的執行效率,可以顯式的設置棧,寫出相應的非遞歸遍歷算法。非遞歸的遍歷算法可以根據遞歸算法的執行過程寫出。至於代碼可以嘗試去寫一寫,這也是一種提升!具體的非遞歸算法主要流程圖貼在下面了:

二叉樹遍歷算法分析:

二叉樹遍歷算法中的基本操作是訪問根結點,不論按哪種次序遍歷,都要訪問所有的結點,對含n個結點的二叉樹,其時間複雜度均為O(n)。所需輔助空間為遍歷過程中所需的棧空間,最多等於二叉樹的深度k乘以每個結點所需空間數,最壞情況下樹的深度為結點的個數n,因此,其空間複雜度也為O(n)。

5.二叉樹中節點的查找與刪除

剛才講到二叉樹的三種金典遍歷放法,那麼節點的查找同樣是可以效仿的,分別叫做前序查找、中序查找以及後序查找,下面代碼只以前序查找為例,三者查找方法思路類似~

至於刪除節點,有三種情況:

1、如果刪除的是根節點,那麼二叉樹就完全被刪了
2、如果刪除的是雙親節點,那麼該雙親節點以及他下面的所有子節點所構成的子樹將被刪除
3、如果刪除的是恭弘=叶 恭弘子節點,那麼就直接刪除該恭弘=叶 恭弘子節點

那麼,我把完整的三個類給貼出來(包含創建、遍歷、查找、刪除)

依舊是Node節點類

package demo5;
/*
 * 節(結)點類 
 */
public class Node {
    //節點的權
    int value;
    //左兒子
    Node leftNode;
    //右兒子
    Node rightNode;
    //構造函數,初始化的時候就給二叉樹賦上權值
    public Node(int value) {
        this.value=value;
    }
    
    //設置左兒子
    public void setLeftNode(Node leftNode) {
        this.leftNode = leftNode;
    }
    //設置右兒子
    public void setRightNode(Node rightNode) {
        this.rightNode = rightNode;
    }
    
    //前序遍歷
    public void frontShow() {
        //先遍歷當前節點的內容
        System.out.println(value);
        //左節點
        if(leftNode!=null) {
            leftNode.frontShow();
        }
        //右節點
        if(rightNode!=null) {
            rightNode.frontShow();
        }
    }

    //中序遍歷
    public void midShow() {
        //左子節點
        if(leftNode!=null) {
            leftNode.midShow();
        }
        //當前節點
        System.out.println(value);
        //右子節點
        if(rightNode!=null) {
            rightNode.midShow();
        }
    }

    //後序遍歷
    public void afterShow() {
        //左子節點
        if(leftNode!=null) {
            leftNode.afterShow();
        }
        //右子節點
        if(rightNode!=null) {
            rightNode.afterShow();
        }
        //當前節點
        System.out.println(value);
    }

    //前序查找
    public Node frontSearch(int i) {
        Node target=null;
        //對比當前節點的值
        if(this.value==i) {
            return this;
        //當前節點的值不是要查找的節點
        }else {
            //查找左兒子
            if(leftNode!=null) {
                //有可能可以查到,也可以查不到,查不到的話,target還是一個null
                target = leftNode.frontSearch(i);
            }
            //如果不為空,說明在左兒子中已經找到
            if(target!=null) {
                return target;
            }
            //查找右兒子
            if(rightNode!=null) {
                target=rightNode.frontSearch(i);
            }
        }
        return target;
    }
    
    //刪除一個子樹
    public void delete(int i) {
        Node parent = this;
        //判斷左兒子
        if(parent.leftNode!=null&&parent.leftNode.value==i) {
            parent.leftNode=null;
            return;
        }
        //判斷右兒子
        if(parent.rightNode!=null&&parent.rightNode.value==i) {
            parent.rightNode=null;
            return;
        }
        
        //遞歸檢查並刪除左兒子
        parent=leftNode;
        if(parent!=null) {
            parent.delete(i);
        }
        
        //遞歸檢查並刪除右兒子
        parent=rightNode;
        if(parent!=null) {
            parent.delete(i);
        }
    }

}

依舊是BinaryTree 二叉樹類

package demo5;
/*
 * 二叉樹Class
 */
public class BinaryTree {
    //根節點root
    Node root;
    
    //設置根節點
    public void setRoot(Node root) {
        this.root = root;
    }
    
    //獲取根節點
    public Node getRoot() {
        return root;
    }

    public void frontShow() {
        if(root!=null) {
            //調用節點類Node中的前序遍歷frontShow()方法
            root.frontShow();
        }
    }

    public void midShow() {
        if(root!=null) {
            //調用節點類Node中的中序遍歷midShow()方法
            root.midShow();
        }
    }

    public void afterShow() {
        if(root!=null) {
            //調用節點類Node中的後序遍歷afterShow()方法
            root.afterShow();
        }
    }
    //查找節點i
    public Node frontSearch(int i) {
        return root.frontSearch(i);
    }
    //刪除節點i
    public void delete(int i) {
        if(root.value==i) {
            root=null;
        }else {
            root.delete(i);
        }
    }
    
}

依舊是TestBinaryTree測試類

package demo5;

public class TestBinaryTree {

    public static void main(String[] args) {
        //創建一顆樹
        BinaryTree binTree = new BinaryTree();
        //創建一個根節點
        Node root = new Node(1);
        //把根節點賦給樹
        binTree.setRoot(root);
        //創建一個左節點
        Node rootL = new Node(2);
        //把新創建的節點設置為根節點的子節點
        root.setLeftNode(rootL);
        //創建一個右節點
        Node rootR = new Node(3);
        //把新創建的節點設置為根節點的子節點
        root.setRightNode(rootR);
        //為第二層的左節點創建兩個子節點
        rootL.setLeftNode(new Node(4));
        rootL.setRightNode(new Node(5));
        //為第二層的右節點創建兩個子節點
        rootR.setLeftNode(new Node(6));
        rootR.setRightNode(new Node(7));
        //前序遍歷樹
        binTree.frontShow();
        System.out.println("===============");
        //中序遍歷
        binTree.midShow();
        System.out.println("===============");
        //後序遍歷
        binTree.afterShow();
        System.out.println("===============");
        //前序查找
        Node result = binTree.frontSearch(5);
        System.out.println(result);
        
        System.out.println("===============");
        //刪除一個子樹
        binTree.delete(4);
        binTree.frontShow();
        
    }

}

到這裏,總結一下,我們學了一種非線性表數據結構,樹。關於樹,有幾個比較常用的概念你需要掌握,那就是:根節點、恭弘=叶 恭弘子節點、父節點、子節點、兄弟節點,還有節點的高度、深度、層數,以及樹的高度等。我們平時最常用的樹就是二叉樹。二叉樹的每個節點最多有兩個子節點,分別是左子節點和右子節點。二叉樹中,有兩種比較特殊的樹,分別是滿二叉樹和完全二叉樹。滿二叉樹又是完全二叉樹的一種特殊情況。二叉樹既可以用鏈式存儲,也可以用數組順序存儲。數組順序存儲的方式比較適合完全二叉樹,其他類型的二叉樹用數組存儲會比較浪費存儲空間。除此之外,二叉樹里非常重要的操作就是前、中、後序遍歷操作,遍歷的時間複雜度是O(n),你需要理解並能用遞歸代碼來實現。

如果本文章對你有幫助,哪怕是一點點,請點個讚唄,謝謝~

歡迎各位關注我的公眾號,一起探討技術,嚮往技術,追求技術…說好了來了就是盆友喔…

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

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

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

※試算大陸海運運費!

[UWP]通過自定義XamlCompositionBrushBase實現圖片平鋪

1. 什麼是XamlCompositionBrushBase

我早就想試試自定義XamlCompositionBrushBase,但一直沒機會。上一篇文章介紹到,原理很簡單,但每次都要寫這些代碼很繁瑣,正好就用這個作為例子試試XamlCompositionBrushBase。

CompositionBrush靈活多變,它的基本用法如下:

  1. 通過Compositor創建CompositionBrush;
  2. 配置CompositionBrush;
  3. 創建SpriteVisual並將它的Brush設置為CompositionBrush;
  4. 使用 將SpriteVisual設置到某個UIElement的可視化層里。

這些步驟很繁瑣,而且不能用在XAML中。XamlCompositionBrushBase提供了將CompositionBrush用在XAML中一個橋樑,他繼承自Brush類,可以直接像普通的XAML 畫筆(如SolidColorBrush)那樣直接用在XAML中。

如上圖所示,中已經提了很不少XamlCompositionBrushBase的實現,它們的使用方式已經有很多文章介紹,這裏不一一列舉。

2. 自定義XamlCompositionBrushBase

這篇文章將介紹一個自定義的畫筆:TiledImageBrush,它的主要目標是實現ImageBrush沒有的圖片平鋪功能,並且它可以在XAML中使用,使用方式如下:

<Rectangle IsHitTestVisible="False">
    <Rectangle.Fill>
        <controls:TiledImageBrush Source="ms-appx:///Assets/flutter.png"/>
    </Rectangle.Fill>
</Rectangle>

順便複習下普通的ImageBrush的用法:

<Rectangle >
    <Rectangle.Fill>
        <ImageBrush ImageSource="ms-appx:///Assets/flutter.png"/>
    </Rectangle.Fill>
</Rectangle>

看起來TiledImageBrush的用法是不是和ImageBrush很像?接下來講解TiledImageBrush的實現步驟。TiledImageBrush繼承自XamlCompositionBrushBase,而實現XamlCompositionBrushBase的一般步驟如下:

protected override void OnConnected()
{
    // Delay creating composition resources until they're required.
    if (CompositionBrush == null)
    {
         CompositionBrush = CreateCompositionBrush();//Create A CompositionBrush.
    }
}

protected override void OnDisconnected()
{
    // Dispose of composition resources when no longer in use.
    if (CompositionBrush != null)
    {
        CompositionBrush.Dispose();
        CompositionBrush = null;
    }
}

首先重寫,當畫筆在屏幕上首次用於繪製元素時會調用這個函數。在這個函數里創建CompositionBrush並賦值給。

然後重寫,它在畫筆不再用於繪製任何元素時被調用。在這個函數里盡可能地釋放各種資源,例如CompositionBrush。這兩步就是實現XamlCompositionBrushBase的基本步驟。

創建CompositionBrush有很多種玩法,我之前寫過兩篇文章分別介紹 及 。這裏使用這篇文章里介紹到的代碼,首先使用LoadedImageSurface.StartLoadFromUri創建CompositionSurfaceBrush,然後加入到BorderEffect里實現圖片平鋪,然後把產生的CompositionEffectBrush賦值給XamlCompositionBrushBase.CompositionBrush

TiledImageBrush中添加了Source屬性用於設置圖片Uri(實際上是個ImageSource類型),模仿ImageBrush,這裏的Source也是一個ImageSource類型的屬性,雖然實際上使用的是它的UriSource。詳細代碼如下:

public ImageSource Source
{
    get => (ImageSource)GetValue(SourceProperty);
    set => SetValue(SourceProperty, value);
}

private void UpdateSurface()
{
    if (Source != null && _surfaceBrush != null)
    {
        var uri = (Source as BitmapImage)?.UriSource ?? new Uri("ms-appx:///");
        _surface = LoadedImageSurface.StartLoadFromUri(uri);
        _surfaceBrush.Surface = _surface;
    }
}

OnConnected的詳細代碼如下:

protected override void OnConnected()
{
    base.OnConnected();

    if (CompositionBrush == null)
    {
        _surfaceBrush = Compositor.CreateSurfaceBrush();
        _surfaceBrush.Stretch = CompositionStretch.None;

        UpdateSurface();

        _borderEffect = new BorderEffect()
        {
            Source = new CompositionEffectSourceParameter("source"),
            ExtendX = Microsoft.Graphics.Canvas.CanvasEdgeBehavior.Wrap,
            ExtendY = Microsoft.Graphics.Canvas.CanvasEdgeBehavior.Wrap
        };

        _borderEffectFactory = Compositor.CreateEffectFactory(_borderEffect);
        _borderEffectBrush = _borderEffectFactory.CreateBrush();
        _borderEffectBrush.SetSourceParameter("source", _surfaceBrush);
        CompositionBrush = _borderEffectBrush;
    }
}

這樣一個基本的XamlCompositionBrush就完成了,完整的代碼可以在這裏查看:

3. 參考

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

【其他文章推薦】

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

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

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

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

台中搬家,彰化搬家,南投搬家前需注意的眉眉角角,別等搬了再說!

Vue躬行記(9)——Vuex

  Vuex是一個專為Vue.js設計的狀態管理庫,適用於多組件共享狀態的場景。Vuex能集中式的存儲和維護所有組件的狀態,並提供相關規則保證狀態的獨立性、正確性和可預測性,這不僅讓調試變得可追蹤,還讓代碼變得更結構化且易維護。本文所使用的Vuex,其版本是3.1.1。

一、基本用法

  首先需要引入Vue和Vuex兩個庫,如果像下面這樣在Vue之後引入Vuex,那麼Vuex會自動調用Vue.use()方法註冊其自身;但如果以模塊的方式引用,那麼就得顯式地調用Vue.use()。注意,因為Vuex依賴Promise,所以對於那些不支持Promise的瀏覽器,要使用Vuex的話,得引入相關的polyfill庫,例如es6-promise。

<script src="js/vue.js"></script>
<script src="js/vuex.js"></script>

  然後創建Vuex應用的核心:Store(倉庫)。它是一個容器,保存着大量的響應式狀態(State),並且這些狀態不能直接修改,需要顯式地將修改請求提交到Mutation(變更)中才能實現更新,因為這樣便於追蹤每個狀態的變化。在下面的示例中,初始化了一個digit狀態,並在mutations選項中添加了兩個可將其修改的方法。

const store = new Vuex.Store({
  state: {
    digit: 0
  },
  mutations: {
    add: state => state.digit++,
    minus: state => state.digit--
  }
});

  接着創建根實例,並將store實例注入,從而讓整個應用都能讀寫其中的狀態,在組件中可通過$store屬性訪問到它,如下所示,以計算屬性的方式讀取digit狀態,並通過調用commit()方法來修改該狀態。

var vm = new Vue({
  el: "#container",
  store: store,
  computed: {
    digit() {
      return this.$store.state.digit;
    }
  },
  methods: {
    add() {
      this.$store.commit("add");
    },
    minus() {
      this.$store.commit("minus");
    }
  }
});

  最後將根實例中的方法分別註冊到兩個按鈕的點擊事件中,如下所示,每當點擊這兩個按鈕時,狀態就會更新,並在頁面中显示。

<div id="container">
  <p>{{digit}}</p>
  <button @click="add">增加</button>
  <button @click="minus">減少</button>
</div>

二、主要組成

  Vuex的主要組成除了上一節提到的Store、State和Mutation之外,還包括Getter和Action,本節會對其中的四個做重點講解,它們之間的關係如圖2所示。

圖2  四者的關係

1)State

  State是一個可存儲狀態的對象,在應用的任何位置都能被訪問到,並且作為單一數據源(Single Source Of Truth)而存在。

  當組件需要讀取大量狀態時,一個個的聲明成計算屬性會顯得過於繁瑣而冗餘,於是Vuex提供了一個名為mapState()的輔助函數,用來將狀態自動映射成計算屬性,它的參數既可以是數組,也可以是對象。

  當計算屬性的名稱與狀態名稱相同,並且不需要做額外處理時,可將名稱組成一個字符串數組傳遞給mapState()函數,在組件中可按原名調用,如下所示。

var vm = new Vue({
  computed: Vuex.mapState([ "digit" ])
});

  當計算屬性的名稱與狀態名稱不同,或者計算屬性讀取的是需要處理的狀態時,可將一個對象傳遞給mapState()函數,其鍵就是計算屬性的名稱,而其值既可以是函數,也可以是字符串,如下代碼所示。如果是函數,那麼它的第一個參數是state,即狀態對象;如果是字符串,那麼就是從state中指定一個狀態作為計算屬性。

var vm = new Vue({
  computed: Vuex.mapState({
    digit: state => state.digit,
    alias: "digit"        //相當於state => state.digit
  })
});

  因為mapState()函數返回的是一個對象,所以當組件內已經包含計算屬性時,可以對其應用擴展運算符(…)來進行合併,如下所示,這是一種極為簡潔的寫法。

var vm = new Vue({
  computed: {
    name() {},
    ...Vuex.mapState([ "digit" ])
  }
});

2)Getter

  Getter是從State中派生出的狀態,當多個組件要對同一個狀態進行相同的處理時,就需要將狀態轉移到Getter中,以免產生重複的冗餘代碼。

  Getter相當於Store的計算屬性,它能接收兩個參數,第一個是state對象,第二個是可選的getters對象,該參數能讓不同的Getter之間相互訪問。Getter的返回值會被緩存,並且只有當依賴值發生變化時才會被重新計算。不過當返回值是函數時,其結果就不會被緩存,如下所示,其中caculate返回的是個数字,而sum返回的是個函數。

const store = new Vuex.Store({
  state: {
    digit: 0
  },
  getters: {
    caculate: state => {
      return state.digit + 2;
    },
    sum: state => right => {
      return state.digit + right;
    }
  }
});

  在組件內可通過this.$store.getters訪問到Getter中的數據,如下所示,讀取了上一個示例中的兩個Getter。

var vm = new Vue({
  methods: {
    add() {
      this.$store.getters.caculate;
      this.$store.getters.sum(1);
    }
  }
});

  Getter也有一個輔助函數,用來將Getter自動映射為組件的計算屬性,名字叫mapGetters(),其參數也是數組或對象。但與之前的mapState()不同,當參數是對象時,其值不能是函數,只能是字符串,如下所示,為了對比兩種寫法,聲明了兩個computed選項。

var vm = new Vue({
  computed: Vuex.mapGetters([ "caculate" ]),
  computed: Vuex.mapGetters({
    alias: "caculate"
  })
});

3)Mutation

  更改狀態的唯一途徑是提交Mutation,Vuex中的Mutation類似於事件,也包含一個類型和回調函數,在函數體中可進行狀態更改的邏輯,並且它能接收兩個參數,第一個是state對象,第二個是可選的附加數據,叫載荷(Payload)。下面這個Mutation的類型是“interval”,接收了兩個參數。

const store = new Vuex.Store({
  state: {
    digit: 0
  },
  mutations: {
    interval: (state, payload) => state.digit += payload.number
  }
});

  在組件中不能直接調用Mutation的回調函數,得通過this.$store.commit()方法觸發更新,如下所示,採用了兩種提交方式,第一種是傳遞type參數,第二種是傳遞包含type屬性的對象。

var vm = new Vue({
  methods: {
    interval() {
      this.$store.commit("interval", { number: 2 });             //第一種
      this.$store.commit({ type: "interval", number: 2 });       //第二種
    }
  }
});

  當多人協作時,Mutation的類型適合寫成常量,這樣更容易維護,也能減少衝突。

const INTERVAL = "interval";

  Mutation有一個名為mapMutations()的輔助函數,其寫法和mapState()相同,它能將Mutation自動映射為組件的方法,如下所示。

var vm = new Vue({
  methods: Vuex.mapMutations(["interval"])
  //相當於
  methods: {
    interval(payload) {
      this.$store.commit(INTERVAL, payload);
    }
  }
});

  注意,為了能追蹤狀態的變更,Mutation只支持同步的更新,如果要異步,那麼得使用Action。

4)Action

  Action類似於Mutation,但不同的是它可以包含異步操作,並且只能用來通知Mutation,不會直接更新狀態。Action的回調函數能接收兩個參數,第一個是與Store實例具有相同屬性和方法的context對象(注意,不是Store實例本身),第二個是可選的附加數據,如下所示,調用commit()方法提交了一個Mutation。

const store = new Vuex.Store({
  actions: {
    interval(context, payload) {
      context.commit("interval", payload);
    }
  }
});

  在組件中能通過this.$store.dispatch()方法分發Action,如下所示,與commit()方法一樣,它也有兩種提交方式。

var vm = new Vue({
  methods: {
    interval() {
      this.$store.dispatch("interval", { number: 2 });            //第一種
      this.$store.dispatch({type: "interval", number: 2});        //第二種
    }
  }
});

  注意,由於dispatch()方法返回的是一個Promise對象,因此它能以一種更優雅的方式來處理異步操作,如下所示。

var vm = new Vue({
  methods: {
    interval() {
      this.$store.dispatch("interval", { number: 2 }).then(() => {
        console.log("success");
      });
    }
  }
});

  Action有一個名為mapActions()的輔助函數,其寫法和mapState()相同,它能將Action自動映射為組件的方法,如下所示。

var vm = new Vue({
  methods: Vuex.mapActions([ "interval" ])
});

三、模塊

  當應用越來越大時,為了避免Store變得過於臃腫,有必要將其拆分到一個個的模塊(Module)中。每個模塊就是一個對象,包含屬於自己的State、Getter、Mutation和Action,甚至還能嵌套其它模塊。

1)局部狀態

  對於模塊內部的Getter和Mutation,它們接收的第一個參數是模塊的局部狀態,而Getter的第三個參數rootState和Action的context.rootState屬性可訪問根節點狀態(即全局狀態),如下所示。

const moduleA = {
  state: { digit: 0 },
  mutations: {
    add: state => state.digit++
  },
  getters: {
    caculate: (state, getter, rootState) => {
      return state.digit + 2;
    }
  },
  actions: {
    interval(context, payload) {
      context.commit("add", payload);
    }
  }
};

2)命名空間

  默認情況下,只有在訪問State時需要帶命名空間,而Getter、Mutation和Action的調用方式不變。將之前的moduleA模塊註冊到Store實例中,如下所示,modules選項的值是一個子模塊對象,其鍵是模塊名稱。

const store = new Vuex.Store({
  modules: {
    a: moduleA
  }
});

  如果要訪問模塊中的digit狀態,那麼可以像下面這樣寫。

store.state.a.digit;

  當模塊的namespaced屬性為true時,它的Getter、Mutation和Action也會帶命名空間,在使用時,需要添加命名空間前綴,如下代碼所示,此舉大大提升了模塊的封裝性和復用性。

const moduleA = {
  namespaced: true
};
var vm = new Vue({
  el: "#container",
  store: store,
  methods: {
    add() {
      this.$store.commit("a/add");
    },
    caculate() {
      this.$store.getters["a/caculate"];
    }
  }
});

  如果要在帶命名空間的模塊中提交全局的Mutation或分發全局的Action,那麼只要將{root: true}作為第三個參數傳給commit()或dispatch()就可實現,如下所示。

var vm = new Vue({
  methods: {
    add() {
      this.$store.dispatch("add", null, { root: true });
      this.$store.commit("add", null, { root: true });
    }
  }
});

  如果要在帶命名空間的模塊中註冊全局的Action,那麼需要將其修改成對象的形式,然後添加root屬性並設為true,再將Action原先的定義轉移到handler()函數中,如下所示。

const moduleA = {
  actions: {
    interval: {
      root: true,
      handler(context, payload) {}
    }
  }
};

3)輔助函數

  當使用mapState()、mapGetters()、mapMutations()和mapActions()四個輔助函數對帶命名空間的模塊做映射時,需要顯式的包含命名空間,如下所示。

var vm = new Vue({
  computed: Vuex.mapState({
    digit: state => state.a.digit
  }),
  methods: Vuex.mapMutations({
    add: "a/add"
  })
});

  這四個輔助函數的第一個參數都是可選的,用於綁定命名空間,可簡化映射過程,如下所示。

var vm = new Vue({
  computed: Vuex.mapState("a", {
    digit: state => state.digit
  }),
  methods: Vuex.mapMutations("a", {
    add: "add"
  })
});

  Vuex還提供了另一個輔助函數createNamespacedHelpers(),可創建綁定命名空間的輔助函數,如下所示。

const { mapState, mapMutations } = Vuex.createNamespacedHelpers("a");

四、動態註冊

  在創建Store實例后,可通過registerModule()方法動態註冊模塊,如下代碼所示,調用了兩次registerModule()方法,第一次註冊了模塊“a”,第二次註冊了嵌套模塊“a/b”。

const store = new Vuex.Store();
store.registerModule("a", moduleA);    
store.registerModule(["a", "b"], moduleAB);

  通過store.state.a和store.state.a.b可訪問模塊的局部狀態。如果要卸載動態註冊的模塊,那麼可以通過unregisterModule()方法實現。

  registerModule()方法的第三個參數是可選的配置對象,當preserveState屬性的值為true時(如下所示),在註冊模塊時會忽略模塊中的狀態,即無法在store中讀取模塊中的狀態。

store.registerModule("a", moduleA, { preserveState: true });

五、表單處理

  表單默認能直接修改組件的狀態,但是在Vuex中,狀態只能由Mutation觸發更新。為了能更好的追蹤狀態的變化,也為了能更符合Vuex的思維,需要讓表單控件與狀態綁定在一起,並通過input或change事件監聽狀態更新的行為,如下所示。

<div id="container">
  <input :value="digit" @input="add" />
</div>

  然後在Store實例中初始化digit狀態,並添加更新狀態的Mutation,如下所示。

const store = new Vuex.Store({
  state: {
    digit: 0
  },
  mutations: {
    add: (state, value) => {
      state.digit = value;
    }
  }
});

  最後在創建根實例時,將digit狀態映射成它的計算屬性,在事件處理程序add()中調用commit()方法,並將控件的值作為第二個參數傳入,如下所示。

var vm = new Vue({
  el: "#container",
  store: store,
  computed: Vuex.mapState(["digit"]),
  methods: {
    add(e) {
      this.$store.commit("add", e.target.value);
    }
  }
});

  還有一個方法也能實現相同的功能,那就是在控件上使用v-model指令,但需要與帶setter的計算屬性配合,如下所示(只給出了關鍵部分的代碼)。

<div id="container">
  <input v-model="digit" />
</div>
<script>
  var vm = new Vue({
    computed: {
      digit: {
        get() {
          return this.$store.state.digit;
        },
        set(value) {
          this.$store.commit("add", value);
        }
      }
    }
  });
</script>

 

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

【其他文章推薦】

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

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

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

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

台中搬家,彰化搬家,南投搬家前需注意的眉眉角角,別等搬了再說!