zoukankan      html  css  js  c++  java
  • Java 面向对象(十)

    常用类之Arrays

    java.util.Arrays 类是 JDK 提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用。

    1、asList

    返回由指定数组支持的固定大小列表。

    public static <T> List<T> asList(T... a) {
    	return new ArrayList<>(a);
    }
    

    注意:这个方法返回的 ArrayList 不是我们常用的集合类 java.util.ArrayList。这里的 ArrayList 是 Arrays 的一个内部类 java.util.Arrays.ArrayList。

    这个内部类有如下属性和方法:

    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) {
    		a = Objects.requireNonNull(array);
    	}
    
    	@Override
    	public int size() {
    		return a.length;
    	}
    
    	@Override
    	public Object[] toArray() {
    		return a.clone();
    	}
    
    	@Override
    	@SuppressWarnings("unchecked")
    	public <T> T[] toArray(T[] a) {
    		int size = size();
    		if (a.length < size)
    			return Arrays.copyOf(this.a, size,
    								 (Class<? extends T[]>) a.getClass());
    		System.arraycopy(this.a, 0, a, 0, size);
    		if (a.length > size)
    			a[size] = null;
    		return a;
    	}
    
    	@Override
    	public E get(int index) {
    		return a[index];
    	}
    
    	@Override
    	public E set(int index, E element) {
    		E oldValue = a[index];
    		a[index] = element;
    		return oldValue;
    	}
    
    	@Override
    	public int indexOf(Object o) {
    		E[] a = this.a;
    		if (o == null) {
    			for (int i = 0; i < a.length; i++)
    				if (a[i] == null)
    					return i;
    		} else {
    			for (int i = 0; i < a.length; i++)
    				if (o.equals(a[i]))
    					return i;
    		}
    		return -1;
    	}
    
    	@Override
    	public boolean contains(Object o) {
    		return indexOf(o) != -1;
    	}
    
    	@Override
    	public Spliterator<E> spliterator() {
    		return Spliterators.spliterator(a, Spliterator.ORDERED);
    	}
    
    	@Override
    	public void forEach(Consumer<? super E> action) {
    		Objects.requireNonNull(action);
    		for (E e : a) {
    			action.accept(e);
    		}
    	}
    
    	@Override
    	public void replaceAll(UnaryOperator<E> operator) {
    		Objects.requireNonNull(operator);
    		E[] a = this.a;
    		for (int i = 0; i < a.length; i++) {
    			a[i] = operator.apply(a[i]);
    		}
    	}
    
    	@Override
    	public void sort(Comparator<? super E> c) {
    		Arrays.sort(a, c);
    	}
    }
    

    (1)返回的 ArrayList 数组是一个定长列表,我们只能对其进行查看或者修改,但是不能进行添加或者删除操作

    通过源码我们发现该类是没有add()或者remove() 这样的方法的,如果对其进行增加或者删除操作,都会调用其父类 AbstractList 对应的方法,而追溯父类的方法最终会抛出 UnsupportedOperationException 异常。

    String[] str = {"a","b","c"};
    List<String> listStr = Arrays.asList(str);
    listStr.set(1, "e");					// 可以进行修改
    System.out.println(listStr.toString());	// [a, e, c]
    listStr.add("a");						// 添加元素会报错 java.lang.UnsupportedOperationException 
    

    (2)引用类型的数组和基本类型的数组区别

    String[] str = { "a", "b", "c" };
    List listStr = Arrays.asList(str);
    System.out.println(listStr.size()); // 3
    
    int[] i = { 1, 2, 3 };
    List listI = Arrays.asList(i);
    System.out.println(listI.size()); 	// 1
    

    上面的结果第一个listStr.size()=3,而第二个 listI.size()=1。

    我们看源码,在 Arrays.asList 中,方法声明为 List asList(T... a)。该方法接收一个可变参数,并且这个可变参数类型是作为泛型的参数。我们知道基本数据类型是不能作为泛型的参数的,但是数组是引用类型,所以数组是可以泛型化的,于是 int[] 作为了整个参数类型,而不是 int 作为参数类型。

    所以将上面的方法泛型化补全应该是:

    String[] str = { "a", "b", "c" };
    List<String> listStr = Arrays.asList(str);
    System.out.println(listStr.size());		// 3
    
    int[] i = { 1, 2, 3 };
    List<int[]> listI = Arrays.asList(i);	// 注意这里List参数为 int[] ,而不是 int
    System.out.println(listI.size());		// 1
    
    Integer[] in = { 1, 2, 3 };
    List<Integer> listIn = Arrays.asList(in);	// 这里参数为int的包装类Integer,所以集合长度为3
    System.out.println(listIn.size());		// 3
    

    (3)返回的列表ArrayList里面的元素都是引用,不是独立出来的对象

    String[] str = { "a", "b", "c" };
    List<String> listStr = Arrays.asList(str);
    // 执行更新操作前
    System.out.println(Arrays.toString(str));// [a, b, c]
    listStr.set(0, "d");// 将第一个元素a改为d
    // 执行更新操作后
    System.out.println(Arrays.toString(str));// [d, b, c]
    

    我们看修改集合的内容,原数组的内容也变化了,所以这里传入的是引用类型。

    (4)已知数组数据,如何快速获取一个可进行增删改查的列表List?

    String[] str = { "a", "b", "c" };
    List<String> listStr = new ArrayList<>(Arrays.asList(str));
    listStr.add("d");
    System.out.println(listStr.size()); // 4
    

    (5)Arrays.asList() 方法使用场景

    Arrays工具类提供了一个方法asList,使用该方法可以将一个变长参数或者数组转换成List 。但是,生成的List的长度是固定的;能够进行修改操作(比如,修改某个位置的元素);不能执行影响长度的操作(如add、remove等操作),否则会抛出UnsupportedOperationException异常。

    所以 Arrays.asList 比较适合那些已经有数组数据或者一些元素,而需要快速构建一个List,只用于读取操作,而不进行添加或删除操作的场景。

    2、sort

    该方法是用于数组排序,在 Arrays 类中有该方法的一系列重载方法,能对7种基本数据类型,包括 byte,char,double,float,int,long,short 等都能进行排序,还有 Object 类型(实现了Comparable接口),以及比较器 Comparator 。

    (1)基本类型的数组

    int[] num = { 1, 3, 8, 5, 2, 4, 6, 7 };
    Arrays.sort(num);
    System.out.println(Arrays.toString(num));// [1, 2, 3, 4, 5, 6, 7, 8]
    

    (2)对象类型数组

    该类型的数组进行排序可以实现 Comparable 接口,重写 compareTo 方法进行排序。

    String[] str = { "a", "f", "c", "d" };
    Arrays.sort(str);
    System.out.println(Arrays.toString(str));// [a, c, d, f]
    

    String 类型实现了 Comparable 接口,内部的 compareTo 方法是按照字典码进行比较的。

    没有实现Comparable接口的,可以通过Comparator实现排序

    class Person {
    	String name;
    	int age;
    
    	public Person(String name, int age) {
    		this.name = name;
    		this.age = age;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public int getAge() {
    		return age;
    	}
    
    	@Override
    	public String toString() {
    		return "Person [name=" + name + ", age=" + age + "]";
    	}
    }
    
    public class Test {
    	public static void main(String[] args) {
    		Person[] p = new Person[] { new Person("zhangsan", 22), new Person("wangwu", 11), new Person("lisi", 33) };
    		Arrays.sort(p, new Comparator<Person>() {
    			@Override
    			public int compare(Person o1, Person o2) {
    				if (o1 == null || o2 == null) {
    					return 0;
    				}
    				return o1.getAge() - o2.getAge();
    			}
    		});
    		System.out.println(Arrays.toString(p));
    	}
    }
    

    3、binarySearch

    用二分法查找数组中的某个元素。该方法和 sort 方法一样,适用于各种基本数据类型以及对象。

    注意:二分法是对有序的数组进行查找(比如先用Arrays.sort()进行排序,然后调用此方法进行查找)。找到元素返回下标,没有则返回 -1

    int[] num = { 1, 3, 8, 5, 2, 4, 6, 7 };
    Arrays.sort(num);
    System.out.println(Arrays.toString(num));// [1, 2, 3, 4, 5, 6, 7, 8]
    System.out.println(Arrays.binarySearch(num, 2));// 返回元素的下标 1
    

    具体源码实现:

    public static int binarySearch(int[] a, int key) {
    	return binarySearch0(a, 0, a.length, key);
    }
    
    private static int binarySearch0(int[] a, int fromIndex, int toIndex,int key) {
    	int low = fromIndex;
    	int high = toIndex - 1;
    	
    	while (low <= high) {
    		int mid = (low + high) >>> 1;//取中间值下标
    		int midVal = a[mid];//取中间值
    		
    		if (midVal < key)
    		low = mid + 1;
    		else if (midVal > key)
    		high = mid - 1;
    		else
    		return mid; 
    	}
    	return -(low + 1); 
    }
    

    4、copyOf 和 copyOfRange

    (1)copyOf

    拷贝数组元素。以使副本具有指定的长度。底层采用 System.arraycopy() 实现,这是一个native方法。

    public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
    

    e.g.

    String[] str = { "1", "2", "5", "3", "4" };
    str = Arrays.copyOf(str, 10);
    System.out.println(Arrays.toString(str));
    

    (2)copyOfRange

    将指定数组的指定范围复制到一个新数组。 底层采用 System.arraycopy()实现。

    5、equals 和 deepEquals

    (1)equals

    equals 用来比较两个数组中对应位置的每个元素是否相等。

    八种基本数据类型以及对象都能进行比较。

    我们先看看 int类型的数组比较源码实现:

    public static boolean equals(int[] a, int[] a2) {
    	if (a==a2)//数组引用相等,则里面的元素一定相等
    		return true;
    	if (a==null || a2==null)//两个数组其中一个为null,都返回false
    		return false;
    
    	int length = a.length;
    	if (a2.length != length)//两个数组长度不等,返回false
    		return false;
    
    	for (int i=0; i<length; i++)//通过for循环依次比较数组中每个元素是否相等
    		if (a[i] != a2[i])
    			return false;
    
    	return true;
    }
    

    在看对象数组的比较:

    public static boolean equals(Object[] a, Object[] a2) {
    	if (a==a2)
    		return true;
    	if (a==null || a2==null)
    		return false;
    
    	int length = a.length;
    	if (a2.length != length)
    		return false;
    
    	for (int i=0; i<length; i++) {
    		Object o1 = a[i];
    		Object o2 = a2[i];
    		if (!(o1==null ? o2==null : o1.equals(o2)))
    			return false;
    	}
    
    	return true;
    }
    

    (2)deepEquals

    也是用来比较两个数组的元素是否相等,不过 deepEquals 能够进行比较多维数组,而且是任意层次的嵌套数组。

    String[][] name1 = { { "G", "a", "o" }, { "H", "u", "a", "n" }, { "j", "i", "e" } };
    String[][] name2 = { { "G", "a", "o" }, { "H", "u", "a", "n" }, { "j", "i", "e" } };
    System.out.println(Arrays.equals(name1, name2));		// false
    System.out.println(Arrays.deepEquals(name1, name2));	// true
    

    6、fill

    该系列方法用于给数组赋值,并能指定某个范围赋值。

    源码分析:

    // 给a数组所有元素赋值 val
    public static void fill(int[] a, int val) {
    	for (int i = 0, len = a.length; i < len; i++)
    		a[i] = val;
    }
    
    // 给从 fromIndex 开始的下标,toIndex-1结尾的下标都赋值 val,左闭右开
    public static void fill(int[] a, int fromIndex, int toIndex, int val) {
    	rangeCheck(a.length, fromIndex, toIndex);// 判断范围是否合理
    	for (int i = fromIndex; i < toIndex; i++)
    		a[i] = val;
    }
    

    7、toString 和 deepToString

    toString 用来打印一维数组的元素,而 deepToString 用来打印多层次嵌套的数组元素。

    源码分析:

    public static String toString(int[] a) {
    	if (a == null)
    		return "null";
    	int iMax = a.length - 1;
    	if (iMax == -1)
    		return "[]";
    
    	StringBuilder b = new StringBuilder();
    	b.append('[');
    	for (int i = 0; ; i++) {
    		b.append(a[i]);
    		if (i == iMax)
    			return b.append(']').toString();
    		b.append(", ");
    	}
    }
    

    本文借鉴处:https://www.cnblogs.com/ysocean/p/8616122.html

  • 相关阅读:
    Apache 虚拟主机 VirtualHost 配置
    EAX、ECX、EDX、EBX寄存器的作用
    Python中文文档 目录(转载)
    八度
    POJ 3268 Silver Cow Party (最短路)
    POJ 2253 Frogger (求每条路径中最大值的最小值,Dijkstra变形)
    2013金山西山居创意游戏程序挑战赛——复赛(1) HDU 4557 非诚勿扰 HDU 4558 剑侠情缘 HDU 4559 涂色游戏 HDU 4560 我是歌手
    HDU 4549 M斐波那契数列(矩阵快速幂+欧拉定理)
    UVA 11624 Fire! (简单图论基础)
    HDU 3534 Tree (树形DP)
  • 原文地址:https://www.cnblogs.com/xzh0717/p/11297566.html
Copyright © 2011-2022 走看看