이전 섹션에서는 주로 배열의 기본 개념을 소개했으며, 이번 블로그 게시물에서는 주로 배열의 다른 측면을 소개합니다.
자바에서 일련의 데이터를 저장하는 방법은 여러 가지가 있는데, 배열보다 조작이 훨씬 편리한가요? 그런데 왜 배열을 교체하는 대신 배열을 사용해야 할까요? 배열은 효율성, 유형, 기본 유형을 유지하는 능력이라는 세 가지 영역에서 다른 종류의 컨테이너와 다릅니다. Java에서 배열은 일련의 객체 참조를 저장하고 무작위로 액세스하는 가장 효율적인 방법입니다.
프로젝트 설계에서 배열은 점점 덜 사용되며 실제로 List 및 Set만큼 사용하기 편리하지 않습니다. 배열은 속도와 같은 일부 측면에서 여전히 몇 가지 장점을 갖고 있으며 컬렉션 클래스의 최하위 계층도 배열을 통해 구현됩니다.
--------这是ArrayList的add()------ public boolean add(E e) { ensureCapacity(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
배열과 목록을 사용하여 비교 작업을 수행해 보겠습니다.
1.합
위의 시간 소모로 볼 때 기본형 배열의 합 계산 속도는 집합의 약 5배 정도입니다. 실제로 목록 컬렉션에는 합계에 치명적인 작업인 list.get(i)이 있습니다. 이 작업은 unboxing 작업입니다. Integer 개체는 intValue 메서드를 통해 자동으로 int 기본 유형으로 변환되므로 불필요한 성능 소모가 발생합니다.
그러니 제발 성능 요구 사항이 더 높은 시나리오에서는 어레이에 우선 순위를 부여합니다.
배열은 길이가 고정되어 있고 초기화 및 선언 후에는 길이를 변경할 수 없습니다. 이는 실제 개발에서는 매우 불편한 일입니다. 우리가 똑똑하다면 분명히 이를 달성할 수 있는 방법을 찾을 수 있습니다. Java가 다중 상속을 구현할 수 없는 것처럼 내부 클래스와 인터페이스를 사용하여 이를 구현할 수도 있습니다(Java 개선 장(9) ----- 다중 상속 구현 참조).
그럼 가변 길이 배열은 어떻게 구현하나요? 구현을 시뮬레이션하기 위해 List 컬렉션 add 메소드의 확장 아이디어를 사용할 수 있습니다. 다음은 ArrayList의 확장 방법입니다.
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毫秒
이 코드에서 유용한 부분은 if 문은 나중에. 아이디어는 원래 배열을 새 배열에 복사하는 것이며, 새 배열은 원래 배열 길이의 1.5배입니다. 따라서 시뮬레이션된 배열 확장 코드는 다음과 같습니다.
public void ensureCapacity(int minCapacity) { modCount++; int oldCapacity = elementData.length; /** * 若当前需要的长度超过数组长度时进行扩容处理 */ if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3) / 2 + 1; //扩容 if (newCapacity < minCapacity) newCapacity = minCapacity; //拷贝数组,生成新的数组 elementData = Arrays.copyOf(elementData, newCapacity); } }
이 우회 방식을 통해 배열을 얻을 수 있습니다. 확장 확장. 따라서 프로젝트에 가변 길이 데이터 세트가 꼭 필요한 경우 배열도 고려 대상에 포함됩니다. 왜냐하면 고정 길이이기 때문입니다.
하나씩 복사하기가 너무 귀찮아서 그냥 List.toArray()를 사용합니다. 배열로 변환한 후 Arrays.copyOf를 통해 복사하는 방법입니다. 컬렉션으로 변환한 후 개인적으로 매우 편리하다고 생각하지만 함정에 빠졌는지 모르겠습니다. 배열 요소가 객체인 경우 배열의 데이터는 객체 참조
public class ArrayUtils {
/**
* @desc 对数组进行扩容
* @author chenssy
* @data 2013-12-8
* @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倍
* @author chenssy
* @data 2013-12-8
* @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 对数组进行扩容处理,
* @author chenssy
* @data 2013-12-8
* @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 );
}
}
결과를 보면 person1의 값도 변경된 것을 볼 수 있는데, 이는 전형적인 얕은 복사 문제이다. 따라서 Arrays.copyOf() 메서드에 의해 생성된 배열은 얕은 복사본입니다. 동시에 배열의 clone() 메소드도 동일하고 컬렉션의 clone() 메소드도 동일하므로 복사 메소드를 사용할 때 얕은 복사 문제에 주의해야 합니다. 有关于深浅拷贝的博文,参考: 我们经常需要使用到Arrays这个工具的asList()方法将其转换成列表。方便是方便,但是有时候会出现莫名其妙的问题。如下: 结果是1,是的你没有看错, 结果就是1。但是为什么会是1而不是5呢?先看asList()的源码 注意这个参数:T…a,这个参数是一个泛型的变长参数,我们知道基本数据类型是不可能泛型化的,也是就说8个基本数据类型是不可作为泛型参数的,但是为什么编译器没有报错呢?这是因为在java中,数组会当做一个对象来处理,它是可以泛型的,所以我们的程序是把一个int型的数组作为了T的类型,所以在转换之后List中就只会存在一个类型为int数组的元素了。所以我们这样的程序System.out.println(datas.equals(list.get(0)));输出结果肯定是true。当然如果将int改为Integer,则长度就会变成5了。 我们在看下面程序: 这个程序非常简单,就是讲一个数组转换成list,然后改变集合中值,但是运行呢? 编译没错,但是运行竟然出现了异常错误!UnsupportedOperationException ,当不支持请求的操作时,就会抛出该异常。从某种程度上来说就是不支持add方法,我们知道这是不可能的!什么原因引起这个异常呢?先看asList()的源代码: 这里是直接返回一个ArrayList对象返回,但是注意这个ArrayList并不是java.util.ArrayList,而是Arrays工具类的一个内之类: 但是这个内部类并没有提供add()方法,那么查看父类: 这里父类仅仅只是提供了方法,方法的具体实现却没有,所以具体的实现需要子类自己来提供,但是非常遗憾 这个内部类ArrayList并没有提高add的实现方法。在ArrayList中,它主要提供了如下几个方法: 1、size:元素数量 2、toArray:转换为数组,实现了数组的浅拷贝。 3、get:获得指定元素。 4、contains:是否包含某元素。 요약하자면 asList는 길이가 불변인 목록을 반환합니다. 배열의 길이와 변환된 목록의 길이는 얼마입니까? 추가 또는 제거를 통해 길이를 늘리거나 줄일 수 없습니다.
위 내용은 Java 개선 장(19)------Array 2의 내용입니다. PHP 중국어 웹사이트(www.php.cn)! 渐析java的浅拷贝和深拷贝:http://www.php.cn/。
使用序列化实现对象的拷贝:http://www.php.cn/
六、数组转换为List注意地方
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
public static <T> List<T> asList(T... a) {
return new ArrayList<T>(a);
}
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);
}
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:131)
at java.util.AbstractList.add(AbstractList.java:91)
at com.array.Test.main(Test.java:18)
public static <T> List<T> asList(T... a) {
return new ArrayList<T>(a);
}
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;
}
/** 省略方法 **/
}
public boolean add(E e) {
add(size(), e);
return true;
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}