在文本文件中求和整数的最快方法

2023-12-27

Question

假设您有一个大型 ASCII 文本文件,每行都有一个随机非负整数,每个整数的范围从 0 到 1,000,000,000。文件中有 100,000,000 行。读取文件并计算所有整数之和的最快方法是什么?

限制:我们有 10MB 的 RAM 可供使用。该文件大小为 1GB,因此我们不想读取整个文件然后对其进行处理。

以下是我尝试过的各种解决方案。我发现结果相当令人惊讶。

有什么比我错过的更快的事情吗?

请注意:下面给出的所有时间均用于运行算法10 times总共(运行一次并丢弃;启动计时器;运行 10 次;停止计时器)。该机器是相当慢的 Core 2 Duo。

方法一:自然法

首先要尝试的是显而易见的方法:

private long sumLineByLine() throws NumberFormatException, IOException {
    BufferedReader br = new BufferedReader(new FileReader(file));
    String line;
    long total = 0;
    while ((line = br.readLine()) != null) {
        int k = Integer.parseInt(line);
        total += k;
    }
    br.close();
    return total;
}

请注意,最大可能的返回值是 10^17,这仍然很容易适合long,所以我们不必担心溢出。

在我的机器上,运行这个 11 次并折扣第一次运行大约需要92.9秒.

方法二:小幅调整

受到评论的启发这个问题 https://stackoverflow.com/questions/25595844/optimal-solution-for-reading-from-file,我尝试不创建一个新的int k存储解析行的结果,而只是将解析的值直接添加到total。所以这:

    while ((line = br.readLine()) != null) {
        int k = Integer.parseInt(line);
        total += k;
    }

变成这样:

    while ((line = br.readLine()) != null)
        total += Integer.parseInt(line);

我确信这不会有任何区别,并且认为编译器很可能会为两个版本生成相同的字节码。但是,令我惊讶的是,它确实节省了一些时间:我们要92.1秒.

方法三:手动解析整数

到目前为止,代码中令我困扰的一件事是我们将String进入一个int,然后将其添加到最后。边走边添加不是更快吗?如果我们解析会发生什么String我们自己?像这样的东西...

private long sumLineByLineManualParse() throws NumberFormatException,
        IOException {
    BufferedReader br = new BufferedReader(new FileReader(file));
    String line;
    long total = 0;
    while ((line = br.readLine()) != null) {
        char chs[] = line.toCharArray();
        int mul = 1;
        for (int i = chs.length - 1; i >= 0; i--) {
            char c = chs[i];
            switch (c) {
            case '0':
                break;
            case '1':
                total += mul;
                break;
            case '2':
                total += (mul << 1);
                break;
            case '4':
                total += (mul << 2);
                break;
            case '8':
                total += (mul << 3);
                break;
            default:
                total += (mul*((byte) c - (byte) ('0')));   
            }
            mul*=10;
        }
    }
    br.close();
    return total;
}

我认为,这可能会节省一点时间,特别是在进行乘法时进行一些位移优化。但是转换为字符数组的开销一定会淹没任何收益:现在这需要148.2秒.

方法四:二进制处理

我们可以尝试的最后一件事是将文件作为二进制数据处理。

如果您不知道整数的长度,从前面解析它会很困难。向后解析它要容易得多:遇到的第一个数字是个位,下一个数字是十位,依此类推。因此,处理整个问题的最简单方法是向后读取文件。

如果我们分配一个byte[](比如说)8MB 的缓冲区,我们可以用文件的最后 8MB 填充它,处理它,然后读取前面的 8MB,依此类推。我们需要小心一点,当我们移动到下一个块时,不要搞砸正在解析的数字,但这是唯一的问题。

当我们遇到一个数字时,我们将它(根据它在数字中的位置适当相乘)添加到总数中,然后将系数乘以 10,以便我们准备好下一个数字。如果我们遇到任何不是数字的内容(CR 或 LF),我们只需重置系数即可。

private long sumBinary() throws IOException {
    RandomAccessFile raf = new RandomAccessFile(file, "r");
    int lastRead = (int) raf.length();
    byte buf[] = new byte[8*1024*1024];
    int mul = 1;
    long total = 0;
    while (lastRead>0) {
        int len = Math.min(buf.length, lastRead);
        raf.seek(lastRead-len);
        raf.readFully(buf, 0, len);
        lastRead-=len;
        for (int i=len-1; i>=0; i--) {
            //48 is '0' and 57 is '9'
            if ((buf[i]>=48) && (buf[i]<=57)) {
                total+=mul*(buf[i]-48);
                mul*=10;
            } else
                mul=1;
        }
    }
    raf.close();
    return total;
}

这运行在30.8秒!那是一个速度提高 3 倍超过之前的最好成绩。

后续问题

  1. 为什么这么快?我原本以为它会获胜,但并没有那么令人印象深刻。主要是转换为a的开销吗?String?还有所有幕后关于角色设置之类的担忧吗?
  2. 我们可以通过使用一个比这更好的方法吗?MappedByteBuffer帮助?我有一种感觉,调用从缓冲区读取的方法的开销会减慢速度,尤其是从缓冲区向后读取时。
  3. 向前读取文件而不是向后读取文件会更好,但仍向后扫描缓冲区吗?这个想法是,您读取文件的第一个块,然后向后扫描,但丢弃最后的半数。然后,当您读取下一个块时,您可以设置偏移量,以便从您丢弃的数字的开头读取。
  4. 有什么我没有想到的可以产生重大影响的事情吗?

更新:更多令人惊讶的结果

首先,观察。我以前就应该想到这一点,但我认为造成效率低下的原因是String基于阅读的内容并不是创造所有内容所花费的时间String但事实上它们的生命周期非常短暂:我们有 100,000,000 个对象供垃圾收集器处理。这势必会让它心烦意乱。

现在一些基于人们发布的答案/评论的实验。

我是否在缓冲区的大小上作弊?

一个建议是,既然BufferedReader使用默认的 16KB 缓冲区,我使用了 8MB 的缓冲区,我不是在比较。如果使用更大的缓冲区,速度肯定会更快。

这就是震惊。这sumBinary()昨天,方法(方法 4)在 8MB 缓冲区下运行了 30.8 秒。今天,代码不变,风向改变了,我们现在是 30.4 秒。如果我将缓冲区大小降低到 16KB,看看它会慢多少,它变得更快!它现在运行在23.7秒。疯狂的。谁看见那个人来了?!

一些实验表明 16KB 是最佳的。也许 Java 人员也做了同样的实验,这就是为什么他们选择 16KB!

问题是否受 I/O 限制?

我也想知道这个问题。磁盘访问花费了多少时间,数字运算花费了多少时间?如果几乎都是磁盘访问,正如对提议的答案之一的充分支持的评论所建议的那样,那么无论我们做什么,我们都将无法做出太大的改进。

通过运行代码并注释掉所有解析和数字运算,可以很容易地测试这一点,但读数仍然完好无损:

private long sumBinary() throws IOException {
    RandomAccessFile raf = new RandomAccessFile(file, "r");
    int lastRead = (int) raf.length();
    byte buf[] = new byte[16 * 1024];
    int mul = 1;
    long total = 0;
    while (lastRead > 0) {
        int len = Math.min(buf.length, lastRead);
        raf.seek(lastRead - len);
        raf.readFully(buf, 0, len);
        lastRead -= len;
        /*for (int i = len - 1; i >= 0; i--) {
            if ((buf[i] >= 48) && (buf[i] <= 57)) {
                total += mul * (buf[i] - 48);
                mul *= 10;
            } else
                mul = 1;
        }*/
    }
    raf.close();
    return total;
}

现在运行在3.7秒!对我来说,这看起来并不受 I/O 限制。

当然,部分 I/O 速度将来自磁盘缓存命中。但这并不是真正的重点:我们仍然需要 20 秒的 CPU 时间(也使用 Linux 的time命令),它足够大,可以尝试减少它。

向前扫描而不是向后扫描

我在原来的帖子中坚持认为,有充分的理由向后而不是向前扫描文件。我没有很好地解释这一点。这个想法是,如果你向前扫描一个数字,你必须累积扫描数字的总价值,然后将其相加。如果向后扫描,则可以将其添加到累计总数中。我的潜意识对自己有某种意义(稍后会详细说明),但我错过了一个关键点,这一点在答案之一中指出:为了向后扫描,我每次迭代都进行两次乘法,但是向前扫描您只需要一张。所以我编写了一个前向扫描版本:

private long sumBinaryForward() throws IOException {
    RandomAccessFile raf = new RandomAccessFile(file, "r");
    int fileLength = (int) raf.length();
    byte buf[] = new byte[16 * 1024];
    int acc = 0;
    long total = 0;
    int read = 0;
    while (read < fileLength) {
        int len = Math.min(buf.length, fileLength - read);
        raf.readFully(buf, 0, len);
        read += len;
        for (int i = 0; i < len; i++) {
            if ((buf[i] >= 48) && (buf[i] <= 57))
                acc = acc * 10 + buf[i] - 48;
            else {
                total += acc;
                acc = 0;
            }
        }
    }
    raf.close();
    return total;
}

这运行在20.0秒,远远击败了向后扫描版本。好的。

乘法缓存

不过,我在晚上意识到,虽然每次迭代执行两次乘法,但可以使用缓存来存储这些乘法,这样我就可以避免在向后迭代期间执行它们。当我醒来时,我很高兴看到有人有同样的想法!

关键是,我们扫描的数字最多有 10 个数字,而可能的数字只有 10 个,因此一个数字的值占累计总数的可能性只有 100 种。我们可以预先计算这些,然后在向后扫描代码中使用它们。这应该会击败前向扫描版本,因为我们现在已经完全摆脱了乘法。 (请注意,我们不能通过前向扫描来做到这一点,因为乘法是累加器的乘法,它可以取最多 10^9 的任何值。只有在后向扫描的情况下,两个操作数都仅限于几种可能性。)

private long sumBinaryCached() throws IOException {
    int mulCache[][] = new int[10][10];
    int coeff = 1;
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 10; j++)
            mulCache[i][j] = coeff * j;
        coeff *= 10;
    }

    RandomAccessFile raf = new RandomAccessFile(file, "r");
    int lastRead = (int) raf.length();
    byte buf[] = new byte[16 * 1024];
    int mul = 0;
    long total = 0;
    while (lastRead > 0) {
        int len = Math.min(buf.length, lastRead);
        raf.seek(lastRead - len);
        raf.readFully(buf, 0, len);
        lastRead -= len;
        for (int i = len - 1; i >= 0; i--) {
            if ((buf[i] >= 48) && (buf[i] <= 57))
                total += mulCache[mul++][buf[i] - 48];
            else
                mul = 0;
        }
    }
    raf.close();
    return total;
}

这运行在26.1秒。至少可以说,令人失望。就 I/O 而言,向后读取的效率较低,但我们已经看到 I/O 并不是这里的主要问题。我原以为这会产生巨大的积极影响。也许数组查找与我们替换的乘法一样昂贵。 (我确实尝试将数组设为 16x16,并使用位移位进行索引,但没有帮助。)

看起来正向扫描就是这样。

使用 MappedByteBuffer

接下来要添加的是MappedByteBuffer,看看这是否比使用原始数据更有效RandomAccessFile。不需要对代码进行太多更改。

private long sumBinaryForwardMap() throws IOException {
    RandomAccessFile raf = new RandomAccessFile(file, "r");
    byte buf[] = new byte[16 * 1024];
    final FileChannel ch = raf.getChannel();
    int fileLength = (int) ch.size();
    final MappedByteBuffer mb = ch.map(FileChannel.MapMode.READ_ONLY, 0,
            fileLength);
    int acc = 0;
    long total = 0;
    while (mb.hasRemaining()) {
        int len = Math.min(mb.remaining(), buf.length);
        mb.get(buf, 0, len);
        for (int i = 0; i < len; i++)
            if ((buf[i] >= 48) && (buf[i] <= 57))
                acc = acc * 10 + buf[i] - 48;
            else {
                total += acc;
                acc = 0;
            }
    }
    ch.close();
    raf.close();
    return total;
}

这似乎确实有所改善:我们现在处于19.0 秒。我们的个人最好成绩又落后了一秒!

那么多线程呢?

建议的答案之一涉及使用多个核心。我有点羞愧,我没有想到这一点!

答案有些棘手,因为假设这是一个 I/O 限制问题。考虑到 I/O 的结果,这似乎有点苛刻!无论如何,当然值得一试。

我们将使用 fork/join 来完成此操作。这是一个类,用于表示对文件的一部分进行计算的结果,请记住,左侧可能有部分结果(如果我们从数字的中间开始),右侧可能有部分结果(如果缓冲区完成了一半)。该类还有一种方法,允许我们将两个这样的结果粘合在一起,形成两个相邻子任务的组合结果。

private class SumTaskResult {
    long subtotal;
    int leftPartial;
    int leftMulCount;
    int rightPartial;

    public void append(SumTaskResult rightward) {
        subtotal += rightward.subtotal + rightPartial
                * rightward.leftMulCount + rightward.leftPartial;
        rightPartial = rightward.rightPartial;
    }
}

现在关键点:RecursiveTask计算结果。对于小问题(少于 64 个字符),它调用computeDirectly()在单线程中计算结果;对于较大的问题,它会分成两个,在单独的线程中解决两个子问题,然后合并结果。

private class SumForkTask extends RecursiveTask<SumTaskResult> {

    private byte buf[];
    // startPos inclusive, endPos exclusive
    private int startPos;
    private int endPos;

    public SumForkTask(byte buf[], int startPos, int endPos) {
        this.buf = buf;
        this.startPos = startPos;
        this.endPos = endPos;
    }

    private SumTaskResult computeDirectly() {
        SumTaskResult result = new SumTaskResult();
        int pos = startPos;

        result.leftMulCount = 1;

        while ((buf[pos] >= 48) && (buf[pos] <= 57)) {
            result.leftPartial = result.leftPartial * 10 + buf[pos] - 48;
            result.leftMulCount *= 10;
            pos++;
        }

        int acc = 0;
        for (int i = pos; i < endPos; i++)
            if ((buf[i] >= 48) && (buf[i] <= 57))
                acc = acc * 10 + buf[i] - 48;
            else {
                result.subtotal += acc;
                acc = 0;
            }

        result.rightPartial = acc;
        return result;
    }

    @Override
    protected SumTaskResult compute() {
        if (endPos - startPos < 64)
            return computeDirectly();
        int mid = (endPos + startPos) / 2;
        SumForkTask left = new SumForkTask(buf, startPos, mid);
        left.fork();
        SumForkTask right = new SumForkTask(buf, mid, endPos);
        SumTaskResult rRes = right.compute();
        SumTaskResult lRes = left.join();
        lRes.append(rRes);
        return lRes;
    }

}

请注意,这是在byte[],而不是整体MappedByteBuffer。原因是我们希望保持磁盘访问顺序。我们将采用相当大的块,分叉/连接,然后移动到下一个块。

这是执行此操作的方法。请注意,我们已将缓冲区大小提高到 1MB(之前不是最佳选择,但这里似乎更明智)。

private long sumBinaryForwardMapForked() throws IOException {
    RandomAccessFile raf = new RandomAccessFile(file, "r");
    ForkJoinPool pool = new ForkJoinPool();

    byte buf[] = new byte[1 * 1024 * 1024];
    final FileChannel ch = raf.getChannel();
    int fileLength = (int) ch.size();
    final MappedByteBuffer mb = ch.map(FileChannel.MapMode.READ_ONLY, 0,
            fileLength);
    SumTaskResult result = new SumTaskResult();
    while (mb.hasRemaining()) {
        int len = Math.min(mb.remaining(), buf.length);
        mb.get(buf, 0, len);
        SumForkTask task = new SumForkTask(buf, 0, len);
        result.append(pool.invoke(task));
    }
    ch.close();
    raf.close();
    pool.shutdown();
    return result.subtotal;
}

现在,令人心碎的失望是:这个漂亮的多线程代码现在需要32.2秒。为什么这么慢?我花了很长时间来调试这个,假设我做了一些非常错误的事情。

事实证明只需要进行一点小小的调整。我认为小问题和大问题之间的阈值 64 是一个合理的阈值;事实证明这完全是荒谬的。

这样想吧。子问题的大小完全相同,因此它们应该几乎在同一时间内完成。因此,分割成比可用处理器更多的部分确实没有意义。在我使用的机器上,只有两个核心,将阈值降低到 64 是荒谬的:它只会增加更多的开销。

现在您不想限制事情,以便它只使用两个核心,即使有更多可用的核心。也许正确的做法是找出运行时的处理器数量,并将其分成那么多部分。

无论如何,如果我将阈值更改为 512KB(缓冲区大小的一半),它现在会在13.3秒。降低到 128KB 或 64KB 将允许使用更多内核(分别最多 8 个或 16 个),并且不会显着影响运行时间。

所以多线程does有很大的不同。

这是一段相当漫长的旅程,但我们一开始需要 92.9 秒,现在已经减少到 13.3 秒……那就是七倍的速度的原始代码。这并不是通过改进渐近(大哦)时间复杂度,它从一开始就是线性(最优)的……这一切都是为了改进常数因子。

美好的一天的工作。

我想接下来我应该尝试使用 GPU...

后记:生成随机数文件

我使用以下代码生成了随机数,运行该代码并将其重定向到一个文件。显然我不能保证你最终会得到与我完全相同的随机数:)

public static void genRandoms() {
    Random r = new Random();
    for (int i = 0; i < 100000000; i++)
        System.out.println(r.nextInt(1000000000));
}

您的主要瓶颈将是文件 IO。解析和相加数字不应该对算法有任何贡献,因为这可以在文件 I/O 等待磁盘时在单独的线程中完成。

几年前,我研究了如何以最快的方式读取文件,并遇到了一些很好的建议 - 我将其实现为扫描例程,如下所示:

// 4k buffer size.
static final int SIZE = 4 * 1024;
static byte[] buffer = new byte[SIZE];

// Fastest because a FileInputStream has an associated channel.
private static void ScanDataFile(Hunter p, FileInputStream f) throws FileNotFoundException, IOException {
    // Use a mapped and buffered stream for best speed.
    // See: http://nadeausoftware.com/articles/2008/02/java_tip_how_read_files_quickly
    final FileChannel ch = f.getChannel();
    long red = 0L;
    do {
        final long read = Math.min(Integer.MAX_VALUE, ch.size() - red);
        final MappedByteBuffer mb = ch.map(FileChannel.MapMode.READ_ONLY, red, read);
        int nGet;
        while (mb.hasRemaining() && p.ok()) {
            nGet = Math.min(mb.remaining(), SIZE);
            mb.get(buffer, 0, nGet);
            for (int i = 0; i < nGet && p.ok(); i++) {
                p.check(buffer[i]);
                //size += 1;
            }
        }
        red += read;
    } while (red < ch.size() && p.ok());
    // Finish off.
    p.close();
    ch.close();
    f.close();
}

您可能希望在测试速度之前调整此技术,因为它使用称为Hunter寻找数据。

正如您所看到的,该建议是在 2008 年提出的,从那时起,Java 已经有了许多增强,因此这可能不会提供任何改进。

Added

我还没有对此进行测试,但这应该适合您的测试并使用相同的技术:

class Summer {

    long sum = 0;
    long val = 0;

    public void add(byte b) {
        if (b >= '0' && b <= '9') {
            val = (val * 10) + (b - '0');
        } else {
            sum += val;
            val = 0;
        }
    }

    public long getSum() {
        return sum + val;
    }
}

private long sumMapped() throws IOException {
    Summer sum = new Summer();
    FileInputStream f = new FileInputStream(file);
    final FileChannel ch = f.getChannel();
    long red = 0L;
    do {
        final long read = Math.min(Integer.MAX_VALUE, ch.size() - red);
        final MappedByteBuffer mb = ch.map(FileChannel.MapMode.READ_ONLY, red, read);
        int nGet;
        while (mb.hasRemaining()) {
            nGet = Math.min(mb.remaining(), SIZE);
            mb.get(buffer, 0, nGet);
            for (int i = 0; i < nGet; i++) {
                sum.add(buffer[i]);
            }
        }
        red += read;
    } while (red < ch.size());
    // Finish off.
    ch.close();
    f.close();
    return sum.getSum();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在文本文件中求和整数的最快方法 的相关文章

  • Spring应用中Eureka健康检查的问题

    我正在开发一个基于 Spring 的应用程序 其中包含多个微服务 我的一个微服务充当尤里卡服务器 到目前为止一切正常 在我所有其他微服务中 用 EnableEurekaClient 我想启用这样的健康检查 应用程序 yml eureka c
  • 如何循环遍历所有组合,例如48 选择 5 [重复]

    这个问题在这里已经有答案了 可能的重复 如何在java中从大小为n的集合中迭代生成k个元素子集 https stackoverflow com questions 4504974 how to iteratively generate k
  • 为什么 JTables 使 TableModel 在呈现时不可序列化?

    所以最近我正在开发一个工具 供我们配置某些应用程序 它不需要是什么真正令人敬畏的东西 只是一个具有一些 SQL 脚本生成功能并创建几个 XML 文件的基本工具 在此期间 我使用自己的 AbstractTableModel 实现创建了一系列
  • 为 java 游戏创建交互式 GUI

    大家好 我正在创建一个类似于 java 中的 farmville 的游戏 我只是想知道如何实现用户通常单击以与游戏客户端交互的交互式对象 按钮 我不想使用 swing 库 通用 Windows 看起来像对象 我想为我的按钮导入自定义图像 并
  • 动态选择端口号?

    在 Java 中 我需要获取端口号以在同一程序的多个实例之间进行通信 现在 我可以简单地选择一些固定的数字并使用它 但我想知道是否有一种方法可以动态选择端口号 这样我就不必打扰我的用户设置端口号 这是我的一个想法 其工作原理如下 有一个固定
  • Spring AspectJ 在双代理接口时失败:无法生成类的 CGLIB 子类

    我正在使用Spring的
  • Java 公历日历更改时区

    我正在尝试设置 HOUR OF DAY 字段并更改 GregorianCalendar 日期对象的时区 GregorianCalendar date new GregorianCalendar TimeZone getTimeZone GM
  • 如何知道Matlab中系统命令执行过程中经过的时间?

    我有一个运行系统脚本的 Matlab 代码 该脚本可能会因命令运行而停止 我想知道是否有一种方法可以让程序知道它是否花费了很长时间并执行其他操作 这是代码 tic status cmdout system iperfcmd The prog
  • PHP 与 MySQL 查询性能( if 、 函数 )

    我只看到这个artice http www onextrapixel com 2010 06 23 mysql has functions part 5 php vs mysql performance 我需要知道在这种情况下什么是最好的表
  • jdbc mysql loginTimeout 不起作用

    有人可以解释一下为什么下面的程序在 3 秒后超时 因为我将其设置为在 3 秒后超时 12秒 我特意关闭了mysql服务器来测试mysql服务器无法访问的这种场景 import java sql Connection import java
  • Java ResultSet 如何检查是否有结果

    结果集 http java sun com j2se 1 4 2 docs api java sql ResultSet html没有 hasNext 方法 我想检查 resultSet 是否有任何值 这是正确的方法吗 if resultS
  • 如何在谷歌地图android上显示多个标记

    我想在谷歌地图android上显示带有多个标记的位置 问题是当我运行我的应用程序时 它只显示一个位置 标记 这是我的代码 public class koordinatTask extends AsyncTask
  • 为什么 Java 8 不允许非公共默认方法?

    让我们举个例子 public interface Testerface default public String example return Hello public class Tester implements Testerface
  • java for windows 中的文件图标叠加

    我正在尝试像 Tortoise SVN 或 Dropbox 一样在文件和文件夹上实现图标叠加 我在网上查了很多资料 但没有找到Java的解决方案 Can anyone help me with this 很抱歉确认您的担忧 但这无法在 Ja
  • 使用 AsyncTask 传递值

    我一直在努力解决这个问题 但我已经到了不知道该怎么办的地步 我想做的是使用一个类下载文件并将其解析为字符串 然后将该字符串发送到另一个类来解析 JSON 内容 所有部件都可以单独工作 并且我已经单独测试了所有部件 我只是不知道如何将值发送到
  • 如何使用 jUnit 将测试用例添加到套件中?

    我有 2 个测试类 都扩展了TestCase 每个类都包含一堆针对我的程序运行的单独测试 如何将这两个类 以及它们拥有的所有测试 作为同一套件的一部分执行 我正在使用 jUnit 4 8 在 jUnit4 中你有这样的东西 RunWith
  • 专门针对 JSP 的测试驱动开发

    在理解 TDD 到底是什么之前 我就已经开始编写测试驱动的代码了 在没有实现的情况下调用函数和类可以帮助我以更快 更有效的方式理解和构建我的应用程序 所以我非常习惯编写代码 gt 编译它 gt 看到它失败 gt 通过构建其实现来修复它的过程
  • 等待进程释放文件

    我如何等待文件空闲以便ss Save 可以用新的覆盖它吗 如果我紧密地运行两次 左右 我会得到一个generic GDI error
  • 如何将双精度/浮点四舍五入为二进制精度?

    我正在编写对浮点数执行计算的代码的测试 不出所料 结果很少是准确的 我想在计算结果和预期结果之间设置一个容差 我已经证实 在实践中 使用双精度 在对最后两位有效小数进行四舍五入后 结果始终是正确的 但是usually四舍五入最后一位小数后
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类

随机推荐

  • 如何知道Android是否连接到WiFi或以太网? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 How to know whether I m connected to WiFi or ethernet in Android In
  • 使用一种编译器创建的静态 C 库是否与另一种编译器兼容?

    就我而言 我有一个使用代码源 gcc 构建的库 目标是arm cortex m4 然后我尝试将该库链接到使用 IAR 编译器编译的项目中 是否可以做到这一点 或者是否必须使用新工具重建库 哪些因素影响这个 静态库是几个目标文件的捆绑 这些目
  • Seaborn 散点图将气泡大小缩放为更大的点

    我正在关注这个example https seaborn pydata org generated seaborn scatterplot html我想创建更大的气泡 但无论我将尺寸列乘以多大 它们仍然很小 是否需要调整某种比例因子 我在文
  • 为什么这个 IFrame 没有占据完整的页面高度? [复制]

    这个问题在这里已经有答案了 我正在使用 Iframe 来显示 google com 或说任何网站 我使用了 height 100 但即便如此 我的 Iframe 大小也只是页面的一半 请让我知道为什么会发生这种情况 链接在这里 http j
  • Spark:测试 RDD 是否为空的有效方法

    没有一个isEmptyRDD 上的方法 那么测试 RDD 是否为空的最有效方法是什么 RDD isEmpty https issues apache org jira browse SPARK 5270将成为 Spark 1 3 0 的一部
  • 与PHP在同一页面显示结果

    我基本上是从 PHP 开始学习的 我想做的事情很简单 我想在同一页面显示计算结果 这是项目的结构 https i stack imgur com XGPVr png Code 索引 php header php
  • WAMP、Laravel 5:路由不起作用 - NotFoundHttpException

    我在本地计算机上使用 Laravel 5 和 WAMP 没有虚拟化 使用 Composer 创建一个名为 LaravelProject 的新项目后 我似乎根本无法让我的路线正常工作 我已经做了什么 Edited httpd conf to
  • 查找:显示模板速度

    我在新的 MVC4 站点上安装了 Mini Profiler 并注意到某些功能的等待时间很长Find DisplayTemplates包括字符串和日期时间 下面是一个例子 在另一个问题中 Sam Saffron 谈到了查找步骤 在随后的运行
  • 理解c++20中的convertible_to概念

    我对 C 20 概念仍然很陌生 我想知道为什么这不起作用 我想创建一个将数字连接为字符串的函数模板 所以我想尝试一些概念 我用了std convertible to检查输入的数据类型 即int在这种情况下 可以转换为std string 但
  • UICollectionView 标题宽度

    在我的 UICollectionViewFlowLayout 子类中 我有这个 self headerReferenceSize CGSizeMake 280 44 但是 标题的显示宽度为 320 这是集合视图的宽度 根据文档 这是正确的
  • 处理 Express 表单中的输入数组?

    假设我必须在同一页面上编辑一批相同类型的对象 jade form action method POST for each message id in messages ids input type text name message id
  • 如何删除Android searchview左侧的空间(不属于actionbar的一部分)?

    我在 android 应用程序中使用 searchview searchview 不是操作栏的一部分 我想删除搜索图标 搜索视图左侧的空格 我搜索了很多并收到了适用于作为操作栏一部分的搜索视图的答案 我尝试使用机器人 布局重力 机器人 重力
  • 为什么 const char* const & = "hello" 可以编译?

    我正在阅读一本书中的代码片段并发现 const char const a hello can compile const char a hello cannot 我所知道的是 在初始化引用时 数组到指针的转换不会发生 const char
  • graphstudio 中可用的引脚在代码中不存在

    我正在使用网络摄像头的源过滤器 当我在 graphstudio 中使用过滤器时 它有两个输出引脚 然而 在代码中 对 IEnumPins gt next 的调用始终返回 S FALSE 我还寻找了另一个可以创建引脚的接口 但没有找到这样的东
  • PHP S3上传进度

    这种情况已经发生过很多次了 但我仍然有点困惑 很多答案只关注谈论上传进度条 而不是从 S3 上传获取实际的上传进度 我已经阅读了很多问题并找到了很多软件 但我仍然没有更深入地理解 S3 上传的基本问题 有没有一种方法可以上传到 S3 同时了
  • “TypeError:item.getAttachmentsAsync 不是函数”Outlook 加载项 office-js 与 Vue

    我一直在关注此链接中的教程 https learn microsoft com en us javascript api outlook office messageread view outlook js preview getAttac
  • 如何删除jquery添加的样式属性

    我正在使用具有一些自定义要求的 devExpress 表 更新 休息了一天 然后回去并使用 React Styling 正确完成了它 感谢您的建议 在屏幕截图中 我禁用了某些单元格 但是 用户希望除所选行之外的所有单元格看起来均已禁用 使用
  • 跟随 NavigationLink 并返回后 SwiftUI .toolbar 消失

    我已将 toolbar 添加到 NavigationView 的顶层 最终将用于选择列表中的项目 而无需使用滑动手势 向上按钮 向下按钮等 我还有一个 navigationBar 正在进行 用于访问帐户和设置的其他视图 在大多数情况下 它看
  • D3.js:结合缩放/画笔

    我目前在 Mike Bostock 的工作画笔和缩放 https bl ocks org mbostock 34f08d5e11952a80609169b7917d4172例如 尽管我没有在 svg 上覆盖矩形对象 而是将其附加到我的图表上
  • 在文本文件中求和整数的最快方法

    Question 假设您有一个大型 ASCII 文本文件 每行都有一个随机非负整数 每个整数的范围从 0 到 1 000 000 000 文件中有 100 000 000 行 读取文件并计算所有整数之和的最快方法是什么 限制 我们有 10M