二十二、数组(5)

本章概要

  • 数组元素修改
  • 数组并行
  • Arrays 工具类
  • 数组拷贝
  • 数组比较
  • 流和数组

数组元素修改

传递给 Arrays.setAll() 的生成器函数可以使用它接收到的数组索引修改现有的数组元素:

ModifyExisting.java

import java.util.Arrays;

import static com.example.test.ArrayShow.show;

public class ModifyExisting {
    public static void main(String[] args) {
        double[] da = new double[7];
        Arrays.setAll(da, new Rand.Double()::get);
        show(da);
        Arrays.setAll(da, n -> da[n] / 100); // [1]
        show(da);

    }
}

在这里插入图片描述

Rand.java

import java.util.*;
import java.util.function.*;

import static com.example.test.ConvertTo.primitive;

public interface Rand {
    int MOD = 10_000;

    class Boolean implements Supplier<java.lang.Boolean> {
        SplittableRandom r = new SplittableRandom(47);

        @Override
        public java.lang.Boolean get() {
            return r.nextBoolean();
        }

        public java.lang.Boolean get(int n) {
            return get();
        }

        public java.lang.Boolean[] array(int sz) {
            java.lang.Boolean[] result =
                    new java.lang.Boolean[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pboolean {
        public boolean[] array(int sz) {
            return primitive(new Boolean().array(sz));
        }
    }

    class Byte
            implements Supplier<java.lang.Byte> {
        SplittableRandom r = new SplittableRandom(47);

        @Override
        public java.lang.Byte get() {
            return (byte) r.nextInt(MOD);
        }

        public java.lang.Byte get(int n) {
            return get();
        }

        public java.lang.Byte[] array(int sz) {
            java.lang.Byte[] result =
                    new java.lang.Byte[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pbyte {
        public byte[] array(int sz) {
            return primitive(new Byte().array(sz));
        }
    }

    class Character
            implements Supplier<java.lang.Character> {
        SplittableRandom r = new SplittableRandom(47);

        @Override
        public java.lang.Character get() {
            return (char) r.nextInt('a', 'z' + 1);
        }

        public java.lang.Character get(int n) {
            return get();
        }

        public java.lang.Character[] array(int sz) {
            java.lang.Character[] result =
                    new java.lang.Character[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pchar {
        public char[] array(int sz) {
            return primitive(new Character().array(sz));
        }
    }

    class Short
            implements Supplier<java.lang.Short> {
        SplittableRandom r = new SplittableRandom(47);

        @Override
        public java.lang.Short get() {
            return (short) r.nextInt(MOD);
        }

        public java.lang.Short get(int n) {
            return get();
        }

        public java.lang.Short[] array(int sz) {
            java.lang.Short[] result =
                    new java.lang.Short[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pshort {
        public short[] array(int sz) {
            return primitive(new Short().array(sz));
        }
    }

    class Integer
            implements Supplier<java.lang.Integer> {
        SplittableRandom r = new SplittableRandom(47);

        @Override
        public java.lang.Integer get() {
            return r.nextInt(MOD);
        }

        public java.lang.Integer get(int n) {
            return get();
        }

        public java.lang.Integer[] array(int sz) {
            int[] primitive = new Pint().array(sz);
            java.lang.Integer[] result =
                    new java.lang.Integer[sz];
            for (int i = 0; i < sz; i++) {
                result[i] = primitive[i];
            }
            return result;
        }
    }

    class Pint implements IntSupplier {
        SplittableRandom r = new SplittableRandom(47);

        @Override
        public int getAsInt() {
            return r.nextInt(MOD);
        }

        public int get(int n) {
            return getAsInt();
        }

        public int[] array(int sz) {
            return r.ints(sz, 0, MOD).toArray();
        }
    }

    class Long implements Supplier<java.lang.Long> {
        SplittableRandom r = new SplittableRandom(47);

        @Override
        public java.lang.Long get() {
            return r.nextLong(MOD);
        }

        public java.lang.Long get(int n) {
            return get();
        }

        public java.lang.Long[] array(int sz) {
            long[] primitive = new Plong().array(sz);
            java.lang.Long[] result =
                    new java.lang.Long[sz];
            for (int i = 0; i < sz; i++) {
                result[i] = primitive[i];
            }
            return result;
        }
    }

    class Plong implements LongSupplier {
        SplittableRandom r = new SplittableRandom(47);

        @Override
        public long getAsLong() {
            return r.nextLong(MOD);
        }

        public long get(int n) {
            return getAsLong();
        }

        public long[] array(int sz) {
            return r.longs(sz, 0, MOD).toArray();
        }
    }

    class Float
            implements Supplier<java.lang.Float> {
        SplittableRandom r = new SplittableRandom(47);

        @Override
        public java.lang.Float get() {
            return (float) trim(r.nextDouble());
        }

        public java.lang.Float get(int n) {
            return get();
        }

        public java.lang.Float[] array(int sz) {
            java.lang.Float[] result =
                    new java.lang.Float[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pfloat {
        public float[] array(int sz) {
            return primitive(new Float().array(sz));
        }
    }

    static double trim(double d) {
        return
                ((double) Math.round(d * 1000.0)) / 100.0;
    }

    class Double implements Supplier<java.lang.Double> {
        SplittableRandom r = new SplittableRandom(47);

        @Override
        public java.lang.Double get() {
            return trim(r.nextDouble());
        }

        public java.lang.Double get(int n) {
            return get();
        }

        public java.lang.Double[] array(int sz) {
            double[] primitive =
                    new Rand.Pdouble().array(sz);
            java.lang.Double[] result =
                    new java.lang.Double[sz];
            for (int i = 0; i < sz; i++) {
                result[i] = primitive[i];
            }
            return result;
        }
    }

    class Pdouble implements DoubleSupplier {
        SplittableRandom r = new SplittableRandom(47);

        @Override
        public double getAsDouble() {
            return trim(r.nextDouble());
        }

        public double get(int n) {
            return getAsDouble();
        }

        public double[] array(int sz) {
            double[] result = r.doubles(sz).toArray();
            Arrays.setAll(result,
                    n -> result[n] = trim(result[n]));
            return result;
        }
    }

    class String
            implements Supplier<java.lang.String> {
        SplittableRandom r = new SplittableRandom(47);
        private int strlen = 7; // Default length

        public String() {
        }

        public String(int strLength) {
            strlen = strLength;
        }

        @Override
        public java.lang.String get() {
            return r.ints(strlen, 'a', 'z' + 1)
                    .collect(StringBuilder::new,
                            StringBuilder::appendCodePoint,
                            StringBuilder::append).toString();
        }

        public java.lang.String get(int n) {
            return get();
        }

        public java.lang.String[] array(int sz) {
            java.lang.String[] result =
                    new java.lang.String[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }
}

ConvertTo.java

public interface ConvertTo {
    static boolean[] primitive(Boolean[] in) {
        boolean[] result = new boolean[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i]; // Autounboxing
        }
        return result;
    }

    static char[] primitive(Character[] in) {
        char[] result = new char[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static byte[] primitive(Byte[] in) {
        byte[] result = new byte[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static short[] primitive(Short[] in) {
        short[] result = new short[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static int[] primitive(Integer[] in) {
        int[] result = new int[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static long[] primitive(Long[] in) {
        long[] result = new long[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static float[] primitive(Float[] in) {
        float[] result = new float[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static double[] primitive(Double[] in) {
        double[] result = new double[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    // Convert from primitive array to wrapped array:
    static Boolean[] boxed(boolean[] in) {
        Boolean[] result = new Boolean[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i]; // Autoboxing
        }
        return result;
    }

    static Character[] boxed(char[] in) {
        Character[] result = new Character[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static Byte[] boxed(byte[] in) {
        Byte[] result = new Byte[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static Short[] boxed(short[] in) {
        Short[] result = new Short[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static Integer[] boxed(int[] in) {
        Integer[] result = new Integer[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static Long[] boxed(long[] in) {
        Long[] result = new Long[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static Float[] boxed(float[] in) {
        Float[] result = new Float[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }

    static Double[] boxed(double[] in) {
        Double[] result = new Double[in.length];
        for (int i = 0; i < in.length; i++) {
            result[i] = in[i];
        }
        return result;
    }
}

ArrayShow.java

import java.util.*;

public interface ArrayShow {
    static void show(Object[] a) {
        System.out.println(Arrays.toString(a));
    }

    static void show(boolean[] a) {
        System.out.println(Arrays.toString(a));
    }

    static void show(byte[] a) {
        System.out.println(Arrays.toString(a));
    }

    static void show(char[] a) {
        System.out.println(Arrays.toString(a));
    }

    static void show(short[] a) {
        System.out.println(Arrays.toString(a));
    }

    static void show(int[] a) {
        System.out.println(Arrays.toString(a));
    }

    static void show(long[] a) {
        System.out.println(Arrays.toString(a));
    }

    static void show(float[] a) {
        System.out.println(Arrays.toString(a));
    }

    static void show(double[] a) {
        System.out.println(Arrays.toString(a));
    }

    // Start with a description:
    static void show(String info, Object[] a) {
        System.out.print(info + ": ");
        show(a);
    }

    static void show(String info, boolean[] a) {
        System.out.print(info + ": ");
        show(a);
    }

    static void show(String info, byte[] a) {
        System.out.print(info + ": ");
        show(a);
    }

    static void show(String info, char[] a) {
        System.out.print(info + ": ");
        show(a);
    }

    static void show(String info, short[] a) {
        System.out.print(info + ": ");
        show(a);
    }

    static void show(String info, int[] a) {
        System.out.print(info + ": ");
        show(a);
    }

    static void show(String info, long[] a) {
        System.out.print(info + ": ");
        show(a);
    }

    static void show(String info, float[] a) {
        System.out.print(info + ": ");
        show(a);
    }

    static void show(String info, double[] a) {
        System.out.print(info + ": ");
        show(a);
    }
}

[1] Lambdas在这里特别有用,因为数组总是在lambda表达式的范围内。

数组并行

我们很快就不得不面对并行的主题。例如,“并行”一词在许多Java库方法中使用。您可能听说过类似“并行程序运行得更快”这样的说法,这是有道理的—当您可以有多个处理器时,为什么只有一个处理器在您的程序上工作呢? 如果您认为您应该利用其中的“并行”,这是很容易被原谅的。

要是这么简单就好了。不幸的是,通过采用这种方法,您可以很容易地编写比非并行版本运行速度更慢的代码。在你深刻理解所有的问题之前,并行编程看起来更像是一门艺术而非科学。
以下是简短的版本:用简单的方法编写代码。不要开始处理并行性,除非它成为一个问题。您仍然会遇到并行性。在本章中,我们将介绍一些为并行执行而编写的Java库方法。因此,您必须对它有足够的了解,以便进行基本的讨论,并避免出现错误。

在阅读并发编程这一章之后,您将更深入地理解它(但是,唉,这还远远不够。只是这些的话,充分理解这个主题是不可能的)。
在某些情况下,即使您只有一个处理器,无论您是否显式地尝试并行,并行实现是惟一的、最佳的或最符合逻辑的选择。它是一个可以一直使用的工具,所以您必须了解它的相关问题。

最好从数据的角度来考虑并行性。对于大量数据(以及可用的额外处理器),并行可能会有所帮助。但您也可能使事情变得更糟。

在本书的其余部分,我们将遇到不同的情况:

  • 1、所提供的惟一选项是并行的。这很简单,因为我们别无选择,只能使用它。这种情况是比较罕见的。
  • 2、有多个选项,但是并行版本(通常是最新的版本)被设计成在任何地方都可以使用(甚至在那些不关心并行性的代码中),如案例#1。我们将按预期使用并行版本。
  • 3、案例1和案例2并不经常发生。相反,您将遇到某些算法的两个版本,一个用于并行使用,另一个用于正常使用。我将描述并行的一个,但不会在普通代码中使用它,因为它也许会产生所有可能的问题。

流式编程产生优雅的代码。例如,假设我们想要创建一个数值由从零开始填充的长数组:

import java.util.stream.LongStream;

import static com.example.test.ArrayShow.show;

public class CountUpward {
    static long[] fillCounted(int size) {
        return LongStream.iterate(0, i -> i + 1).limit(size).toArray();
    }

    public static void main(String[] args) {
        long[] l1 = fillCounted(20); // No problem
        show(l1);
        // On my machine, this runs out of heap space:
        // - long[] l2 = fillCounted(10_000_000);
    }
}

二十二、数组(5)_第1张图片

实际上可以存储到将近1000万,但是之后就会耗尽堆空间。常规的 setAll() 是有效的,但是如果我们能更快地处理如此大量的数字,那就更好了。
我们可以使用 setAll() 初始化更大的数组。如果速度成为一个问题,Arrays.parallelSetAll() 将(可能)更快地执行初始化(请记住并行性中描述的问题)。

import java.util.Arrays;

public class ParallelSetAll {
    static final int SIZE = 10_000_000;

    static void intArray() {
        int[] ia = new int[SIZE];
        Arrays.setAll(ia, new Rand.Pint()::get);
        Arrays.parallelSetAll(ia, new Rand.Pint()::get);
    }

    static void longArray() {
        long[] la = new long[SIZE];
        Arrays.setAll(la, new Rand.Plong()::get);
        Arrays.parallelSetAll(la, new Rand.Plong()::get);
    }

    public static void main(String[] args) {
        intArray();
        longArray();
    }
}

数组分配和初始化是在单独的方法中执行的,因为如果两个数组都在 main() 中分配,它会耗尽内存(至少在我的机器上是这样。还有一些方法可以告诉Java在启动时分配更多的内存)。

Arrays工具类

您已经看到了 java.util.Arrays 中的 fill()setAll()/parallelSetAll() 。该类包含许多其他有用的 静态 程序方法,我们将对此进行研究。

概述:

  • asList(): 获取任何序列或数组,并将其转换为一个 列表集合 (集合章节介绍了此方法)。
  • copyOf():以新的长度创建现有数组的新副本。
  • copyOfRange():创建现有数组的一部分的新副本。
  • equals():比较两个数组是否相等。
  • deepEquals():多维数组的相等性比较。
  • stream():生成数组元素的流。
  • hashCode():生成数组的哈希值(您将在附录中了解这意味着什么:理解equals()和hashCode())。
  • deepHashCode(): 多维数组的哈希值。
  • sort():排序数组
  • parallelSort():对数组进行并行排序,以提高速度。
  • binarySearch():在已排序的数组中查找元素。
  • parallelPrefix():使用提供的函数并行累积(以获得速度)。基本上,就是数组的reduce()。
  • spliterator():从数组中产生一个Spliterator;这是本书没有涉及到的流的高级部分。
  • toString():为数组生成一个字符串表示。你在整个章节中经常看到这种用法。
  • deepToString():为多维数组生成一个字符串。你在整个章节中经常看到这种用法。对于所有基本类型和对象,所有这些方法都是重载的。

数组拷贝

与使用for循环手工执行复制相比,copyOf()copyOfRange() 复制数组要快得多。这些方法被重载以处理所有类型。

我们从复制 intInteger 数组开始:

Count.java

import java.util.*;
import java.util.function.*;

import static com.example.test.ConvertTo.primitive;

public interface Count {
    class Boolean
            implements Supplier<java.lang.Boolean> {
        private boolean b = true;

        @Override
        public java.lang.Boolean get() {
            b = !b;
            return java.lang.Boolean.valueOf(b);
        }

        public java.lang.Boolean get(int n) {
            return get();
        }

        public java.lang.Boolean[] array(int sz) {
            java.lang.Boolean[] result =
                    new java.lang.Boolean[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pboolean {
        private boolean b = true;

        public boolean get() {
            b = !b;
            return b;
        }

        public boolean get(int n) {
            return get();
        }

        public boolean[] array(int sz) {
            return primitive(new Boolean().array(sz));
        }
    }

    class Byte
            implements Supplier<java.lang.Byte> {
        private byte b;

        @Override
        public java.lang.Byte get() {
            return b++;
        }

        public java.lang.Byte get(int n) {
            return get();
        }

        public java.lang.Byte[] array(int sz) {
            java.lang.Byte[] result =
                    new java.lang.Byte[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pbyte {
        private byte b;

        public byte get() {
            return b++;
        }

        public byte get(int n) {
            return get();
        }

        public byte[] array(int sz) {
            return primitive(new Byte().array(sz));
        }
    }

    char[] CHARS =
            "abcdefghijklmnopqrstuvwxyz".toCharArray();

    class Character
            implements Supplier<java.lang.Character> {
        private int i;

        @Override
        public java.lang.Character get() {
            i = (i + 1) % CHARS.length;
            return CHARS[i];
        }

        public java.lang.Character get(int n) {
            return get();
        }

        public java.lang.Character[] array(int sz) {
            java.lang.Character[] result =
                    new java.lang.Character[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pchar {
        private int i;

        public char get() {
            i = (i + 1) % CHARS.length;
            return CHARS[i];
        }

        public char get(int n) {
            return get();
        }

        public char[] array(int sz) {
            return primitive(new Character().array(sz));
        }
    }

    class Short
            implements Supplier<java.lang.Short> {
        short s;

        @Override
        public java.lang.Short get() {
            return s++;
        }

        public java.lang.Short get(int n) {
            return get();
        }

        public java.lang.Short[] array(int sz) {
            java.lang.Short[] result =
                    new java.lang.Short[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pshort {
        short s;

        public short get() {
            return s++;
        }

        public short get(int n) {
            return get();
        }

        public short[] array(int sz) {
            return primitive(new Short().array(sz));
        }
    }

    class Integer
            implements Supplier<java.lang.Integer> {
        int i;

        @Override
        public java.lang.Integer get() {
            return i++;
        }

        public java.lang.Integer get(int n) {
            return get();
        }

        public java.lang.Integer[] array(int sz) {
            java.lang.Integer[] result =
                    new java.lang.Integer[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pint implements IntSupplier {
        int i;

        public int get() {
            return i++;
        }

        public int get(int n) {
            return get();
        }

        @Override
        public int getAsInt() {
            return get();
        }

        public int[] array(int sz) {
            return primitive(new Integer().array(sz));
        }
    }

    class Long
            implements Supplier<java.lang.Long> {
        private long l;

        @Override
        public java.lang.Long get() {
            return l++;
        }

        public java.lang.Long get(int n) {
            return get();
        }

        public java.lang.Long[] array(int sz) {
            java.lang.Long[] result =
                    new java.lang.Long[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Plong implements LongSupplier {
        private long l;

        public long get() {
            return l++;
        }

        public long get(int n) {
            return get();
        }

        @Override
        public long getAsLong() {
            return get();
        }

        public long[] array(int sz) {
            return primitive(new Long().array(sz));
        }
    }

    class Float
            implements Supplier<java.lang.Float> {
        private int i;

        @Override
        public java.lang.Float get() {
            return java.lang.Float.valueOf(i++);
        }

        public java.lang.Float get(int n) {
            return get();
        }

        public java.lang.Float[] array(int sz) {
            java.lang.Float[] result =
                    new java.lang.Float[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pfloat {
        private int i;

        public float get() {
            return i++;
        }

        public float get(int n) {
            return get();
        }

        public float[] array(int sz) {
            return primitive(new Float().array(sz));
        }
    }

    class Double
            implements Supplier<java.lang.Double> {
        private int i;

        @Override
        public java.lang.Double get() {
            return java.lang.Double.valueOf(i++);
        }

        public java.lang.Double get(int n) {
            return get();
        }

        public java.lang.Double[] array(int sz) {
            java.lang.Double[] result =
                    new java.lang.Double[sz];
            Arrays.setAll(result, n -> get());
            return result;
        }
    }

    class Pdouble implements DoubleSupplier {
        private int i;

        public double get() {
            return i++;
        }

        public double get(int n) {
            return get();
        }

        @Override
        public double getAsDouble() {
            return get(0);
        }

        public double[] array(int sz) {
            return primitive(new Double().array(sz));
        }
    }
}

ArrayCopying.java

import java.util.Arrays;

import static com.example.test.ArrayShow.show;

class Sup {
    // Superclass
    private int id;

    Sup(int n) {
        id = n;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + id;
    }
}

class Sub extends Sup { // Subclass

    Sub(int n) {
        super(n);
    }
}

public class ArrayCopying {
    public static final int SZ = 15;

    public static void main(String[] args) {
        int[] a1 = new int[SZ];
        Arrays.setAll(a1, new Count.Integer()::get);
        show("a1", a1);
        int[] a2 = Arrays.copyOf(a1, a1.length); // [1]
        // Prove they are distinct arrays:
        Arrays.fill(a1, 1);
        show("a1", a1);
        show("a2", a2);
        // Create a shorter result:
        a2 = Arrays.copyOf(a2, a2.length / 2); // [2]
        show("a2", a2);
        // Allocate more space:
        a2 = Arrays.copyOf(a2, a2.length + 5);
        show("a2", a2);
        // Also copies wrapped arrays:
        Integer[] a3 = new Integer[SZ]; // [3]
        Arrays.setAll(a3, new Count.Integer()::get);
        Integer[] a4 = Arrays.copyOfRange(a3, 4, 12);
        show("a4", a4);
        Sub[] d = new Sub[SZ / 2];
        Arrays.setAll(d, Sub::new); // Produce Sup[] from Sub[]:
        Sup[] b = Arrays.copyOf(d, d.length, Sup[].class); // [4]
        show(b); // This "downcast" works fine:
        Sub[] d2 = Arrays.copyOf(b, b.length, Sub[].class); // [5]
        show(d2); // Bad "downcast" compiles but throws exception:
        Sup[] b2 = new Sup[SZ / 2];
        Arrays.setAll(b2, Sup::new);
        try {
            Sub[] d3 = Arrays.copyOf(b2, b2.length, Sub[].class); // [6]
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

二十二、数组(5)_第2张图片

[1] 这是复制的基本方法;只需给出返回的复制数组的大小。这对于编写需要调整存储大小的算法很有帮助。复制之后,我们把a1的所有元素都设为1,以证明a1的变化不会影响a2中的任何东西。

[2] 通过更改最后一个参数,我们可以缩短或延长返回的复制数组。

[3] copyOf()copyOfRange() 也可以使用包装类型。copyOfRange() 需要一个开始和结束索引。

[4] copyOf()copyOfRange() 都有一个版本,该版本通过在方法调用的末尾添加目标类型来创建不同类型的数组。我首先想到的是,这可能是一种从原生数组生成包装数组的方法,反之亦然。
但这没用。它的实际用途是“向上转换”和“向下转换”数组。也就是说,如果您有一个子类型(派生类型)的数组,而您想要一个基类型的数组,那么这些方法将生成所需的数组。

[5] 您甚至可以成功地“向下强制转换”,并从超类型的数组生成子类型的数组。这个版本运行良好,因为我们只是“upcast”。

[6] 这个“数组转换”将编译,但是如果类型不兼容,您将得到一个运行时异常。在这里,强制将基类型转换为派生类型是非法的,因为派生对象中可能有基对象中没有的属性和方法。

实例表明,原生数组和对象数组都可以被复制。但是,如果复制对象的数组,那么只复制引用—不复制对象本身。这称为浅拷贝(有关更多细节,请参阅附录:传递和返回对象)。

还有一个方法 System.arraycopy() ,它将一个数组复制到另一个已经分配的数组中。这将不会执行自动装箱或自动卸载—两个数组必须是完全相同的类型。

数组比较

数组 提供了 equals() 来比较一维数组,以及 deepEquals() 来比较多维数组。对于所有原生类型和对象,这些方法都是重载的。

数组相等的含义:数组必须有相同数量的元素,并且每个元素必须与另一个数组中的对应元素相等,对每个元素使用 equals()(对于原生类型,使用原生类型的包装类的 equals() 方法;例如,int的Integer.equals()。

import java.util.*;

public class ComparingArrays {
    public static final int SZ = 15;

    static String[][] twoDArray() {
        String[][] md = new String[5][];
        Arrays.setAll(md, n -> new String[n]);
        for (int i = 0; i < md.length; i++) {
            Arrays.setAll(md[i], new Rand.String()::get);
        }
        return md;
    }

    public static void main(String[] args) {
        int[] a1 = new int[SZ], a2 = new int[SZ];
        Arrays.setAll(a1, new Count.Integer()::get);
        Arrays.setAll(a2, new Count.Integer()::get);
        System.out.println("a1 == a2: " + Arrays.equals(a1, a2));
        a2[3] = 11;
        System.out.println("a1 == a2: " + Arrays.equals(a1, a2));
        Integer[] a1w = new Integer[SZ], a2w = new Integer[SZ];
        Arrays.setAll(a1w, new Count.Integer()::get);
        Arrays.setAll(a2w, new Count.Integer()::get);
        System.out.println("a1w == a2w: " + Arrays.equals(a1w, a2w));
        a2w[3] = 11;
        System.out.println("a1w == a2w: " + Arrays.equals(a1w, a2w));
        String[][] md1 = twoDArray(), md2 = twoDArray();
        System.out.println(Arrays.deepToString(md1));
        System.out.println("deepEquals(md1, md2): " + Arrays.deepEquals(md1, md2));
        System.out.println("md1 == md2: " + Arrays.equals(md1, md2));
        md1[4][1] = "#$#$#$#";
        System.out.println(Arrays.deepToString(md1));
        System.out.println("deepEquals(md1, md2): " + Arrays.deepEquals(md1, md2));
    }
}

二十二、数组(5)_第3张图片

最初,a1和a2是完全相等的,所以输出是true,但是之后其中一个元素改变了,这使得结果为false。a1w和a2w是对一个封装类型数组重复该练习。

md1md2 是通过 twoDArray() 以相同方式初始化的多维字符串数组。注意,deepEquals() 返回 true,因为它执行了适当的比较,而普通的 equals() 错误地返回 false。如果我们更改数组中的一个元素,deepEquals() 将检测它。

流和数组

stream() 方法很容易从某些类型的数组中生成元素流。

import java.util.*;

public class StreamFromArray {
    public static void main(String[] args) {
        String[] s = new Rand.String().array(10);
        Arrays.stream(s).skip(3).limit(5).map(ss -> ss + "!").forEach(System.out::println);
        int[] ia = new Rand.Pint().array(10);
        Arrays.stream(ia).skip(3).limit(5)
                .map(i -> i * 10).forEach(System.out::println);
        Arrays.stream(new long[10]);
        Arrays.stream(new double[10]);
        // Only int, long and double work:
        // - Arrays.stream(new boolean[10]);
        // - Arrays.stream(new byte[10]);
        // - Arrays.stream(new char[10]);
        // - Arrays.stream(new short[10]);
        // - Arrays.stream(new float[10]);
        // For the other types you must use wrapped arrays:
        float[] fa = new Rand.Pfloat().array(10);
        Arrays.stream(ConvertTo.boxed(fa));
        Arrays.stream(new Rand.Float().array(10));
    }
}

二十二、数组(5)_第4张图片

只有“原生类型” intlongdouble 可以与 Arrays.stream() 一起使用;对于其他的,您必须以某种方式获得一个包装类型的数组。

通常,将数组转换为流来生成所需的结果要比直接操作数组容易得多。请注意,即使流已经“用完”(您不能重复使用它),您仍然拥有该数组,因此您可以以其他方式使用它----包括生成另一个流。

你可能感兴趣的:(#,On,Java,基础卷,数组元素修改,数组并行,Arrays,工具类,数组拷贝,数组比较,流和数组)