第16章 数组

第16章 数组

数组为什么特殊

就JDK8及以后而言,数组与其他容器方面的区别就是,数组的效率更高,同时可以保存基本类型(在自动装箱后,容器也相当于拥有该能力了),但是带来的缺点是数组对象的大小是固定的,在声明周期中不可改变,同时并没有容器类如此丰富的功能。

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        long l = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            list.add(i);
        }
        System.out.println(System.currentTimeMillis()-l);
        int[] ints = new int[1000000];
        l = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            ints[i] = i;
        }
        System.out.println(System.currentTimeMillis()-l);
    }

16.2 数组是第一级对象

无论使用哪种类型的数组,数组标识符其实只是一个引用,指向在堆中创建的一个真实对象,这个(数组)对象用以保存指向其他对象的引用。

  public static void main(String[] args) {
        int[] ints; // 未初始化前不能使用
        String[] a = new String[4]; // 引用类型被初始化为null,基本类型初始化为0
        String[] b = {"a", "b", "c"}; // 只能在初始化时使用
        b = new String[]{"a"};
        for (int i = 0; i < a.length/*仅代表数组容量,不能代表有多少元素*/; i++) {
            if (a[i] == null)
                a[i] = "a";
        }
    }

16.3 返回一个数组

public class Test2 {
    // 源数组应当作为一个域
    // 放在方法中的缺点是每次调用都会被创建,影响性能
     private static String[] strings = {"a","b","c","d","e","f"};
    public static void main(String[] args) {
        System.out.println(Arrays.toString(randomLetter(3)));
    }

    // 返回一个指定数量的随机不重复字符串数组
    public static String[] randomLetter(int n){
        // 当返回数组长度大于源数组长度时抛出异常
        if (n > strings.length)
            throw new RuntimeException("请求数组超长");
        String[] result = new String[n];
        // 为什么要引入一个和源数组长度相同的布尔数组?
        // 为了防止返回数组出现重复字母
        boolean[] picked = new boolean[strings.length];
        Random random = new Random();
        int t;
        for (int i = 0; i < n; i++) {
            do {
                t = random.nextInt(strings.length);
                // 当该字母已被选择后重新选择一个
            }while (picked[t]);
            result[i] = strings[t];
            // 该字母被选择后,设置该字母不可被选
            picked[t] = true;
        }
        return result;
    }
}

16.4 多维数组

   public static void main(String[] args) {
        int[][] ints ={{1,2,3},{1,2}};
        int[][][] tints = new int[2][3][4];
       // 将多维数组转为String
        System.out.println(Arrays.deepToString(tints));
        // [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]]
    }

粗糙数组

构成矩阵的每个向量都可以有任意长度

    public static void main(String[] args) {
        Random random = new Random();
        // 初始化时确定三维数组的长度
        int[][][] ints = new int[random.nextInt(5)][][];
        for (int i = 0; i < ints.length; i++) {
            // 确定二维数组的长度
            ints[i] = new int[random.nextInt(5)][];
            for (int j = 0; j < ints[i].length; j++) {
                // 确定三维数组的长度
                ints[i][j] = new int[random.nextInt(5)];
            }
        }
        System.out.println(Arrays.deepToString(ints));
        // 处理非基类类型数组
          String[][][] strings = {/*三维*/
                {/*二维*/
                    
                },{
                    {/*一维*/"a","b"},
                    {"c"}
                }
        };
    }

16.5 数组与泛型

class A<T>{
    // 更多的时候是使用参数化方法而不是参数化类
    public  T[] f(T[] args){
        return args;
    }
}

 public static void main(String[] args) {
        // 不允许实例化具有参数化类型的数组,
        // 允许参数化数组本身的类型
        // A<String>[] as = new A<>[2]; // 不允许
        Integer[] integers = new A<Integer>().f(new Integer[]{1,2,3});
    }
public static void main(String[] args) {
        // 允许对有参数化类型数组的引用
        List<String>[] lists = new List[10];
        lists[0] = new ArrayList<>();
        // 泛型数组是Object类型
        Object[] objects = lists;
        // Object[]数组可以放入任意Object类型
        objects[1] = new ArrayList<Integer>();
    }
class A<T>{
    public T[] array;
    public A(int size){
        // array = new T[size]; // 不允许
        // 只能转型,存在隐患,可以存入其他类型
        array = (T[]) new Object[size];
    }
}

16.6 创建测试数据

Arrays.fill()

用同一个值填充各个位置

  public static void main(String[] args) {
        int[] ints = new int[5];
        Arrays.fill(ints,10);
        System.out.println(Arrays.toString(ints)); // [10, 10, 10, 10, 10]
        String[] strings = new String[5];
        Arrays.fill(strings,0,2,"a");
        System.out.println(Arrays.toString(strings)); // [a, a, null, null, null]
    }

数据生成器

interface Generator<T>{
    T next();
}

class RandomGenerator{
    static char[] chars = "abcdef".toCharArray();
    static Random random = new Random();
    // 随机生成字节
    public static class Byt implements Generator<Byte>{
        @Override
        public Byte next() {
            return (byte)random.nextInt();
        }
    }
    // 随机生成整数
    public static class Int implements Generator<Integer>{
        @Override
        public Integer next() {
            return random.nextInt();
        }
    }

	// 随机生成字符
    public static class Char implements Generator<Character>{
        @Override
        public Character next() {
            int i = random.nextInt(chars.length);
            return chars[i];
        }
    }
	// 随机生成字符串
    public static class Str implements Generator<String>{
        @Override
        public String next() {
            int offset = random.nextInt(chars.length);
            int count = 1+random.nextInt(chars.length-offset);
            return String.valueOf(chars,offset,count);
        }
    }
}

public class Test2 {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        test(RandomGenerator.class);
    }

    public static void test(Class<?> surroundingClass) throws IllegalAccessException, InstantiationException {
        // 获得内部所有类
        for(Class<?> c:surroundingClass.getClasses()){
            System.out.print(c.getSimpleName()+":");
           Generator<?> generator = (Generator<?>) c.newInstance();
            for (int i = 0; i < 5; i++) {
                System.out.println(generator.next()+" ");
            }
            System.out.println();
        }
    }
}
/*
Str:abcd abcd e e def 
Char:f c a d b 
Int:745926114 -1501156431 1314007545 850905548 1753242117 
Byt:-51 36 83 -128 -29 
*/

从Generator中创建数组

// 填充数组
    public static <T> T[] array(T[] ts,Generator<T> generator){
        for (int i = 0; i < ts.length; i++) {
            ts[i] = generator.next();
        }
        return ts;
    }

    // 生成数组
    public static <T> T[] array(Class<T> c,int size,Generator<T> generator){
        T[] ts = (T[]) Array.newInstance(c,size);
        return array(ts,generator);
    }

    // 上述两个生成器只能生成Object数组,不能生成基本类型数组,所以,需要转换器
    public static char[] primitive(Character[] characters){
        char[] chars = new char[characters.length];
        for (int i = 0; i < characters.length; i++) {
            // 自动转型
            chars[i] = characters[i];
        }
        return chars;
    }
    // 重载
    public static byte[] primitive(Byte[] objects){
        byte[] bytes = new byte[objects.length];
        for (int i = 0; i < objects.length; i++) {
            bytes[i] = objects[i];
        }
        return bytes;
    }

  public static void main(String[] args) {
        String[] strings = new String[5];
        // 填充
        array(strings,new RandomGenerator.Str());
        System.out.println(Arrays.toString(strings));
        // 生成
        Byte[] objects = array(Byte.class, 5, new RandomGenerator.Byt());
        // 转换
        byte[] bytes = primitive(objects);
        System.out.println(Arrays.toString(bytes));

    }

16.7 Arrays实用功能

复制数组

 public static void main(String[] args) {
        int[] ints1 = new int[5];
        Arrays.fill(ints1,1);
        int[] ints2 = new int[10];
        // 源数组,起始位置,目标数组,目标位置,长度
     	// 不能自动装箱,数组类型必须完全一致
        System.arraycopy(ints1,0,ints2,5,5);
        System.out.println(Arrays.toString(ints2)); // [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
    }

数组的比较

    public static void main(String[] args) {
        int[] ints1 = new int[5];
        Arrays.fill(ints1,1);
        int[] ints2 = new int[5];
        Arrays.fill(ints2,1);
        // 数组长度必须相等,每个元素也必须相等
        System.out.println(Arrays.equals(ints1,ints2)); // true
        ints2[0] = 2;
        System.out.println(Arrays.equals(ints1,ints2)); // false
        String[] strings1 = {"a","a","a"};
        String[] strings2 = {new String("a"),new String("a"),new String("a")};
        // 元素之间用equals()方法比较,虽然,strings1指向同一个位置,strings2是在堆中开辟了三个空间,但是他们的值是相等的
        System.out.println(Arrays.equals(strings1,strings2)); // true
    }

数组元素的比较

第一种比较,实现Comparable接口

class ComType implements Comparable<ComType>{
    private static Random random = new Random();
    private int i;
    private int j;

    public ComType(int i, int j) {
        this.i = i;
        this.j = j;
    }

    @Override
    public int compareTo(ComType o) {
        return i > o.i?1:(i == o.i?0:-1);
    }

    @Override
    public String toString() {
        return "{"+"i=" + i +
                ", j=" + j+"}" ;
    }
	// 生成ComType类
    public static Generator<ComType> generator(){
        return new Generator<ComType>() {
            @Override
            public ComType next() {
                return new ComType(random.nextInt(10),random.nextInt(10));
            }
        };
    }
}
  public static void main(String[] args) {
        ComType[] comTypes = new ComType[5];
        array(comTypes,ComType.generator());
        // 未排序
        System.out.println(Arrays.toString(comTypes));
      // [{i=9, j=3}, {i=6, j=1}, {i=9, j=6}, {i=0, j=5}, {i=2, j=1}]
        Arrays.sort(comTypes);
        // 排序后
        System.out.println(Arrays.toString(comTypes));
      // [{i=0, j=5}, {i=2, j=1}, {i=6, j=1}, {i=9, j=3}, {i=9, j=6}]
    }

反转自然排序

 public static void main(String[] args) {
        ComType[] comTypes = new ComType[5];
        array(comTypes,ComType.generator());
        Arrays.sort(comTypes,/*反转自然排序*/Collections.reverseOrder());
        // 排序后
        System.out.println(Arrays.toString(comTypes));
     // [{i=7, j=4}, {i=5, j=4}, {i=3, j=4}, {i=2, j=8}, {i=1, j=3}]
    }

自定义排序规则

class MyComparator implements Comparator<ComType>{

    // 按j的大小排序
    @Override
    public int compare(ComType o1, ComType o2) {
        return o1.j > o2.j?1:(o1.j == o2.j?0:-1);
    }
}

 public static void main(String[] args) {
        ComType[] comTypes = new ComType[5];
        array(comTypes,ComType.generator());
        Arrays.sort(comTypes,new MyComparator());
        // 排序后
        System.out.println(Arrays.toString(comTypes));
        // [{i=1, j=2}, {i=8, j=3}, {i=3, j=3}, {i=1, j=8}, {i=9, j=9}]
    }

数组排序

    public static void main(String[] args) {
        String[] strings = new String[5];
        // 排序前
        array(strings,new RandomGenerator.Str());
        System.out.println(Arrays.toString(strings)); // [cdEF, F, EF, F, d]
        // 排序后
        Arrays.sort(strings);
        System.out.println(Arrays.toString(strings)); // [EF, F, F, cdEF, d]
        // 反转排序后
        Arrays.sort(strings,Collections.reverseOrder());
        System.out.println(Arrays.toString(strings)); // [d, cdEF, F, F, EF]
        // 忽略大小写
        Arrays.sort(strings,String.CASE_INSENSITIVE_ORDER);
        System.out.println(Arrays.toString(strings)); // [cdEF, d, EF, F, F]

    }

在已排序的数组中查找

  public static void main(String[] args) {
        int[] ints = {10,20,30,40,50};
        int i = Arrays.binarySearch(ints, 30);
        System.out.println(i); // 2
        i = Arrays.binarySearch(ints, 6);
        System.out.println(i); // -1
        i = Arrays.binarySearch(ints, 60);
        System.out.println(i); // -6
    }
/*
如果可以查到元素,返回该元素的索引;
含有重复元素,无法保证返回的是哪一个的索引;
如果,不含有该元素则返回-插入点-1;
插入点:第一个大于要查找元素的索引,如果插入值最大,则为该数组的长度
*/

排序对象数组时,如果使用了自定义的排序,查询时也必须指定顺序

class A{
    public int i ;
    static Random random = new Random();
    public A(int i) {
        this.i = i;
    }

    @Override
    public String toString() {
        return "{" +
                "i=" + i +
                '}';
    }

    public static Generator<A> generator(){
        return new Generator<A>() {
            @Override
            public A next() {
                return new A(random.nextInt(10));
            }
        };
    }


}

class MyComparator implements Comparator<A>{
    @Override
    public int compare(A o1, A o2) {
        return  o2.i > o1.i?1:(o2.i == o1.i?0:-1);
    }
}

  public static void main(String[] args) {
        A[] as = new A[5];
        MyComparator myComparator = new MyComparator();
        array(as,A.generator());
        Arrays.sort(as,myComparator); // 指定了自己的排序
        System.out.println(Arrays.toString(as));
        int i = Arrays.binarySearch(as,as[2],myComparator); // 查询时也必须指定
        System.out.println(i);
    }

16.8 总结

数组在早期版本中是必须的,但是随着泛型和自动装箱技术的出现,容器几乎完全可以替代数组了,在开发中应该使用容器,除非是对性能有极高的要求。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值