警告.这里涉及一些问题.
事实上,有两个问题混合在一起:
>如何以相反的顺序将数据从一个列表传输到另一个列表?
>如何从BigInteger创建数字列表?
我同意comment by Roman C:“从一个列表移到另一个列表是没用的”.至少,在这种情况下似乎没用.但是如果tempList发生了某些事情,并且从一个列表中删除元素并将它们添加到另一个列表(逐个)的一般方法以任何方式都是合理的,那么如何提高这个特定情况的性能的问题可能仍然可行.
关于如何以一个相反的顺序将数据从一个列表传输到另一个列表的核心问题:
令人惊讶的是,以现在写的形式,
……第二个片段远比第一个慢!
(以下说明)
像这样的简单测试比较两种方法. (当然,像这样的“micorbenchmarks”应该用一粒盐,但由于这里的性能与渐近运行时间有关,这在这里是合理的)
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Random;
public class BigIntegerDigitsPerformanceLists
{
public static void main(String[] args)
{
testListConversion();
}
private static void testListConversion()
{
long before = 0;
long after = 0;
for (int size = 10; size <= 1000000; size *= 10)
{
List inputA = createRandomList(size);
List inputB = createRandomList(size);
before = System.nanoTime();
List resultA = convertA(inputA);
after = System.nanoTime();
System.out.printf(Locale.ENGLISH,
"A: size %8d time %8.2fms result %d
",
size, (after-before)/1e6, resultA.get(0));
before = System.nanoTime();
List resultB = convertB(inputB);
after = System.nanoTime();
System.out.printf(Locale.ENGLISH,
"B: size %8d time %8.2fms result %d
",
size, (after-before)/1e6, resultB.get(0));
}
}
private static List createRandomList(int size)
{
List result = new ArrayList();
Random random = new Random(0);
for (int i=0; i
{
result.add(random.nextInt(10));
}
return result;
}
private static List convertA(List list)
{
List result = new ArrayList();
Collections.reverse(list);
while (!list.isEmpty())
{
result.add(list.get(0));
list.remove(0);
}
return result;
}
private static List convertB(List list)
{
List result = new ArrayList();
while (!list.isEmpty())
{
result.add(list.get(list.size() - 1));
list.remove(list.get(list.size() - 1));
}
return result;
}
}
我机器上的输出是
A: size 10 time 0.08ms result 4
B: size 10 time 0.05ms result 4
A: size 100 time 0.13ms result 1
B: size 100 time 0.39ms result 1
A: size 1000 time 1.27ms result 6
B: size 1000 time 2.96ms result 6
A: size 10000 time 39.72ms result 1
B: size 10000 time 220.82ms result 1
A: size 100000 time 3766.45ms result 7
B: size 100000 time 21734.66ms result 7
...
但….
这是由于错误的方法调用.第二种方法包含该行
list.remove(list.get(list.size() - 1));
这是这种情况的罪魁祸首:你有一个Integer对象列表.你正在调用remove,传入一个Integer对象.此方法将搜索整个列表,并删除第一次出现的参数.这不仅速度慢,而且还会导致结果明显错误!
你真正想要做的是使用最后一个元素的索引删除最后一个元素.所以改变这一行
list.remove((int)list.size() - 1);
给出完全不同的计时结果:
A: size 10 time 0.08ms result 4
B: size 10 time 0.03ms result 4
A: size 100 time 0.13ms result 1
B: size 100 time 0.10ms result 1
A: size 1000 time 1.28ms result 6
B: size 1000 time 0.46ms result 6
A: size 10000 time 39.09ms result 1
B: size 10000 time 2.63ms result 1
A: size 100000 time 3763.97ms result 7
B: size 100000 time 9.83ms result 7
...
那么,当正确实施时,那么
……第一个片段比第二个片段慢得多!
关于如何从BigInteger创建数字列表的问题:有几种可能的性能改进.
使用%= 10和/ = 10次调用的序列手动提取数字非常慢.避免模??运算已经带来了一个小的加速.而不是
digit = number % 10;
number = number / 10;
你能做到的
nextNumber = number / 10;
digit = number - (nextNumber * 10);
number = nextNumber;
但由于BigInteger的不变性和昂贵的划分,这仍然比简单地将BigInteger转换为字符串并从那里提取数字要慢几个数量级,如dasblinkenlight suggested in his answer所示.
一个简单的比较:
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Random;
public class BigIntegerDigitsPerformance
{
public static void main(String[] args)
{
testListCreation();
}
private static void testListCreation()
{
long before = 0;
long after = 0;
for (int size = 10; size <= 100000; size *= 10)
{
BigInteger number = createRandomBigInteger(size);
before = System.nanoTime();
List resultA = createA(number);
after = System.nanoTime();
System.out.printf(Locale.ENGLISH,
"A: size %8d time %8.2fms result %d
",
size, (after-before)/1e6, resultA.get(0));
before = System.nanoTime();
List resultB = createB(number);
after = System.nanoTime();
System.out.printf(Locale.ENGLISH,
"B: size %8d time %8.2fms result %d
",
size, (after-before)/1e6, resultB.get(0));
before = System.nanoTime();
List resultC = createC(number);
after = System.nanoTime();
System.out.printf(Locale.ENGLISH,
"B: size %8d time %8.2fms result %d
",
size, (after-before)/1e6, resultC.get(0));
}
}
private static BigInteger createRandomBigInteger(int size)
{
StringBuilder sb = new StringBuilder();
Random random = new Random(0);
for (int i=0; i
{
sb.append(String.valueOf(random.nextInt(10)));
}
return new BigInteger(sb.toString());
}
private static List createA(BigInteger number)
{
ArrayList list = new ArrayList();
while (number.compareTo(BigInteger.ZERO) == 1)
{
list.add((number.mod(BigInteger.TEN)).intValue());
number = number.divide(BigInteger.TEN);
}
return list;
}
private static List createB(BigInteger number)
{
ArrayList list = new ArrayList();
while (number.compareTo(BigInteger.ZERO) == 1)
{
BigInteger next = number.divide(BigInteger.TEN);
BigInteger diff = number.subtract(next.multiply(BigInteger.TEN));
list.add(diff.intValue());
number = next;
}
return list;
}
private static List createC(BigInteger number)
{
String s = number.toString();
ArrayList list = new ArrayList(s.length());
for (int i=s.length()-1; i>=0; i--)
{
list.add(s.charAt(i) - '0');
}
return list;
}
}
输出将是这样的:
...
A: size 1000 time 9.20ms result 6
B: size 1000 time 6.44ms result 6
C: size 1000 time 1.96ms result 6
A: size 10000 time 452.44ms result 1
B: size 10000 time 334.82ms result 1
C: size 10000 time 16.29ms result 1
A: size 100000 time 43876.93ms result 7
B: size 100000 time 32334.84ms result 7
C: size 100000 time 297.92ms result 7
表明toString方法比手动方法快一百多倍.