Java基础篇:数组
一、數組基礎知識:
1、什么是數組:
數組,就是一個容器,存放著同一種類型的數據的集合。數組的好處就是可以自動給數組中的元素從0開始編號,方便操作這些元素。數組屬于引用變量,并且數組的長度是固定的,數組的使用有四個步驟,聲明數組,分配空間,賦值,處理。
2、一維數組的聲明與創建:
元素類型[] 數組名 = new 元素類型[元素個數或數組長度];
元素類型[] 數組名 = new 元素類型[]{元素,元素,……};
元素類型[] 數組名 = {元素,元素,……};
示例:int[] arr = new int[5];
int[] arr = new int[]{3,5,1,7};
int[] arr = {3,5,1,7};
注意:給數組分配空間時,必須指定數組能夠存儲的元素個數來確定數組大小。創建數組之后不能修改數組的大小。可以使用length?屬性獲取數組的大小。
3、數組的初始化:
int[] arr = new int[5];//創建數組第一種方式,此時默認值都是為0 arr[0] = 1;//數組的初始化 arr[1] = 2;//數組的初始化int[] arr = new int[]{3,5,1,7};//第二種方式:創建并初始化數組int[] arr = {3,5,1,7};//第三種方式:創建并初始化數組int[] arr; arr = {1,2,3,4,5};//這種方式是錯誤的4、數組的常見異常:
(1)ArrayIndexOutOfBoundsException 索引值越界。
原因:訪問了不存在的索引值:
public static void main(String[] args) {int[] x = { 1, 2, 3 };System.out.println(x[3]);//數組角標從0開始 }(2)NullPointerException 空指針異常:
原因: 引用類型變量沒有指向任何對象,而訪問了對象的屬性或者是調用了對象的方法。
public static void main(String[] args) {int[] x = { 1, 2, 3 };x = null;System.out.println(x[1]); }5、數組內存分析:
6、Arrays的使用:
遍歷:?toString()????將數組的元素以字符串的形式返回
排序:?sort()????????將數組按照升序排列
查找:?binarySearch()在指定數組中查找指定元素,返回元素的索引,如果沒有找到返回(-插入點-1)?注意:使用查找的功能的時候,數組一定要先排序。
public static void main(String[] args) {int[] array = new int[]{10,30,50,40,60};System.out.println(Arrays.toString(array));Arrays.sort(array);System.out.println(Arrays.toString(array));System.out.println("最小值:"+array[0]+";最大值:"+array[array.length-1]);int result = Arrays.binarySearch(array, 40);System.out.println("目標值的角標:"+result); }7、二維數組:
(1)二維數組定義:數組類型[][]?數組名?=?new?數組類型[一維數組的個數][每一個一維數組中元素的個數];
public static void main(String[] args) { int[][] a = new int[3][4];System.out.println(a);//[[I@15db9742System.out.println(a.length);//獲取二維數組中存儲的一維數組的個數3System.out.println(a[0]);//獲取的是二維數組中第一個一維數組:[I@6d06d69cSystem.out.println(a[0].length);//第一個一維數組的長度:4System.out.println(Arrays.toString(a[0]));//默認值都是0:[0, 0, 0, 0] }疑問:?為什么a.length?=?3,?a[0].length?=?4?
(2)二維數組的初始化:
//靜態初始化: int[][] b = new int[][]{{11,12,13,14},{21,22,23,24},{31,32,33,34}}; //動態初始化: int[][] c = new int[3][4]; int value = 0; for(int i = 0;i<c.length;i++){for(int j = 0;j<c[i].length;j++){c[i][j] = ++value;} }?
二、數組提高:
1、數組的特點:
在java中有很多方式來存儲一列數據,而且在操作上面比數組方便的多?但為什么我們還需要使用數組,而不是替代它呢?
數組與其他種類的容器之間的區別有三個方面呢:效率、類型和保存基本類型的能力。在Java中,數組是一種效率最高的存儲和隨機訪問對象引用序列的方式。
數組確實是沒有List、Set這些集合使用方便,但是在某些方面數組還是存在一些優勢的,例如:速度,而且集合類的底層也都是通過數組來實現的。
--------這是ArrayList的add()------public boolean add(E e) {ensureCapacity(size + 1); // Increments modCount!!elementData[size++] = e;return true;}(1)例子:數組和list求和操作的比較:
Long time1 = System.currentTimeMillis();for(int i = 0 ; i < 100000000 ;i++){sum += arrays[i%10];}Long time2 = System.currentTimeMillis();System.out.println("數組求和所花費時間:" + (time2 - time1) + "毫秒");Long time3 = System.currentTimeMillis();for (int i = 0; i < 100000000; i++) {sum += list.get(i%10);}Long time4 = System.currentTimeMillis();System.out.println("List求和所花費時間:" + (time4 - time3) + "毫秒"); --------------Output: 數組求和所花費時間:696毫秒 List求和所花費時間:3498毫秒從上面的時間消耗上面來說數組對于基本類型的求和計算的速度是集合的5倍左右。其實在list集合中,求和當中有一個致命的動作:list.get(i)。這個動作是進行拆箱動作,Integer對象通過intValue方法自動轉換成一個int基本類型,在這里就產生了不必要的性能消耗。
所以在性能要求較高的場景中請優先考慮數組。
2、變長數組:
數組是定長的,一旦初始化聲明后是不可以改變長度的。一旦初始化聲明后是不可改變長度的。這對我們在實際開發中是非常不方便的,聰明的我們肯定是可以找到方法來實現的。
那么如何來實現變長數組呢?我們可以利用List集合add方法里面的擴容思路來模擬實現。下面是ArrayList的擴容方法:
public void ensureCapacity(int minCapacity) {modCount++; /*** 若當前需要的長度超過數組長度時進行擴容處理*/int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);}它的思路是將原始數組拷貝到新數組中,新數組是原始數組長度的1.5倍。所以模擬的數組擴容代碼如下:
public class ArrayUtils {/*** @desc 對數組進行擴容* @param <T>* @param datas 原始數組* @param newLen 擴容大小* @return T[]*/public static <T> T[] expandCapacity(T[] datas,int newLen){newLen = newLen < 0 ? datas.length :datas.length + newLen; //生成一個新的數組return Arrays.copyOf(datas, newLen);}/*** @desc 對數組進行擴容處理,1.5倍* @param <T>* @param datas 原始數組* @return T[]*/public static <T> T[] expandCapacity(T[] datas){int newLen = (datas.length * 3) / 2; //擴容原始數組的1.5倍//生成一個新的數組return Arrays.copyOf(datas, newLen);}/*** @desc 對數組進行擴容處理,* @param <T>* @param datas 原始數組* @param mulitiple 擴容的倍數* @return T[]*/public static <T> T[] expandCapacityMul(T[] datas,int mulitiple){mulitiple = mulitiple < 0 ? 1 : mulitiple;int newLen = datas.length * mulitiple;return Arrays.copyOf(datas,newLen );} }通過這種迂回的方式我們可以實現數組的擴容。因此在項目中如果確實需要變長的數據集,數組也是在考慮范圍之內的,我們不能因為他是固定長度而排斥他!
3、數組復制問題:
前在做集合拷貝的時候由于集合沒有拷貝的方法,所以一個一個的復制是非常麻煩的,所以我就干脆使用List.toArray()方法轉換成數組然后再通過Arrays.copyOf拷貝,在轉換成集合,個人覺得非常方便,殊不知我已經陷入了其中的陷進!我們知道若數組元素為對象,則數組里面數據是對象引用。
public class Test {public static void main(String[] args) {Person person_01 = new Person("chenssy_01");Person[] persons1 = new Person[]{person_01};Person[] persons2 = Arrays.copyOf(persons1,persons1.length);System.out.println("數組persons1:");display(persons1);System.out.println("---------------------");System.out.println("數組persons2:");display(persons2);//改變其值persons2[0].setName("chessy_02");System.out.println("------------改變其值后------------");System.out.println("數組persons1:");display(persons1);System.out.println("---------------------");System.out.println("數組persons2:");display(persons2);}public static void display(Person[] persons){for(Person person : persons){System.out.println(person.toString());}} } -------------Output: 數組persons1: 姓名是:chenssy_01 --------------------- 數組persons2: 姓名是:chenssy_01 ------------改變其值后------------ 數組persons1: 姓名是:chessy_02 --------------------- 數組persons2: 姓名是:chessy_02從結果中發現,persons1中的值也發生了改變,這是典型的淺拷貝問題。所以通過Arrays.copyOf()方法產生的數組是一個淺拷貝。同時數組的clone()方法也是,集合的clone()方法也是,所以我們在使用拷貝方法的同時一定要注意淺拷貝這問題。
4、數組轉換為List注意的地方:
(1)我們經常需要使用到Arrays這個工具的asList()方法將其轉換成列表。方便是方便,但是有時候會出現莫名其妙的問題。如下:
public static void main(String[] args) {int[] datas = new int[]{1,2,3,4,5};List list = Arrays.asList(datas);System.out.println(list.size());} ------------Output: 1?結果是1,是的你沒有看錯, 結果就是1。但是為什么會是1而不是5呢?先看asList()的源碼:
public static <T> List<T> asList(T... a) {return new ArrayList<T>(a);}注意這個參數:T…a,這個參數是一個泛型的變長參數,我們知道基本數據類型是不可能泛型化的,也是就說8個基本數據類型是不可作為泛型參數的,但是為什么編譯器沒有報錯呢?這是因為在java中,數組會當做一個對象來處理,它是可以泛型的,所以我們的程序是把一個int型的數組作為了T的類型,所以在轉換之后List中就只會存在一個類型為int數組的元素了。所以我們這樣的程序System.out.println(datas.equals(list.get(0)));輸出結果肯定是true。當然如果將int改為Integer,則長度就會變成5了。
(2)我們再看下面程序:
enum Week{Sum,Mon,Tue,Web,Thu,Fri,Sat}public static void main(String[] args) {Week[] weeks = {Week.Sum,Week.Mon,Week.Tue,Week.Web,Week.Thu,Week.Fri};List<Week> list = Arrays.asList(weeks);list.add(Week.Sat);}這個程序非常簡單,就是講一個數組轉換成list,然后改變集合中值,但是運行呢?
Exception in thread "main" java.lang.UnsupportedOperationExceptionat java.util.AbstractList.add(AbstractList.java:131)at java.util.AbstractList.add(AbstractList.java:91)at com.array.Test.main(Test.java:18)編譯沒錯,但是運行竟然出現了異常錯誤!UnsupportedOperationException ,當不支持請求的操作時,就會拋出該異常。從某種程度上來說就是不支持add方法,我們知道這是不可能的!什么原因引起這個異常呢?先看asList()的源代碼:
public static <T> List<T> asList(T... a) {return new ArrayList<T>(a);}這里是直接返回一個ArrayList對象返回,但是注意這個ArrayList并不是java.util.ArrayList,而是Arrays工具類的一個內之類:
private static class ArrayList<E> extends AbstractList<E>implements RandomAccess, java.io.Serializable{private static final long serialVersionUID = -2764017481108945198L;private final E[] a;ArrayList(E[] array) {if (array==null)throw new NullPointerException();a = array;}/** 省略方法 **/}但是這個內部類并沒有提供add()方法,那么查看父類:
public boolean add(E e) {add(size(), e);return true;}public void add(int index, E element) {throw new UnsupportedOperationException();}這里父類僅僅只是提供了方法,方法的具體實現卻沒有,所以具體的實現需要子類自己來提供,但是非常遺憾這個內部類ArrayList并沒有提高add的實現方法。在ArrayList中,它主要提供了如下幾個方法:
?????? 1、size:元素數量
?????? 2、toArray:轉換為數組,實現了數組的淺拷貝。
?????? 3、get:獲得指定元素。
?????? 4、contains:是否包含某元素。
?????? 所以綜上所述,asList返回的是一個長度不可變的列表。數組是多長,轉換成的列表是多長,我們是無法通過add、remove來增加或者減少其長度的。
?
參考文章:
https://blog.csdn.net/oguro/article/details/52971487
https://blog.csdn.net/chenssy/article/details/17732815
?
總結
以上是生活随笔為你收集整理的Java基础篇:数组的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java基础篇:异常机制
- 下一篇: Java集合篇:Map常用遍历方式 以及