Hibernate一對多、多對一的關係表達

一、關係表達:

1、一對多、多對一表的關係:

學生表:

 

 

  班級表:

 

 

 在學生表中,學生的學號是主鍵。在班級表中,班級號是主鍵,因此,學生表的外鍵是classno。因此,班級對應學生是一對多,學生對應班級是多對一。因為,一個班級可以有多個學生,但是一個學生只能在一個班級。

2、對象的一對多、多對一關係:

(1)在Class類中,定義Set集合,表達一對多的關係:

 

 

 

package pers.zhb.domain;
import java.util.HashSet;
import java.util.Set;
public class Class {
    private String classno;
    private String department;
    private String monitor;
    private String classname;
    private Set<Student> classes=new HashSet<Student>();//使用set集合表達一對多關係
    public Class(){
    }
    public Set<Student> getClasses() {
        return classes;
    }
    public void setClasses(Set<Student> classes) {
        this.classes = classes;
    }
    public String getMonitor() {
        return monitor;
    }

    public void setMonitor(String monitor) {
        this.monitor = monitor;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }



    public String getClassname() {
        return classname;
    }

    public void setClassname(String classname) {
        this.classname = classname;
    }
    public String getClassno() {
        return classno;
    }

    public void setClassno(String classno) {
        this.classno = classno;
    }
    @Override
    public String toString() {
        return "Class{" +
                "classno='" + classno + '\'' +
                ", department='" + department + '\'' +
                ", monitor='" + monitor + '\'' +
                ", classname='" + classname + '\'' +
                ", classes=" + classes +
                '}';
    }
}
package pers.zhb.domain;
public class Student {
    private Integer studentno;
    private String sname;
    private String sex;
    private String birthday;
    private String classno;
    private Float point;
    private String phone;
    private String email;
    private Clas aClas;
    public Student(){//無參的構造方法
    }
    public Clas getaClas() {
        return aClas;
    }

    public void setaClas(Clas aClas) {
        this.aClas = aClas;
    }
    @Override
    public String toString() {
        return "Student{" +
                "studentno='" + studentno + '\'' +
                ", sname='" + sname + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday='" + birthday + '\'' +
                ", classno='" + classno + '\'' +
                ", point=" + point +
                ", phone='" + phone + '\'' +
                ", email='" + email + '\'' +
                '}';
    }

    public int getStudentno() {
        return studentno;
    }

    public void setStudentno(int studentno) {
        this.studentno = studentno;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public String getClassno() {
        return classno;
    }

    public void setClassno(String classno) {
        this.classno = classno;
    }

    public float getPoint() {
        return point;
    }

    public void setPoint(float point) {
        this.point = point;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

  

  

(2)定義學生和班級的關係:

 

 

 

package pers.zhb.domain;
import java.util.HashSet;
import java.util.Set;
public class Clas {
    private String classno;
    private String department;
    private String monitor;
    private String classname;
    private Set<Student> students=new HashSet<Student>();//使用set集合表達一對多關係
    public Clas(){
    }
    public Set<Student> getStudents() {
        return students;
    }
    public void setClasses(Set<Student> students) {
        this.students = students;
    }
    public String getMonitor() {
        return monitor;
    }

    public void setMonitor(String monitor) {
        this.monitor = monitor;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }



    public String getClassname() {
        return classname;
    }

    public void setClassname(String classname) {
        this.classname = classname;
    }
    public String getClassno() {
        return classno;
    }

    public void setClassno(String classno) {
        this.classno = classno;
    }
    @Override
    public String toString() {
        return "Class{" +
                "classno='" + classno + '\'' +
                ", department='" + department + '\'' +
                ", monitor='" + monitor + '\'' +
                ", classname='" + classname + '\'' +
                ",students=" + students +
                '}';
    }
}

  

3、配置映射文件:

  Class.hbm.xml:

(1)實現一對多的關係映射,即:一個班級對應多個學生:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="pers.zhb.domain">
    <class name="Clas" table="class">
        <id name="classno" column="classno">
            <generator class="native"></generator>
        </id><!--主鍵-->
        <property name="department" column="department"></property>
        <property name="monitor" column="monitor"></property>
        <property name="classname" column="classname"></property>
        <set name="students" table="student"><!--一對多關係配置-->
        <key column="classno" update="false"></key><!--指定了集合表的外鍵-->
            <one-to-many class="Student"></one-to-many>
        </set>
    </class>
</hibernate-mapping>

 

<set name="students">

指定映射的存儲學生的集合的名字。

<key column="classesno"></key>

映射的class表的外鍵。

<one-to-many class="Student"></one-to-many>

指定學生的類型。

(2)實現多對一的關係映射,即:多個學生對應一個班級。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="pers.zhb.domain">
    <class name="Student" table="student">
        <id name="studentno" column="studentno" >
            <generator class="native"></generator>
        </id>
        <property name="birthday" column="birthday"></property>
        <property name="classno" column="classno" insert="false" update="false"></property>
        <property name="email" column="email"></property>
        <property name="phone" column="phone"></property>
        <property name="sex" column="sex"></property>
        <property name="sname" column="sname"></property>
        <property name="point" column="point"></property>
        <many-to-one name="aClas" column="classno" class="Clas"></many-to-one>
    </class>
</hibernate-mapping>

 

name屬性:映射的班級。

column屬性:映射的班級對象對應的外鍵。

class屬性:指定班級的類型。

4、主配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <!--配置數據庫信息-必須的-->
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/stu_mangement</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
        <!--配置hibernate信息-可選的-->
        <property name="hibernate.show_sql">true</property><!--輸出底層sql語句-->
        <property name="hibernate.format_sql">true</property><!--格式化輸出sql語句-->
        <property name="hibernate.hbm2ddl.auto">update</property><!--hibernate幫助創建表,如果已經有表更新表,如果沒有則創建新表-->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <property name="hibernate.connection.isolation">4</property>
        <!--指定session與當前線程綁定-->
        <property name="hibernate.current_session_context_class">thread</property>
        <!--配置數據庫的方言,讓hibernate識別框架自己的特有語句-->
        <!--把映射文件放到核心配置文件-->
        <mapping resource="pers/zhb/domain/Student.hbm.xml"/><!--都在src目錄下-->
        <mapping resource="pers/zhb/domain/Class.hbm.xml"/><!--都在src目錄下-->
    </session-factory>
</hibernate-configuration>

 二、具體運用:

1、增加:

(1)創建一個新班級併為新班級添加兩名學生:

public class Test {
    public static void testSel() {
            Session session = HibernateUtils.openSession();//獲得session
            Transaction transaction = session.beginTransaction();//開啟事務
            Clas clas=new Clas();
            clas.setClassname("計科171");
            clas.setClassno(4600);
            clas.setDepartment("一號樓");
            clas.setMonitor("zhai");

            Student student=new Student();
            student.setSname("");
            student.setStudentno(2017151411);
            student.setPoint(123f);
            student.setSex("");
            student.setBirthday("2019-11-11");
            student.setPhone("18739496522");
            student.setClassno("221221");
            student.setEmail("34288334@qq.com");

            Student student1=new Student();
            student1.setSname("翟hb");
            student1.setStudentno(2017151419);
            student1.setPoint(666f);
            student1.setSex("");
            student1.setBirthday("2019-11-11");
            student1.setPhone("18739496522");
            student1.setClassno("221221");
            student1.setEmail("34288334@qq.com");

            clas.getStudents().add(student);//一對多,一個班級下有多個學生
            clas.getStudents().add(student1);//獲取Set集合對象並向其中添加元素

            student.setaClas(clas);//多對一,學生屬於哪一個班級
            student1.setaClas(clas);

            session.save(clas);
            session.save(student);
            session.save(student1);

            transaction.commit();//提交事務
            session.close();//關閉資源
        }

 

 (2)為一個已經存在的班級添加學生:

 public static void testAdd(){
            Session session = HibernateUtils.openSession();//獲得session
            Transaction transaction = session.beginTransaction();//開啟事務
            Clas clas=session.get(Clas.class,80501);//獲得一個已經存在的班級
            Student student=new Student();//創建一個學生對象
            student.setSname("翟zz");
            student.setStudentno(20190000);
            student.setPoint(133f);
            student.setSex("男");
            student.setBirthday("2019-11-16");
            student.setPhone("18739496522");
            student.setEmail("34288334@qq.com");

            Student student1=new Student();//再創建一個學生對象
            student1.setSname("翟zz");
            student1.setStudentno(20190000);
            student1.setPoint(133f);
            student1.setSex("男");
            student1.setBirthday("2019-11-16");
            student1.setPhone("18739496522");
            student1.setEmail("34288334@qq.com");

            clas.getStudents().add(student);//學生添加到班級
            student.setaClas(clas);//班級與學生對應
            clas.getStudents().add(student1);
            student1.setaClas(clas);

            session.save(student);
            session.save(student1);


            transaction.commit();//提交事務
            session.close();//關閉資源

        }

  

 

 2、刪除:

刪除80501班的一名學生信息:

 public static void testDel() {
           Session session = HibernateUtils.openSession();//獲得session
           Transaction transaction = session.beginTransaction();//開啟事務
           Clas clas=session.get(Clas.class,80501);//獲得要刪除的學生屬於那一個班級
           Student student=session.get(Student.class,937221532);//獲得要刪除的學生
           clas.getStudents().remove(student);
           student.setaClas(null);
           transaction.commit();//提交事務
           session.close();//關閉資源
       }

 

 

 

 

 

 

 

 

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

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

python機器學習——隨機梯度下降

上一篇我們實現了使用梯度下降法的自適應線性神經元,這個方法會使用所有的訓練樣本來對權重向量進行更新,也可以稱之為批量梯度下降(batch gradient descent)。假設現在我們數據集中擁有大量的樣本,比如百萬條樣本,那麼如果我們現在使用批量梯度下降來訓練模型,每更新一次權重向量,我們都要使用百萬條樣本,訓練時間很長,效率很低,我們能不能找到一種方法,既能使用梯度下降法,但是又不要每次更新權重都要使用到所有的樣本,於是隨機梯度下降法(stochastic gradient descent)便被提出來了。

隨機梯度下降法可以只用一個訓練樣本來對權重向量進行更新:
\[ \eta(y^i-\phi(z^i))x^i \]
這種方法比批量梯度下降法收斂的更快,因為它可以更加頻繁的更新權重向量,並且使用當個樣本來更新權重,相比於使用全部的樣本來更新更具有隨機性,有助於算法避免陷入到局部最小值,使用這個方法的要注意在選取樣本進行更新時一定要隨機選取,每次迭代前都要打亂所有的樣本順序,保證訓練的隨機性,並且在訓練時的學習率也不是固定不變的,可以隨着迭代次數的增加,學習率逐漸減小,這種方法可以有助於算法收斂。

現在我們有了使用全部樣本的批量梯度下降法,也有了使用單個樣本的隨機梯度下降法,那麼一種折中的方法,稱為最小批學習(mini-batch learning),它每次使用一部分訓練樣本來更新權重向量。

接下來我們實現使用隨機梯度下降法的Adaline

from numpy.random import seed
class AdalineSGD(object):
    """ADAptive LInear NEuron classifier.

    Parameters
    ----------
    eta:float
        Learning rate(between 0.0 and 1.0
    n_iter:int
        Passes over the training dataset.

    Attributes
    ----------
    w_: 1d-array
        weights after fitting.
    errors_: list
        Number of miscalssifications in every epoch.
    shuffle:bool(default: True)
        Shuffle training data every epoch
        if True to prevent cycles.
    random_state: int(default: None)
        Set random state for shuffling
        and initalizing the weights.

    """

    def __init__(self, eta=0.01, n_iter=10, shuffle=True, random_state=None):
        self.eta = eta
        self.n_iter = n_iter
        self.w_initialized = False
        self.shuffle = shuffle
        if random_state:
            seed(random_state)

    def fit(self, X, y):
        """Fit training data.

        :param X:{array-like}, shape=[n_samples, n_features]
        :param y: array-like, shape=[n_samples]
        :return:
        self:object

        """

        self._initialize_weights(X.shape[1])
        self.cost_ = []

        for i in range(self.n_iter):
            if self.shuffle:
                X, y = self._shuffle(X, y)
            cost = []
            for xi, target in zip(X, y):
                cost.append(self._update_weights(xi, target))
            avg_cost = sum(cost)/len(y)
            self.cost_.append(avg_cost)
        return self
    
    def partial_fit(self, X, y):
        """Fit training data without reinitializing the weights."""
        if not self.w_initialized:
            self._initialize_weights(X.shape[1])
        if y.ravel().shape[0] > 1:
            for xi, target in zip(X, y):
                self._update_weights(xi, target)
        else:
            self._update_weights(X, y)
        return self
    
    def _shuffle(self, X, y):
        """Shuffle training data"""
        r = np.random.permutation(len(y))
        return X[r], y[r]
    
    def _initialize_weights(self, m):
        """Initialize weights to zeros"""
        self.w_ = np.zeros(1 + m)
        self.w_initialized = True
    
    def _update_weights(self, xi, target):
        """Apply Adaline learning rule to update the weights"""
        output = self.net_input(xi)
        error = (target - output)
        self.w_[1:] += self.eta * xi.dot(error)
        self.w_[0] += self.eta * error
        cost = 0.5 * error ** 2
        return cost
    
    def net_input(self, X):
        """Calculate net input"""
        return np.dot(X, self.w_[1:]) + self.w_[0]
    
    def activation(self, X):
        """Computer linear activation"""
        return self.net_input(X)
    
    def predict(self, X):
        """Return class label after unit step"""
        return np.where(self.activation(X) >= 0.0, 1, -1)

其中_shuffle方法中,調用numpy.random中的permutation函數得到0-100的一個隨機序列,然後這個序列作為特徵矩陣和類別向量的下標,就可以起到打亂樣本順序的功能。

現在開始訓練

ada = AdalineSGD(n_iter=15, eta=0.01, random_state=1)
ada.fit(X_std, y)

畫出分界圖和訓練曲線圖

plot_decision_region(X_std, y, classifier=ada)
plt.title('Adaline - Stochastic Gradient Desent')
plt.xlabel('sepal length [standardized]')
plt.ylabel('petal length [standardized]')
plt.legend(loc = 'upper left')
plt.show()
plt.plot(range(1, len(ada.cost_) + 1), ada.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Average Cost')
plt.show()

從上圖可以看出,平均損失下降很快,在大概第15次迭代后,分界線和使用批量梯度下降的Adaline分界線很類似。

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

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

※幫你省時又省力,新北清潔一流服務好口碑

解鎖新夥伴關係 政府、企業與社會的氣候合作

文:李翊僑(荷蘭馬斯垂克大學永續科學、政策與社會碩士生)

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

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

※幫你省時又省力,新北清潔一流服務好口碑

聯合國氣候會議 環保署長與各國官員互動多

摘錄自2019年12月13日中央社報導

台灣不是巴黎氣候協定的締約國,仍積極參加聯合國氣候會議。代表台灣參與的環保署長張子敬表示,今年與各國官員的互動比往年多,不過,台灣減碳作為仍有不足的地方。

聯合國氣候變化綱要公約(UNFCCC)第25次締約方會議(COP 25)2日至13日在西班牙首都馬德里舉行,外交部旗下的國際合作發展基金會、交通部中央氣象局、經濟部工業局、工業技術研究院、行政院農業委員會、媽媽監督核電廠聯盟等機關和團體利用會議期間在周邊會議發聲。

張子敬在馬德里接受中央社記者訪問時表示,雖然無法進入大會會場,不過在場外與各國官員的互動比往年都多。除了邦交國,他也與歐盟、英國、瑞典、德國等國代表會晤,各國對台灣對抗氣候變遷的作法相當重視,台灣也樂於幫助需要幫助的國家。

此外,張子敬還接受在地的「ABC日報」等媒體專訪,說明台灣的能源和減碳政策,以及為何因為中國的壓力,無法參與這次的氣候會議。

張子敬表示,在減碳的作為上,台灣仍有許多不足之處。首先,台灣十分依賴進口化石能源,每度電的碳排量過高,住商、農業和交通部門的減碳都有待加強。

其次,除了建立碳交易制度,落實總量管制,張子敬也支持徵收碳稅,「使用者付費本來就應該」。他說,環保署在水污染防治有許多經驗,未來碳稅的收入將專款專用,用來減少碳排。

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

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

電動車商機蓬勃,兩岸投資兩樣情

全球電動車市場持續成長,直接刺激鋰電池與電動馬達等相關零組件的需求量。台灣、中國兩岸廠商積極針對電動車進行投資布局,但局勢因策略與市場的不同而有所差異。

中國投資額高,但潛藏不穩定性

中國是目前全球電動車市場成長速度最快的國家之一,但今年首季因補貼政策所造成的不確定性,使電動車銷量比去年同期暴跌九成。

不少中國地方政府將電動車與新能源車列為新興產業投資鼓勵對象,也吸引了大筆投資。據統計,2015年至今,中國大陸各地規劃或已展開建設的新能源汽車建設專案超過30個,總投資額超過人民幣1,000億元,呈現遍地開花的局勢。

但《經濟日報》引述中國媒體指出,上述專案多以「新能源汽車產業圈」的形式存在,看似投資新能源車,實際上是為了恢復原有的汽車產能。部分電動車生產企業甚至已進入停產狀態,未來若無法改善,可能會被強制退市。

雖然中國電動車相關投資積極,但卻有泡沫化的隱憂。

台電池廠投入國際電動車市場

相較之下,台灣長泓能源科技、宏境科技對電動車產業的佈局較為穩健,且也更為注重其他海外市場。

長泓能源科技與台灣車廠、客運業者合作,推動電動大巴專案,目標搶下台灣政府10年內投入1萬輛電動大巴的政策。同時,長泓已與特斯拉的移動式20kWh儲能系統、台塑汽車啟動電池等展開測試合作,積極步入電池組裝領域。

長泓能源科技董事長陳明德表示,公司強化氧化鋰鐵電池的產品性能與應用整合,安全性高,且有機會搶攻中國大陸電動車市場。

另一方面,宏境科技已取得美國電動沙灘巡邏車電池組的1,000輛訂單,同時與某汽車通路商合作,打入中國大陸NEXT EV油電混和車馬達控制器供應鏈,數量可望超過一萬輛。宏境科技也已取得中瑞電動車每年550輛的動力鋰電池組訂單,每年可貢獻約1億元新台幣營收。

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

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

※幫你省時又省力,新北清潔一流服務好口碑

F-立凱、五龍動力聯手搶攻中國電動車市場

F-立凱公告將與五龍動力、香港上市公司五龍電動車集團簽訂三方交易契約,共同深化電動車產業的上、下游布局,著眼爭取中國乃至於全球的電動車市場。

根據協議,三方將以策略聯盟、資本合作的方式取得股權轉換方案。契約載明,五龍動力將以每股新台幣35元的價格取得立凱電新發行的普通股,佔增資後股本21.8%;思慕完成後,五龍電動車集團在另外以1億港幣(約新台幣4.2億)現金取得立凱電旗下車電事業部門──立凱綠能(蓋曼)股權,以及台灣立凱綠能的部分資產。交易完成後,五龍動力與五龍電動車集團預計將投資凱電超過新台幣20億元。

此外,立凱電同時將以每股0.5元港幣的價格認購五龍電動車集團新發行之430萬普通股與2.75億元港幣的無擔保可轉換公司債,總投資額預計將達港幣4.9億元,並成為五龍電動車集團股東。此舉將幫助立凱電正式邁入中國電動車市場。

F-立凱由尹衍樑投資,為電動巴士系統與磷酸鐵鋰電池正極材料廠商。與五龍動力、五龍電動車集團的交易完成後,將在兩岸三地與國際市場進行明確產業分工,由立凱電繼續投入正極材料研發、製造與銷售,同時為五龍動力提供LFP-NCO奈米金屬氧化物共晶體化磷酸鋰鐵電池正極材料M系列產品的生產與技術顧問服務。三方也將合作於中國建立材料廠,以因應未來中國電動車龐大的材料需求。

此外,台灣立凱綠能也將與五龍電動車在電池芯、電池模組以及電動車技術等領域合作,同時不忘繼續拓展台灣電動巴士業務。F-立凱表示,本次策略聯盟,將可幫助F-立凱、五龍動力、五龍電動車集團、立凱電整合技術、製造、市場、供應鏈與資金,全面進軍中國與全球的儲能市場和電動車市場。

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

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

※幫你省時又省力,新北清潔一流服務好口碑

特斯拉第 2 季淨損擴大 二度下修銷售預測

美國電動車大廠特斯拉(Tesla)公佈第 2 季財報,凈虧損為 1.843 億美元,而因車輛銷售持續攀高,營收成長 24%,達到 9.55 億美元,但與去年同期的凈虧損 1.542 億美元相比有所擴大,且一年內第二度下修銷售預測。   特斯拉表示,今年交車目標為 5 萬至 5.5 萬輛之間。去年執行長穆斯克發下豪語,說今年的銷售量將達 6 萬輛,今年稍早已調降到 5.5 萬輛,如今再度下修。不過,特斯拉仍預期今年 9 月底這款車將開始「少量交車」,但進度就算只延後一周,整體產量就會減少約 800 輛。     特斯拉上季資本支出總計 4.052 億美元,主要是持續用在 Model X 新車,以及預定 2016 年開幕的內華達州電池新廠。今年上半年的總支出達 8.312 億美元,預估全年支出約為 15 億美元。經調整後特斯拉上季每股虧損 48 美分,優於分析師預估的每股虧損 60 美分,去年同期則是每股獲利 13 美分。         

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

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

Java 8 Streams API 詳解

流式編程作為Java 8的亮點之一,是繼Java 5之後對集合的再一次升級,可以說Java 8幾大特性中,Streams API 是作為Java 函數式的主角來設計的,誇張的說,有了Streams API之後,萬物皆可一行代碼。

什麼是Stream

Stream被翻譯為流,它的工作過程像將一瓶水導入有很多過濾閥的管道一樣,水每經過一個過濾閥,便被操作一次,比如過濾,轉換等,最後管道的另外一頭有一個容器負責接收剩下的水。

示意圖如下:

首先通過source產生流,然後依次通過一些中間操作,比如過濾,轉換,限制等,最後結束對流的操作。

Stream也可以理解為一個更加高級的迭代器,主要的作用便是遍歷其中每一個元素。

為什麼需要Stream

Stream作為Java 8的一大亮點,它專門針對集合的各種操作提供各種非常便利,簡單,高效的API,Stream API主要是通過Lambda表達式完成,極大的提高了程序的效率和可讀性,同時Stram API中自帶的并行流使得併發處理集合的門檻再次降低,使用Stream API編程無需多寫一行多線程的大門就可以非常方便的寫出高性能的併發程序。使用Stream API能夠使你的代碼更加優雅。

流的另一特點是可無限性,使用Stream,你的數據源可以是無限大的。

在沒有Stream之前,我們想提取出所有年齡大於18的學生,我們需要這樣做:

List<Student> result=new ArrayList<>();
for(Student student:students){
 
    if(student.getAge()>18){
        result.add(student);
    }
}
return result;

使用Stream,我們可以參照上面的流程示意圖來做,首先產生Stream,然後filter過濾,最後歸併到容器中。

轉換為代碼如下:

return students.stream().filter(s->s.getAge()>18).collect(Collectors.toList());
  • 首先stream()獲得流
  • 然後filter(s->s.getAge()>18)過濾
  • 最後collect(Collectors.toList())歸併到容器中

是不是很像在寫sql?

如何使用Stream

我們可以發現,當我們使用一個流的時候,主要包括三個步驟:

  • 獲取流
  • 對流進行操作
  • 結束對流的操作

獲取流

獲取流的方式有多種,對於常見的容器(Collection)可以直接.stream()獲取
例如:

  • Collection.stream()
  • Collection.parallelStream()
  • Arrays.stream(T array) or Stream.of()

對於IO,我們也可以通過lines()方法獲取流:

  • java.nio.file.Files.walk()
  • java.io.BufferedReader.lines()

最後,我們還可以從無限大的數據源中產生流:

  • Random.ints()

值得注意的是,JDK中針對基本數據類型的昂貴的裝箱和拆箱操作,提供了基本數據類型的流:

  • IntStream
  • LongStream
  • DoubleStream

這三種基本數據類型和普通流差不多,不過他們流裏面的數據都是指定的基本數據類型。

Intstream.of(new int[]{1,2,3});
Intstream.rang(1,3);

對流進行操作

這是本章的重點,產生流比較容易,但是不同的業務系統的需求會涉及到很多不同的要求,明白我們能對流做什麼,怎麼做,才能更好的利用Stream API的特點。

流的操作類型分為兩種:

  • Intermediate:中間操作,一個流可以後面跟隨零個或多個intermediate操作。其目的主要是打開流,做出某種程度的數據映射/過濾,然後會返回一個新的流,交給下一個操作使用。這類操作都是惰性化的(lazy),就是說,僅僅調用到這類方法,並沒有真正開始流的遍歷。

    map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

  • Terminal:終結操作,一個流只能有一個terminal操作,當這個操作執行后,流就被使用“光”了,無法再被操作。所以這必定是流的最後一個操作。Terminal操作的執行,才會真正開始流的遍歷,並且會生成一個結果,或者一個 side effect。

    forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

IntermediateTerminal完全可以按照上圖的流程圖理解,Intermediate表示在管道中間的過濾器,水會流入過濾器,然後再流出去,而Terminal操作便是最後一個過濾器,它在管道的最後面,流入Terminal的水,最後便會流出管道。

下面依次詳細的解讀下每一個操作所能產生的效果:

中間操作

對於中間操作,所有的API的返回值基本都是Stream<T>,因此以後看見一個陌生的API也能通過返回值判斷它的所屬類型。

map/flatMap

map顧名思義,就是映射,map操作能夠將流中的每一個元素映射為另外的元素。

 <R> Stream<R> map(Function<? super T, ? extends R> mapper);

可以看到map接受的是一個Function,也就是接收參數,並返回一個值。

比如:

//提取 List<Student>  所有student 的名字 
List<String> studentNames = students.stream().map(Student::getName)
                                             .collect(Collectors.toList());

上面的代碼等同於以前的:

List<String> studentNames=new ArrayList<>();
for(Student student:students){
    studentNames.add(student.getName());
}

再比如:將List中所有字母轉換為大寫:

List<String> words=Arrays.asList("a","b","c");
List<String> upperWords=words.stream().map(String::toUpperCase)
                                      .collect(Collectors.toList());

flatMap顧名思義就是扁平化映射,它具體的操作是將多個stream連接成一個stream,這個操作是針對類似多維數組的,比如容器裡面包含容器等。

List<List<Integer>> ints=new ArrayList<>(Arrays.asList(Arrays.asList(1,2),
                                          Arrays.asList(3,4,5)));
List<Integer> flatInts=ints.stream().flatMap(Collection::stream).
                                       collect(Collectors.toList());

可以看到,相當於降維。

filter

filter顧名思義,就是過濾,通過測試的元素會被留下來並生成一個新的Stream

Stream<T> filter(Predicate<? super T> predicate);

同理,我們可以filter接收的參數是Predicate,也就是推斷型函數式接口,接收參數,並返回boolean值。

比如:

//獲取所有大於18歲的學生
List<Student> studentNames = students.stream().filter(s->s.getAge()>18)
                                              .collect(Collectors.toList());

distinct

distinct是去重操作,它沒有參數

  Stream<T> distinct();

sorted

sorted排序操作,默認是從小到大排列,sorted方法包含一個重載,使用sorted方法,如果沒有傳遞參數,那麼流中的元素就需要實現Comparable<T>方法,也可以在使用sorted方法的時候傳入一個Comparator<T>

Stream<T> sorted(Comparator<? super T> comparator);

Stream<T> sorted();

值得一說的是這個ComparatorJava 8之後被打上了@FunctionalInterface,其他方法都提供了default實現,因此我們可以在sort中使用Lambda表達式

例如:

//以年齡排序
students.stream().sorted((s,o)->Integer.compare(s.getAge(),o.getAge()))
                                  .forEach(System.out::println);;

然而還有更方便的,Comparator默認也提供了實現好的方法引用,使得我們更加方便的使用:

例如上面的代碼可以改成如下:

//以年齡排序 
students.stream().sorted(Comparator.comparingInt(Student::getAge))
                            .forEach(System.out::println);;

或者:

//以姓名排序
students.stream().sorted(Comparator.comparing(Student::getName)).
                          forEach(System.out::println);

是不是更加簡潔。

peek

peek有遍歷的意思,和forEach一樣,但是它是一个中間操作。

peek接受一個消費型的函數式接口。

Stream<T> peek(Consumer<? super T> action);

例如:

//去重以後打印出來,然後再歸併為List
List<Student> sortedStudents= students.stream().distinct().peek(System.out::println).
                                                collect(Collectors.toList());

limit

limit裁剪操作,和String::subString(0,x)有點先溝通,limit接受一個long類型參數,通過limit之後的元素只會剩下min(n,size)個元素,n表示參數,size表示流中元素個數

 Stream<T> limit(long maxSize);

例如:

//只留下前6個元素並打印
students.stream().limit(6).forEach(System.out::println);

skip

skip表示跳過多少個元素,和limit比較像,不過limit是保留前面的元素,skip是保留後面的元素

Stream<T> skip(long n);

例如:

//跳過前3個元素並打印 
students.stream().skip(3).forEach(System.out::println);

終結操作

一個流處理中,有且只能有一個終結操作,通過終結操作之後,流才真正被處理,終結操作一般都返回其他的類型而不再是一個流,一般來說,終結操作都是將其轉換為一個容器。

forEach

forEach是終結操作的遍歷,操作和peek一樣,但是forEach之後就不會再返迴流

 void forEach(Consumer<? super T> action);

例如:

//遍歷打印
students.stream().forEach(System.out::println);

上面的代碼和一下代碼效果相同:

for(Student student:students){
    System.out.println(sudents);
}

toArray

toArrayList##toArray()用法差不多,包含一個重載。

默認的toArray()返回一個Object[]

也可以傳入一個IntFunction<A[]> generator指定數據類型

一般建議第二種方式。

Object[] toArray();

<A> A[] toArray(IntFunction<A[]> generator);

例如:

 Student[] studentArray = students.stream().skip(3).toArray(Student[]::new);

max/min

max/min即使找出最大或者最小的元素。max/min必須傳入一個Comparator

Optional<T> min(Comparator<? super T> comparator);

Optional<T> max(Comparator<? super T> comparator);

count

count返迴流中的元素數量

long count();

例如:

long  count = students.stream().skip(3).count();

reduce

reduce為歸納操作,主要是將流中各個元素結合起來,它需要提供一個起始值,然後按一定規則進行運算,比如相加等,它接收一個二元操作 BinaryOperator函數式接口。從某種意義上來說,sum,min,max,average都是特殊的reduce

reduce包含三個重載:

T reduce(T identity, BinaryOperator<T> accumulator);

Optional<T> reduce(BinaryOperator<T> accumulator);

 <U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

例如:

List<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
        
long count = integers.stream().reduce(0,(x,y)->x+y);

以上代碼等同於:

long count = integers.stream().reduce(Integer::sum).get();

reduce兩個參數和一個參數的區別在於有沒有提供一個起始值,

如果提供了起始值,則可以返回一個確定的值,如果沒有提供起始值,則返回Opeational防止流中沒有足夠的元素。

anyMatch allMatch noneMatch

測試是否有任意元素\所有元素\沒有元素匹配表達式

他們都接收一個推斷類型的函數式接口:Predicate

 boolean anyMatch(Predicate<? super T> predicate);

 boolean allMatch(Predicate<? super T> predicate);

 boolean noneMatch(Predicate<? super T> predicate)

例如:

 boolean test = integers.stream().anyMatch(x->x>3);

findFirst、 findAny

獲取元素,這兩個API都不接受任何參數,findFirt返迴流中第一個元素,findAny返迴流中任意一個元素。

Optional<T> findFirst();

Optional<T> findAny();

也有有人會問findAny()這麼奇怪的操作誰會用?這個API主要是為了在并行條件下想要獲取任意元素,以最大性能獲取任意元素

例如:

int foo = integers.stream().findAny().get();

collect

collect收集操作,這個API放在後面將是因為它太重要了,基本上所有的流操作最後都會使用它。

我們先看collect的定義:

 <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);

<R, A> R collect(Collector<? super T, A, R> collector);

可以看到,collect包含兩個重載:

一個參數和三個參數,

三個參數我們很少使用,因為JDK提供了足夠我們使用的Collector供我們直接使用,我們可以簡單了解下這三個參數什麼意思:

  • Supplier:用於產生最後存放元素的容器的生產者
  • accumulator:將元素添加到容器中的方法
  • combiner:將分段元素全部添加到容器中的方法

前兩個元素我們都很好理解,第三個元素是幹嘛的呢?因為流提供了并行操作,因此有可能一個流被多個線程分別添加,然後再將各個子列表依次添加到最終的容器中。

↓ – – – – – – – – –

↓ — — —

↓ ———

如上圖,分而治之。

例如:

List<String> result = stream.collect(ArrayList::new, List::add, List::addAll);

接下來看只有一個參數的collect

一般來說,只有一個參數的collect,我們都直接傳入Collectors中的方法引用即可:

List<Integer> = integers.stream().collect(Collectors.toList());

Collectors中包含很多常用的轉換器。toList(),toSet()等。

Collectors中還包括一個groupBy(),他和Sql中的groupBy一樣都是分組,返回一個Map

例如:

//按學生年齡分組
Map<Integer,List<Student>> map= students.stream().
                                collect(Collectors.groupingBy(Student::getAge));

groupingBy可以接受3個參數,分別是

  1. 第一個參數:分組按照什麼分類
  2. 第二個參數:分組最後用什麼容器保存返回(當只有兩個參數是,此參數默認為HashMap
  3. 第三個參數:按照第一個參數分類后,對應的分類的結果如何收集

有時候單參數的groupingBy不滿足我們需求的時候,我們可以使用多個參數的groupingBy

例如:

//將學生以年齡分組,每組中只存學生的名字而不是對象
Map<Integer,List<String>> map =  students.stream().
  collect(Collectors.groupingBy(Student::getAge,Collectors.mapping(Student::getName,Collectors.toList())));

toList默認生成的是ArrayList,toSet默認生成的是HashSet,如果想要指定其他容器,可以如下操作:

 students.stream().collect(Collectors.toCollection(TreeSet::new));

Collectors還包含一個toMap,利用這個API我們可以將List轉換為Map

  Map<Integer,Student> map=students.stream().
                           collect(Collectors.toMap(Student::getAge,s->s));

值得注意的一點是,IntStreamLongStream,DoubleStream是沒有collect()方法的,因為對於基本數據類型,要進行裝箱,拆箱操作,SDK並沒有將它放入流中,對於基本數據類型流,我們只能將其toArray()

優雅的使用Stream

了解了Stream API,下面詳細介紹一下如果優雅的使用Steam

  • 了解流的惰性操作

    前面說到,流的中間操作是惰性的,如果一個流操作流程中只有中間操作,沒有終結操作,那麼這個流什麼都不會做,整個流程中會一直等到遇到終結操作操作才會真正的開始執行。

    例如:

    students.stream().peek(System.out::println);

    這樣的流操作只有中間操作,沒有終結操作,那麼不管流裡面包含多少元素,他都不會執行任何操作。

  • 明白流操作的順序的重要性

    Stream API中,還包括一類Short-circuiting,它能夠改變流中元素的數量,一般這類API如果是中間操作,最好寫在靠前位置:

    考慮下面兩行代碼:

    students.stream().sorted(Comparator.comparingInt(Student::getAge)).
                      peek(System.out::println).
                      limit(3).              
                      collect(Collectors.toList());
    students.stream().limit(3).
                      sorted(Comparator.comparingInt(Student::getAge)).
                      peek(System.out::println).
                      collect(Collectors.toList());

    兩段代碼所使用的API都是相同的,但是由於順序不同,帶來的結果都非常不一樣的,

    第一段代碼會先排序所有的元素,再依次打印一遍,最後獲取前三個最小的放入list中,

    第二段代碼會先截取前3個元素,在對這三個元素排序,然後遍歷打印,最後放入list中。

  • 明白Lambda的局限性

    由於Java目前只能Pass-by-value,因此對於Lambda也和有匿名類一樣的final的局限性。

    具體原因可以參考

    因此我們無法再lambda表達式中修改外部元素的值。

    同時,在Stream中,我們無法使用break提前返回。

  • 合理編排Stream的代碼格式

    由於可能在使用流式編程的時候會處理很多的業務邏輯,導致API非常長,此時最後使用換行將各個操作分離開來,使得代碼更加易讀。

    例如:

    students.stream().limit(3).
                      sorted(Comparator.comparingInt(Student::getAge)).
                      peek(System.out::println).
                      collect(Collectors.toList());

    而不是:

    students.stream().limit(3).sorted(Comparator.comparingInt(Student::getAge)).peek(System.out::println).collect(Collectors.toList());

    同時由於Lambda表達式省略了參數類型,因此對於變量,盡量使用完成的名詞,比如student而不是s,增加代碼的可讀性。

    盡量寫出敢在代碼註釋上留下你的名字的代碼!

總結

總之,Stream是Java 8 提供的簡化代碼的神器,合理使用它,能讓你的代碼更加優雅。

尊重勞動成功,轉載註明出處

參考鏈接:

《Effective Java》3th

如果覺得寫得不錯,歡迎關注微信公眾號:逸游Java ,每天不定時發布一些有關Java乾貨的文章,感謝關注

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

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

Android音頻開發(1):基礎知識

Android音頻開發(1):基礎知識

導讀

人的說話頻率基本上為300Hz~3400Hz,但是人耳朵聽覺頻率基本上為20Hz~20000Hz。

對於人類的語音信號而言,實際處理一般經過以下步驟:

人嘴說話——>聲電轉換——>抽樣(模數轉換)——>量化(將数字信號用適當的數值表示)——>編碼(數據壓縮)——>

傳輸(網絡或者其他方式)

——> 解碼(數據還原)——>反抽樣(數模轉換)——>電聲轉換——>人耳聽聲。

  • 抽樣率

實際中,人發出的聲音信號為模擬信號,想要在實際中處理必須為数字信號,即採用抽樣、量化、編碼的處理方案。

處理的第一步為抽樣,即模數轉換。

簡單地說就是通過波形採樣的方法記錄1秒鐘長度的聲音,需要多少個數據。

根據奈魁斯特(NYQUIST)採樣定理,用兩倍於一個正弦波的頻繁率進行採樣就能完全真實地還原該波形。

所以,對於聲音信號而言,要想對離散信號進行還原,必須將抽樣頻率定為40KHz以上。實際中,一般定為44.1KHz。

44.1KHz採樣率的聲音就是要花費44000個數據來描述1秒鐘的聲音波形。

原則上採樣率越高,聲音的質量越好,採樣頻率一般共分為22.05KHz、44.1KHz、48KHz三個等級。

22.05 KHz只能達到FM廣播的聲音品質,44.1KHz則是理論上的CD音質界限,48KHz則已達到DVD音質了。

  • 碼率

對於音頻信號而言,實際上必須進行編碼。在這裏,編碼指信源編碼,即數據壓縮。如果,未經過數據壓縮,直接量化進行傳輸則被稱為PCM(脈衝編碼調製)。
要算一個PCM音頻流的碼率是一件很輕鬆的事情,採樣率值×採樣大小值×聲道數 bps。
一個採樣率為44.1KHz,採樣大小為16bit,雙聲道的PCM編碼的WAV文件,它的數據速率則為 44.1K×16×2 =1411.2 Kbps。
我們常說128K的MP3,對應的WAV的參數,就是這個1411.2 Kbps,這個參數也被稱為數據帶寬,它和ADSL中的帶寬是一個概念。將碼率除以8,就可以得到這個WAV的數據速率,即176.4KB/s。

這表示存儲一秒鐘採樣率為44.1KHz,採樣大小為16bit,雙聲道的PCM編碼的音頻信號,需要176.4KB的空間,1分鐘則約為10.34M,這對大部分用戶是不可接受的,尤其是喜歡在電腦上聽音樂的朋友,要降低磁盤佔用

只有2種方法,降低採樣指標或者壓縮。降低指標是不可取的,因此專家們研發了各種壓縮方案。最原始的有DPCM、ADPCM,其中最出名的為MP3。

所以,採用了數據壓縮以後的碼率遠小於原始碼率。

一、發的主要應用有哪些?

音頻播放器,錄音機,語音電話,音視頻監控應用,音視頻直播應用,音頻編輯/處理軟件,藍牙耳機/音箱,等等。

二、頻開發的具體內容有哪些?

(1)音頻採集/播放

(2)音頻算法處理(去噪、靜音檢測、回聲消除、音效處理、功放/增強、混音/分離,等等)

(3)音頻的編解碼和格式轉換

(4)音頻傳輸協議的開發(SIP,A2DP、AVRCP,等等)

三、 音頻應用的難點在哪?

延時敏感、卡頓敏感、噪聲抑制(Denoise)、回聲消除(AEC)、靜音檢測(VAD)、混音算法,等等。

四、 音頻開發基礎概念有哪些?

在音頻開發中,下面的這幾個概念經常會遇到。

1. 採樣率(samplerate)

採樣就是把模擬信號数字化的過程,不僅僅是音頻需要採樣,所有的模擬信號都需要通過採樣轉換為可以用0101來表示的数字信號,示意圖如下所示:

藍色代表模擬音頻信號,紅色的點代表採樣得到的量化數值。

採樣頻率越高,紅色的間隔就越密集,記錄這一段音頻信號所用的數據量就越大,同時音頻質量也就越高。

根據奈奎斯特理論,採樣頻率只要不低於音頻信號最高頻率的兩倍,就可以無損失地還原原始的聲音。

通常人耳能聽到頻率範圍大約在20Hz~20kHz之間的聲音,為了保證聲音不失真,採樣頻率應在40kHz以上。常用的音頻採樣頻率有:8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz、96kHz、192kHz等。

對採樣率為44.1kHz的AAC音頻進行解碼時,一幀的解碼時間須控制在23.22毫秒內。

通常是按1024個採樣點一幀

分析:

1. AAC

一個AAC原始幀包含某段時間內1024個採樣點相關數據。

用1024主要是因為AAC是用的1024點的mdct。

音頻幀的播放時間=一個AAC幀對應的採樣樣本的個數/採樣頻率(單位為s)

採樣率(samplerate)為 44100Hz,表示每秒 44100個採樣點,

所以,根據公式,

音頻幀的播放時長 = 一個AAC幀對應的採樣點個數 / 採樣頻率

則,當前一幀的播放時間 = 1024 * 1000/44100= 23.22 ms(單位為ms)

48kHz採樣率,

則,當前一幀的播放時間 = 1024 * 1000/48000= 21.333ms(單位為ms)

22.05kHz採樣率,

則,當前一幀的播放時間 = 1024 * 1000/22050= 46.439ms(單位為ms)

2. MP3

mp3 每幀均為1152個字節,

則:

每幀播放時長 = 1152 * 1000 / sample_rate

例如:sample_rate = 44100HZ時,

計算出的時長為26.122ms,

這就是經常聽到的mp3每幀播放時間固定為26ms的由來。

2. 量化精度(位寬)

上圖中,每一個紅色的採樣點,都需要用一個數值來表示大小,這個數值的數據類型大小可以是:4bit、8bit、16bit、32bit等等,位數越多,表示得就越精細,聲音質量自然就越好,當然,數據量也會成倍增大。

常見的位寬是:8bit 或者 16bit

3. 聲道數(channels)

由於音頻的採集和播放是可以疊加的,因此,可以同時從多個音頻源採集聲音,並分別輸出到不同的揚聲器,故聲道數一般表示聲音錄製時的音源數量或回放時相應的揚聲器數量。

單聲道(Mono)和雙聲道(Stereo)比較常見,顧名思義,前者的聲道數為1,後者為2

4. 音頻幀(frame)

是用於測量显示幀數的量度。所謂的測量單位為每秒显示幀數(Frames per Second,簡稱:FPS)或“赫茲”(Hz)。

音頻跟視頻很不一樣,視頻每一幀就是一張圖像,而從上面的正玄波可以看出,音頻數據是流式的,本身沒有明確的一幀幀的概念,在實際的應用中,為了音頻算法處理/傳輸的方便,一般約定俗成取2.5ms~60ms為單位的數據量為一幀音頻。

這個時間被稱之為“採樣時間”,其長度沒有特別的標準,它是根據編×××和具體應用的需求來決定的,我們可以計算一下一幀音頻幀的大小:

假設某通道的音頻信號是採樣率為8kHz,位寬為16bit,20ms一幀,雙通道,則一幀音頻數據的大小為:

int size = 8000 x 16bit x 0.02s x 2 = 5120 bit = 640 byte

五、常見的音頻編碼方式有哪些?

上面提到過,模擬的音頻信號轉換為数字信號需要經過採樣和量化,量化的過程被稱之為編碼,根據不同的量化策略,產生了許多不同的編碼方式,常見的編碼方式有:PCM 和 ADPCM,這些數據代表着無損的原始数字音頻信號,添加一些文件頭信息,就可以存儲為WAV文件了,它是一種由微軟和IBM聯合開發的用於音頻数字存儲的標準,可以很容易地被解析和播放。

我們在音頻開發過程中,會經常涉及到WAV文件的讀寫,以驗證採集、傳輸、接收的音頻數據的正確性。

六、常見的音頻壓縮格式有哪些?

首先簡單介紹一下音頻數據壓縮的最基本的原理:因為有冗餘信息,所以可以壓縮。

(1) 頻譜掩蔽效應: 人耳所能察覺的聲音信號的頻率範圍為20Hz~20KHz,在這個頻率範圍以外的音頻信號屬於冗餘信號。

(2) 時域掩蔽效應: 當強音信號和弱音信號同時出現時,弱信號會聽不到,因此,弱音信號也屬於冗餘信號。

下面簡單列出常見的音頻壓縮格式:

MP3,AAC,OGG,WMA,Opus,FLAC,APE,m4a,AMR,等等

七、Adndroid VoIP相關的開源應用有哪些 ?

imsdroid,sipdroid,csipsimple,linphone,WebRTC 等等

八、音頻算法處理的開源庫有哪些 ?

speex、ffmpeg,webrtc audio module(NS、VAD、AECM、AGC),等等

九、Android提供了哪些音頻開發相關的API?

音頻採集: MediaRecoder,AudioRecord

音頻播放: SoundPool,MediaPlayer,AudioTrack

音頻編解碼: MediaCodec

NDK API: OpenSL ES

十、音頻開發的延時標準是什麼?

ITU-TG.114規定,對於高質量語音可接受的時延是300ms。一般來說,如果時延在300~400ms,通話的交互性比較差,但還可以接受。時延大於400ms時,則交互通信非常困難。

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

不甩歐盟反對 川普批准制裁俄歐天然氣管線

摘錄自2019年12月21日中央社報導

法新社報導,美國總統川普21日批准,可制裁替俄國建設天然氣管線到德國的企業。美國國會擔心,管線將讓俄國獲得影響歐洲盟邦的危險籌碼;但歐盟反對制裁,認為自己有權決定能源政策。

美國的制裁鎖定在波羅的海建造北溪天然氣2號管線(Nord Stream 2)的相關公司;造價近110億美元的這條管線,可讓俄國輸送至歐洲經濟體龍頭德國的天然氣量倍增。美國國會議員警告,管線會助長一個含敵意的俄國政府,並在歐陸緊張升溫之際,大增俄國總統蒲亭(Vladimir Putin)的影響力。

不過華府此舉已激怒莫斯科及歐盟,後者表示他們有權決定自己的能源政策。

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

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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