在文本操作和整数操作之间的Java核心中的性能问题

时间:2014-05-21 09:23:22

标签: java performance javac

我有PowerSet Util类来计算所有可能的set子集:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * Created by ngrigoriev on 5/19/14.
 */
public class PowerSet {
    /**
     * Returns the power set from the given set by using a binary counter ordered by size Example: S = {a,b,c} P(S) = {[], [c], [b], [b, c], [a],
     * [a, c], [a, b], [a, b, c]}
     * @param superSet - Typed Set {@see Set}
     * @return LinkedHashSet @{see LinkedHashSet} with all possible Sub sets of @param superSet
     */
    public static <E> List<Set<E>> getAllCombinationsOfSubsets(final Set<E> superSet) {
        final List<E> listSet = new ArrayList<>(superSet);
        // create the empty power set
        final List<Set<E>> power = new LinkedList<>();

        // get the number of elements in the set
        final int elements = listSet.size();

        // the number of members of a power set is 2^n
        final int powerElements = (int) Math.pow(2, elements +1) - 1;

        // run a binary counter for the number of power elements
        for (int i = 0; i <= powerElements; i++) {
            // create a new set
            final Set<E> innerSet = new HashSet<>();

            // convert each digit in the current binary number to the
            // corresponding element
            // in the given set
            int tmp = i;
            for (int j = 0; j < elements; j++) {
                if (tmp % 2 == 1) {
                    innerSet.add(listSet.get(j));
                }

                tmp = tmp >> 1;
                if (tmp <= 0) {
                    break;
                }
            }

            // add the new set to the power set
            power.add(innerSet);
        }

        return power;
    }

    public static <E> List<Set<E>> powerset(final Set<E> superSet) {
        final List<E> listSet = new ArrayList<>(superSet);
        // create the empty power set
        final List<Set<E>> power = new LinkedList<>();

        // get the number of elements in the set
        final int elements = listSet.size();

        // the number of members of a power set is 2^n
        final int powerElements = (int) Math.pow(2, elements);

        // run a binary counter for the number of power elements
        for (int i = 0; i < powerElements; i++) {

            // convert the binary number to a String containing n digits
            final String binary = intToBinary(i, elements);

            // create a new set
            final LinkedHashSet<E> innerSet = new LinkedHashSet<>();

            // convert each digit in the current binary number to the
            // corresponding element
            // in the given set
            for (int j = 0; j < binary.length(); j++) {
                if (binary.charAt(j) == '1') {
                    innerSet.add(listSet.get(j));
                }
            }

            // add the new set to the power set
            power.add(innerSet);

        }

        return power;
    }


    /**
     * Converts the given integer to a String representing a binary number with
     * the specified number of digits For example when using 4 digits the binary
     * 1 is 0001
     *
     * @param binary
     * int
     * @param digits
     * int
     * @return String
     */
    private static String intToBinary(final int binary, final int digits) {

        final String temp = Integer.toBinaryString(binary);
        final int foundDigits = temp.length();
        String returner = temp;
        for (int i = foundDigits; i < digits; i++) {
            returner = "0" + returner;
        }

        return returner;
    }
}

这是一个UnitTest

import java.util.HashSet;
import java.util.Set;

import junitparams.JUnitParamsRunner;

import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Created by ngrigoriev on 5/19/14.
 */
@RunWith(JUnitParamsRunner.class)
public class TestPowerSet {

    public static final int CONST = 1000;

    @Test
    public void testOnInteger() {
        final Set<Integer> set = new HashSet<>();
        for (int i = 0; i < 10; i++) {
            set.add(i);
        }
        final long time = System.currentTimeMillis();

        for (int lll = 1; lll < CONST; lll++) {
            PowerSet.getAllCombinationsOfSubsets(set);
        }

        System.out.println("Time: " + (System.currentTimeMillis() - time));
        System.out.println("total  " + Runtime.getRuntime().totalMemory() / 1024);

        final long time2 = System.currentTimeMillis();

        for (int lll = 1; lll < CONST; lll++) {
            PowerSet.powerset(set);
        }

        System.out.println("Time: " + (System.currentTimeMillis() - time2));
        System.out.println("total  " + Runtime.getRuntime().totalMemory() / 1024);
    }
}

问题为什么powerset方法工作得更快。有时候占用的内存较少??? 我已经尝试了查看byteCode,但我没有发现很大的区别。可能有人可以解释我发生了什么。

1 个答案:

答案 0 :(得分:3)

这里的原因很简单:较慢的方法是错误。它返回一个包含两倍于更快的元素的列表。

例如,运行

public class TestPowerSet2
{
    public static void main(String[] args)
    {
        Set<Integer> set = new LinkedHashSet<Integer>(Arrays.asList(0,1,2));

        List<Set<Integer>> resultA = PowerSet.getAllCombinationsOfSubsets(set);
        System.out.println("Result A:");
        for (Set<Integer> s : resultA)
        {
            System.out.println("    "+s);
        }

        List<Set<Integer>> resultB = PowerSet.powerset(set);
        System.out.println("Result B:");
        for (Set<Integer> s : resultB)
        {
            System.out.println("    "+s);
        }
    }
}

getAllCombinationsOfSubsets中元素数量的计算和循环应与powerset方法中的元素数量相同,即

// the number of members of a power set is 2^n
final int powerElements = (int) Math.pow(2, elements);

// run a binary counter for the number of power elements
for (int i = 0; i < powerElements; i++) {

然后,第一种方法更快(如预期的那样)。

顺便说一句:2的幂也可以用位移计算:

final int powerElements = 1 << elements;