1. 程式人生 > 實用技巧 >ROS入門筆記(十一):編寫與測試簡單的Service和Client (Python)

ROS入門筆記(十一):編寫與測試簡單的Service和Client (Python)

先看再點贊,給自己一點思考的時間,思考過後請毫不猶豫微信搜尋【沉默王二】,關注這個長髮飄飄卻靠才華苟且的程式設計師。
本文 GitHub github.com/itwanger 已收錄,裡面還有技術大佬整理的面試題,以及二哥的系列文章。

關於 Java 基礎、Java 面向物件程式設計、Java 字串、Java 陣列等方面的知識點已經可以告一段落了,小夥伴們可以在「沉默王二」公眾號後臺回覆「小白」獲取第二版手冊。覺得不錯的話,請隨手轉發給身邊的小夥伴,贈人玫瑰,手有餘香哈。

那麼接下來,我開始肝 Java 集合方面的文章了,小夥伴們請默默為我鼓個掌,我能聽得到,真的,別吝嗇你的掌聲,響起來。第一篇,必須得從 ArrayList 開始,畢竟 ArrayList 可以稱得上是集合方面最常用的類了,估計沒有之一。

ArrayList 實現了 List 介面,是基於陣列實現的。小夥伴們都知道,陣列的大小是固定的,建立的時候指定了大小,就不能再調整了,如果陣列滿了,就不能再新增任何元素了。ArrayList 是陣列很好的替代方案,它提供了比陣列更豐富的預定義方法(增刪改查),並且大小是可以根據元素的多少進行自動調整的,非常靈活。

準備在 ArrayList 的第四個位置(下標為 3)上新增一個元素 55。

此時 ArrayList 中第五個位置以後的元素將會向後移動。

準備把 23 從 ArrayList 中移除。

此時下標為 7、8、9 的元素往前挪。

01、如何建立一個 ArrayList

ArrayList<String>alist=new
ArrayList<String>();

可以通過上面的語句來建立一個字串型別的 ArrayList(通過尖括號來限定 ArrayList 中元素的型別,如果嘗試新增其他型別的元素,將會產生編譯錯誤),更簡化的寫法如下:

List<String>alist=newArrayList<>();

由於 ArrayList 實現了 List 介面,所以 alist 變數的型別可以是 List 型別;new 關鍵字聲明後的尖括號中可以不再指定元素的型別,因為編譯器可以通過前面尖括號中的型別進行智慧推斷。

如果非常確定 ArrayList 中元素的個數,在建立的時候還可以指定初始大小。

List<String>alist=newArrayList<>(20);

這樣做的好處是,可以有效地避免在新增新的元素時進行不必要的擴容。但通常情況下,我們很難確定 ArrayList 中元素的個數,因此一般不指定初始大小。

02、向 ArrayList 中新增一個元素

可以通過 add() 方法向 ArrayList 中新增一個元素,如果不指定下標的話,就預設新增在末尾。

alist.add("沉默王二");

感興趣的小夥伴可以研究一下 add() 方法的原始碼,它在新增元素的時候會執行 grow() 方法進行擴容,這個是面試官特別喜歡考察的一個重點。

下面是 add(E e) 方法的原始碼:

publicbooleanadd(Ee){
modCount++;
add(e,elementData,size);
returntrue;
}

呼叫了私有的 add(E e, Object[] elementData, int s) 方法:

privatevoidadd(Ee,Object[]elementData,ints){
if(s==elementData.length)
elementData=grow();
elementData[s]=e;
size=s+1;
}

然後呼叫了非常關鍵的 grow(int minCapacity) 方法:

privateObject[]grow(intminCapacity){
intoldCapacity=elementData.length;
if(oldCapacity>0||elementData!=DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
intnewCapacity=ArraysSupport.newLength(oldCapacity,
minCapacity-oldCapacity,/*minimumgrowth*/
oldCapacity>>1/*preferredgrowth*/);
returnelementData=Arrays.copyOf(elementData,newCapacity);
}else{
returnelementData=newObject[Math.max(DEFAULT_CAPACITY,minCapacity)];
}
}

如果建立 ArrayList 的時候沒有指定初始大小,那麼 ArrayList 的初始大小就是 DEFAULT_CAPACITY:

privatestaticfinalintDEFAULT_CAPACITY=10;

可以容納 10 個元素。

還可以通過 add(int index, E element) 方法把元素新增到指定的位置:

alist.add(0,"沉默王三");

add(int index, E element) 方法的原始碼如下:

publicvoidadd(intindex,Eelement){
rangeCheckForAdd(index);
modCount++;
finalints;
Object[]elementData;
if((s=size)==(elementData=this.elementData).length)
elementData=grow();
System.arraycopy(elementData,index,
elementData,index+1,
s-index);
elementData[index]=element;
size=s+1;
}

該方法會呼叫到一個非常重要的本地方法 System.arraycopy(),它會對陣列進行復制(要插入位置上的元素往後複製,參照文章一開頭提到的兩張圖片)。

03、更新 ArrayList 中的元素

可以使用 set() 方法來更改 ArrayList 中的元素,需要提供下標和新元素。

alist.set(0,"沉默王四");

原來 0 位置上的元素為“沉默王三”,現在將其更新為“沉默王四”。

來看一下 set() 方法的原始碼:

publicEset(intindex,Eelement){
Objects.checkIndex(index,size);
EoldValue=elementData(index);
elementData[index]=element;
returnoldValue;
}

該方法會先對指定的下標進行檢查,看是否越界,然後替換新值並返回舊值。

04、刪除 ArrayList 中的元素

remove(int index) 方法用於刪除指定下標位置上的元素,remove(Object o) 方法用於刪除指定值的元素。

alist.remove(1);
alist.remove("沉默王四");

先來看 remove(int index) 方法的原始碼:

publicEremove(intindex){
Objects.checkIndex(index,size);
finalObject[]es=elementData;

@SuppressWarnings("unchecked")EoldValue=(E)es[index];
fastRemove(es,index);

returnoldValue;
}

該方法返回要刪除的元素,真正的刪除操作在 fastRemove(es, index) 方法中。

再來看 remove(Object o) 方法的原始碼:

publicbooleanremove(Objecto){
finalObject[]es=elementData;
finalintsize=this.size;
inti=0;
found:{
if(o==null){
for(;i<size;i++)
if(es[i]==null)
breakfound;
}else{
for(;i<size;i++)
if(o.equals(es[i]))
breakfound;
}
returnfalse;
}
fastRemove(es,i);
returntrue;
}

該方法通過 break label 的方式找到要刪除元素(null 的時候使用 == 操作符判斷,非 null 的時候使用 equals() 方法,意味著如果有相同元素時,刪除第一個)的下標,然後呼叫 fastRemove() 方法。

既然都呼叫了 fastRemove() 方法,那就繼續來跟蹤一下原始碼:

privatevoidfastRemove(Object[]es,inti){
modCount++;
finalintnewSize;
if((newSize=size-1)>i)
System.arraycopy(es,i+1,es,i,newSize-i);
es[size=newSize]=null;
}

當刪除的是末尾的元素時,不需要複製陣列,直接把末尾的元素賦值為 null 即可;否則的話,就需要呼叫 System.arraycopy() 對陣列進行復制。參照文章一開頭提到的第三張、第四張圖片。

05、查詢 ArrayList 中的元素

如果要正序查詢一個元素,可以使用 indexOf() 方法;如果要倒序查詢一個元素,可以使用 lastIndexOf() 方法。

alist.indexOf("沉默王二");
alist.lastIndexOf("沉默王二");

來看一下 indexOf() 方法的原始碼:

publicintindexOf(Objecto){
returnindexOfRange(o,0,size);
}

intindexOfRange(Objecto,intstart,intend){
Object[]es=elementData;
if(o==null){
for(inti=start;i<end;i++){
if(es[i]==null){
returni;
}
}
}else{
for(inti=start;i<end;i++){
if(o.equals(es[i])){
returni;
}
}
}
return-1;
}

如果元素為 null 的時候使用“==”操作符,否則使用 equals() 方法——該方法不是 null 安全的。

lastIndexOf() 方法和 indexOf() 方法類似,不過遍歷的時候從最後開始。

contains() 方法可以判斷 ArrayList 中是否包含某個元素,其內部呼叫了 indexOf() 方法:

publicbooleancontains(Objecto){
returnindexOf(o)>=0;
}

如果 ArrayList 中的元素是經過排序的,就可以使用二分查詢法,效率更快。

Collections 類的 sort() 方法可以對 ArrayList 進行排序,該方法會按照字母順序對 String 型別的列表進行排序。如果是自定義型別的列表,還可以指定 Comparator 進行排序。

List<String>copy=newArrayList<>(alist);
copy.add("a");
copy.add("c");
copy.add("b");
copy.add("d");

Collections.sort(copy);
System.out.println(copy);

輸出結果如下所示:

[a,b,c,d]

排序後就可以使用二分查詢法了:

intindex=Collections.binarySearch(copy,"b");

06、最後

關於 ArrayList,就先介紹這麼多吧,通過原始碼的角度,我想小夥伴們一定對 ArrayList 有了更深刻的印象。

簡單總結一下 ArrayList 的時間複雜度,方便後面學習 LinkedList 時作為一個對比。

1)通過下標(也就是 get(int index))訪問一個元素的時間複雜度為 O(1),因為是直達的,無論資料增大多少倍,耗時都不變。

2)新增一個元素(也就是 add())的時間複雜度為 O(1),因為直接新增到末尾。

3)刪除一個元素的時間複雜度為 O(n),因為要遍歷列表,資料量增大幾倍,耗時也增大幾倍。

4)查詢一個未排序的列表時間複雜度為 O(n),因為要遍歷列表;查詢排序過的列表時間複雜度為 O(log n),因為可以使用二分查詢法,當資料增大 n 倍時,耗時增大 logn 倍(這裡的 log 是以 2 為底的,每找一次排除一半的可能)。


我是沉默王二,一枚有顏值卻靠才華苟且的程式設計師。關注即可提升學習效率,別忘了三連啊,點贊、收藏、留言,我不挑,奧利給

注:如果文章有任何問題,歡迎毫不留情地指正。

很多讀者都同情我說,“二哥,你像母豬似的日更原創累不累?”我只能說寫作不易,且行且珍惜啊,關鍵是我真的喜歡寫作。最後,歡迎微信搜尋「沉默王二」第一時間閱讀,回覆「簡歷」更有阿里大佬的簡歷模板,本文 GitHub github.com/itwanger 已收錄,歡迎 star。