对列表中的多个 BigInteger 值执行逻辑与运算 Java

Perform logical AND operation on multiple BigInteger Values in lists Java

我正在尝试开发一种在多个 BigInteger 值之间执行逻辑 AND 运算的有效方法。例如,让我们考虑下面的片段:

public static void main(String[] args) {
    List<BigInteger> a = Arrays.asList(new BigInteger("1000",2), new BigInteger("101",2), new BigInteger("111",2));
    List<BigInteger> b = Arrays.asList(new BigInteger("10",2), new BigInteger("10110",2), new BigInteger("10011",2));
    List<BigInteger> c = Arrays.asList(new BigInteger("1",2), new BigInteger("10110",2), new BigInteger("11110",2));

    List<BigInteger> dd = IntStream.range(0, a.size())
                              .mapToObj(i -> a.get(i).and(b.get(i)).and(c.get(i)))
                              .collect(Collectors.toList());
    
    System.out.println(dd);
    //OUTPUT = [0, 4, 2]
}

但是,此解决方案的问题是 map 函数接受的值的静态数量。让我们假设一个包含多个列表(也超过 3 个)的 ArrayList,我该如何更改之前的解决方案?

public static void main(String[] args) {
    List<BigInteger> a = Arrays.asList(new BigInteger("1000",2), new BigInteger("101",2), new BigInteger("111",2));
    List<BigInteger> b = Arrays.asList(new BigInteger("10",2), new BigInteger("10110",2), new BigInteger("10011",2));
    List<BigInteger> c = Arrays.asList(new BigInteger("1",2), new BigInteger("10110",2), new BigInteger("11110",2));
    ArrayList<List<BigInteger>> collection = new ArrayList<List<BigInteger>>();
    collection.add(a);
    collection.add(b);
    collection.add(c);
    List<BigInteger> dd = null;
    //......
    System.out.println(dd);
}

请注意,我决定选择采用 stream.map(),因为列表中 BigInteger 值的数量可能很大(数千个值),我想利用 parallelStream() 来执行此操作。但是,我接受任何有效执行此操作的建议。

如果我正确理解了您的尝试,那么这就是 Streamreduce 方法的用途。

例如,以下应该有效:

final List<BigInteger> result = IntStream.range(0, a.size())
    .mapToObj(i -> collection.stream()
        .map(l -> l.get(i)) // Map to the i'th element of all streams.

        // This line is the trick.  It effectively performs '&' on all elements
        // in the mapped stream.  The first argument is the 'identity' element
        // of '&'-ing, which is a binary 11111...
        // -1 Behaves like this (although I have no idea how it works for
        // BigInteger, it seems to give the right values for the things you've
        // given as examples).
        .reduce(BigInteger.ZERO.subtract(BigInteger.ONE), BigInteger::and))
    .collect(Collectors.toList());

注意:如果您在这里寻求效率(这对您的用例确实很重要),那么请考虑您是否需要 BigInteger。忽略符号(因为你正在进行按位运算)long 数组会给你 64 位,很容易 涵盖你在示例中给出的所有内容(和常见用例中的大多数数字)并且会更快。

你可以试试这样的

    List<BigInteger> dd = IntStream.range(0, a.size())
      .mapToObj(i -> 
        collection.stream()
            .map(col -> col.get(i))
            .reduce(BigInteger::and)
            .get())
      .collect(Collectors.toList());

替代答案的灵感来自 Stanislav Bashkyrtsev's comment, which uses BitSets

public static void main(String[] args) {
    final List<BitSet> a = Arrays.asList(
            BitSet.valueOf(new long[] {0b1000L}),
            BitSet.valueOf(new long[] {0b101L}),
            BitSet.valueOf(new long[] {0b111L})
    );
    final List<BitSet> b = Arrays.asList(
            BitSet.valueOf(new long[] {0b10L}),
            BitSet.valueOf(new long[] {0b1_0110L}),
            BitSet.valueOf(new long[] {0b1_0011L})
    );
    final List<BitSet> c = Arrays.asList(
            BitSet.valueOf(new long[] {0b1L}),
            BitSet.valueOf(new long[] {0b1_0110L}),
            BitSet.valueOf(new long[] {0b1_1110L})
    );

    final List<List<BitSet>> bitSets = Arrays.asList(a, b, c);

    final List<BitSet> results = IntStream.range(0, a.size())
        .mapToObj(i -> bitSets.stream().map(l -> l.get(i))

            // First argument of reduce is identity element, it needs
            // to be a BitSet with at least as many bits as any input
            // ones which are all set.  In this case, 64 bits are
            // used, initialised by a long.  If you have /more/ bits
            // needed, then create one with new BitSet(bitCount);
            // and then set all bits.
            .reduce(BitSet.valueOf(new long[] {0xFFFF_FFFF_FFFF_FFFFL}), (_0, _1) -> {

                // Or used to copy the first element into a new
                // BitSet.
                // No need to copy  here if you don't mind
                // modifying the above Lists.
                final BitSet tmp = BitSet.valueOf(new long[] {});
                tmp.or(_0);

                tmp.and(_1);
                return tmp;
            }))
        .collect(Collectors.toList());

    results.forEach(bs -> System.out.println(convert(bs)));
}

public static long convert(final BitSet bits) {
    long value = 0L;
    for (int i = bits.nextSetBit(0); i >= 0; i = bits.nextSetBit(i + 1)) {
        value += bits.get(i) ? (1L << i) : 0L;
    }
    return value;
}

采用 convert 方法并从 this answer 稍作修改。