是这样的,最近准备跳槽啦,开始面试了,遇到这样一个问题:给你一个无穷字符串类型的 IP ,IP 之间以
,
分割,类似这样String ipStr = "192.168.10.222,192.168.10.43,192.168.10.243"
,现在需要你写一个方法,入参为ipStr
,出参为ipStr
的最后一个 Ip。 我比较蠢。。。。当面试官问我的时候,我想到的解决方案就是遍历这个数组,然后存入一个集合,根据索引来定位最后一个IP
,但是面试官讲我这个有性能问题,我自己尝试了一遍,在一千万以内的数据量内,感觉是不存在性能问题的🤣🤣🤣,但是一亿条数据后直接堆溢出了,丢。。。。下面附代码,请多吐槽,让我意识到自己的不足。
package com.mobaijun.streamtest;
import org.springframework.util.StopWatch;
import java.util.ArrayList;
/**
* Software:IntelliJ IDEA 2021.2 x64
* Author: https://www.mobaijun.com
* Date: 2022/2/21 16:48
* ClassName:IpStr
* 类描述:
*/
public class IpStr {
/**
* 计时器
*/
private static StopWatch stopWatch = new StopWatch();
public static void main(String[] args) {
StringBuilder str = new StringBuilder();
// N
for (int i = 1; i <= 10000000; i++) {
str.append(i)
.append("." + i)
.append("." + i)
.append("." + i)
.append(",");
}
stopWatch.start();
String s = ipStr(String.valueOf(str));
stopWatch.stop();
System.out.println("运算耗时为:" + stopWatch.prettyPrint());
System.out.println("计算值为: = " + s);
}
public static String ipStr(String str) {
String[] ipList = str.split(",");
ArrayList<String> arrayList = new ArrayList<>();
for (String s : ipList) {
arrayList.add(s);
}
str = arrayList.get(arrayList.size() - 1);
return str;
}
}
运算耗时为:StopWatch '': running time = 5084789100 ns
---------------------------------------------
ns % Task name
---------------------------------------------
5084789100 100%
计算值为: = 10000000.10000000.10000000.10000000
Exception in thread "main" java.lang.OutOfMemoryError
at java.lang.AbstractStringBuilder.hugeCapacity(AbstractStringBuilder.java:161)
at java.lang.AbstractStringBuilder.newCapacity(AbstractStringBuilder.java:155)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:125)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at com.mobaijun.streamtest.IpStr.main(IpStr.java:27)
在经过几个群的讨论后,我换了方式来实现这道题,一种是反转 list,一样影响效率,一种是直接倒叙遍历。
public class IpStr2 {
/**
* 计时器
*/
private static StopWatch stopWatch = new StopWatch();
public static void main(String[] args) {
StringBuilder str = new StringBuilder();
// N
for (int i = 1; i <= 10000000; i++) {
str.append(i)
.append("." + i)
.append("." + i)
.append("." + i)
.append(",");
}
stopWatch.start();
String s = ipStr(String.valueOf(str));
stopWatch.stop();
System.out.println("运算耗时为:" + stopWatch.prettyPrint());
System.out.println("计算值为: = " + s);
}
public static String ipStr(String str) {
ArrayList<String> list = new ArrayList<>();
String[] split = str.split(",");
for (String s : split) {
list.add(s);
}
// 反转list
Collections.reverse(list);
return list.get(0);
}
}
运算耗时为:StopWatch '': running time = 2156518100 ns
---------------------------------------------
ns % Task name
---------------------------------------------
2156518100 100%
计算值为: = 10000000.10000000.10000000.10000000
这个结果也不是很理想,只要存入 list 必然有性能问题。
public class IpStr3 {
/**
* 计时器
*/
private static StopWatch stopWatch = new StopWatch();
public static void main(String[] args) {
StringBuilder str = new StringBuilder();
// N
for (int i = 1; i <= 10000000; i++) {
if (i == 10000000) {
str.append(i)
.append("." + i)
.append("." + i)
.append("." + i);
} else {
str.append(i)
.append("." + i)
.append("." + i)
.append("." + i)
.append(",");
}
}
stopWatch.start();
String s = ipStr(String.valueOf(str));
stopWatch.stop();
System.out.println("运算耗时为:" + stopWatch.prettyPrint());
System.out.println("计算值为: = " + s);
}
public static String ipStr(String str) {
int one = str.lastIndexOf(',');
str = str.substring(one + 1);
return str;
}
}
运算耗时为:StopWatch '': running time = 219355000
--------------------------------------------
ns % Task name
--------------------------------------------
219355000 100%
计算值为: = 10000000.10000000.10000000.10000000
这种方式也无法处理亿级数据量,但是同样千万数据已经把时间压缩到 2 秒左右。 目前只有倒序遍历没有测试过,这块有点头大,各位小伙伴有其他实现方案请在下方留言区进行讨论,欢迎指教。后续倒序搞定了,我会附思路和源码。