java面试相关问题
Easul Lv6

获取一个字符串中,任意子串出现次数。

  • 准备
    • 使用substring()获取每一次判断完剩下的字符串(会创建一堆字符串对象)
    • 使用indexOf()从查询到后边的位置继续查询
    • 使用子串进行切割(需要判断首尾有没有该字串,因为首尾不会切割出空串)
  • 思路
    • 用大圈套小圈,外层的大圈循环取几个字符(所有字符可以不取),内层的小圈每次取一个子串
    • 用indexOf从开始找是否有匹配(-1则没有),有匹配的话则从匹配的位置往后数所取字符个数个位置继续找,直到到最后一个位置或-1
  • 代码
    JAVA
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    String originStr = "asdffdassdf";
    for (int subStrLength = 1; subStrLength <= originStr.length(); subStrLength++) {
    for (int subStrIndex = 0; subStrIndex + subStrLength <= originStr.length(); subStrIndex++) {
    String subStr = originStr.substring(subStrIndex, subStrIndex + subStrLength);
    int times = 0;
    int index = 0;
    while ((index = originStr.indexOf(subStr, index)) != -1) {
    times++;
    index += subStrLength;
    if (index >= originStr.length()) {
    break;
    }
    }
    System.out.println(subStr + "出现的次数为" + times + "次");
    }
    }

获取两个字符串中最大相同的子串

  • 思路
    • 找到短的那个字符串
    • 从短字符串中依次获取子串,查看是否包含在另一个字符串中(子串从长取到短)
  • 代码
    JAVA
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    String originStr_1 = "asdffdassdf";
    String originStr_2 = "asfdsafd";
    String shortStr = originStr_1.length() < originStr_2.length() ? originStr_1 : originStr_2;
    String longStr = originStr_1.length() > originStr_2.length() ? originStr_1 : originStr_2;
    Set<String> resultSet = new HashSet<>();

    for (int subStrLength = 1; subStrLength <= shortStr.length(); subStrLength++) {
    for (int subStrIndex = 0; subStrIndex + subStrLength <= shortStr.length(); subStrIndex++) {
    String subStr = shortStr.substring(subStrIndex, subStrIndex + subStrLength);
    if (longStr.contains(subStr)) {
    resultSet.add(subStr);
    }
    }
    }

    int maxLength = 0;
    for (String result: resultSet) {
    if (result.length() > maxLength) {
    maxLength = result.length();
    }
    }

    for (String result: resultSet) {
    if (result.length() == maxLength) {
    System.out.println(result);
    }
    }

包装类的题

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// jdk1.5后,自动装箱,如果装箱的是一个字节,该数据会被共享,不会新开辟空间
// 1. 值直接从缓冲池中取
Integer a = 127;
Integer b = 127;
// true,指向了同一个对象
System.out.println(a == b);
// true,值相等
System.out.println(a.equals(b));
// 2. 不在缓冲池中的对象
Integer a = 128;
Integer b = 128;
// false,两个对象
System.out.println(a == b);
// true,值相等
System.out.println(a.equals(b));
// 3. 直接创建两个对象
Integer a = new Integer(3);
Integer b = new Integer(3);
// false,两个对象
System.out.println(a == b);
// true,值相等
System.out.println(a.equals(b));

Integer对象是在Integer.IntegerCache创建的,-128到127的值已被缓存,新的Integer对象在此区间会被复用,不在此区间会在堆产生新对象。128不在此区间,所以产生了两个对象,hashCode值不同,所以需要使用intValue()来取到真实的值。Long对象也是如此。
Integer.valueOf会复用值
参考链接

使用LinkedList模拟堆栈(FILO)或者队列(FIFO)数据结构

需要自己定义一个容器,用LinkedList作为底层来描述

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class Test {
public static void main(String[] args) {
StackDemo n = new StackDemo();
n.add(new Person(1 ,"E"));
n.add(new Student());
n.get();
QueueDemo n2 = new QueueDemo();
n2.add(new Person(1 ,"E"));
n2.add(new Student());
n2.get();
}
}
class StackDemo {
private LinkedList<Object> stack;

public StackDemo() {
stack = new LinkedList<>();
}

public void add(Object elem) {
stack.addFirst(elem);
}

public Object get() {
// 使用remove的方法相当于移除并返回这个,然后下一个排到第一个
return stack.removeFirst();
}

public boolean isNull() {
return stack.isEmpty();
}
}

class QueueDemo {
private LinkedList<Object> queue;

public QueueDemo() {
queue = new LinkedList<>();
}

public void add(Object elem) {
queue.addFirst(elem);
}

public Object get() {
// 使用remove的方法相当于移除并返回这个,然后下一个排到第一个
return queue.removeLast();
}

public boolean isNull() {
return queue.isEmpty();
}
}

TreeSet传入字符串,以其长度排序

使用比较器进行排序,毕竟String重写他的compareTo方法不方便

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
class StringComparator implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
int length = o1.length() - o2.length();
return length == 0 ? o1.compareTo(o2) : length;
}
}

class Test {
public static void main(String[] args) {
TreeSet<String> str = new TreeSet<>(new StringComparator());
}
}

获取字符串中,每一个字母出现的次数,要求打印结果是a(2)b(1)

  • 思考
    • 这里一个是字母,一个是次数,相当于字母与次数有映射关系。且关系数很多
    • 当前容器中数组和Map集合有映射关系,可以做这件事。数组用于映射关系的一方为有序编号,Map用于映射关系的两方都没有有序编号
    • 使用Map集合发现,保证唯一性的一方具备顺序,因而使用TreeMap集合
  • 代码
    JAVA
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public static void main(String[] args) {
    String tempStr = "fdgav123cbsacdfs";
    String s = getCharsCount(tempStr);
    System.out.println(s);
    }

    private String getCharsCount(String tempStr) {
    Map<Character, Integer> charMap = new TreeMap<>();
    for (char ch : tempStr.toCharArray()) {
    if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z') {
    Integer tempCount = charMap.get(ch);
    int count = 1;
    if (tempCount != null) {
    count += tempCount;
    }
    charMap.put(ch, count);
    }
    }

    return map2String(charMap);
    }

    private String map2String(Map<Character, Integer> charMap) {
    StringBuilder sb = new StringBuilder();
    Iterator<Map.Entry<Character, Integer>> charIter = charMap.entrySet().iterator();
    while (charIter.hasNext()) {
    Map.Entry<Character, Integer> charEntry = charIter.next();
    sb.append(charEntry.getKey() + "(" + charEntry.getValue() + ")");
    }
    return sb.toString();
    }

将数据插入到有序数据中仍有序

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
给定一个有序数组,想要给该数组中存储一个元素,还要保证该数组有序.那么如何找到这个元素应该插入的下标
可以使用折半查找,因为有序
*/
public static int halfIndex(int[] arr, int number){
int max = arr.length - 1;
int min = 0;
while(min <= max){
int mid = (max + min)>>1;
if(arr[mid] > number){
max = mid - 1;
}
else if(arr[mid] < number){
min = mid + 1;
}
else{
return mid;
}
}
// 根据判断发现.无论插到最左边,还是最右边,或者中间,min的位置,都可以用来当作插入的位置.
return min;
}

获取一个整数的十六进制

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/**
需求:获取一个整数的十六进制(取8进制与7,左移3位 取2进制与1,左移1位)
16进制表示 :0xABC
8进制表示 :07524
2进制表示 :0b1101
思路:整数与15相与,获得四位二进制位,然后转换这个数到16进制,然后整数向右移4位.
当整数为0时,结束循环
*/
// 第一种:用unicode转码,字符串存结果
public static void toHex(int number){
int temp;
String result = "";
do {
temp = number & 15;
if(temp > 9)
result = (char)(temp-10+'A') + result;
else
result = temp + result;
number = number>>4;
} while (number != 0);
System.out.println(result);
}
// 第二种:用查表法,数组存结果,需要先知道结果长度.
public static void toHex(int number){
// 查表法
char[] chs = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
char[] result = new char[8];
int pos = 0;// 索引下标
do {
// 也可以索引最开始最大,然后从右向左存
result[pos++] = chs[number & 15];
number = number >> 4;
} while (number != 0);//用do while的好处还可以判断第一次输入0的情况
// 最后的pos位没有使用
for (int i = pos-1; i >= 0; i--) {
System.out.print(result[i]);
}
System.out.println();
}

// 综合:转成指定进制,base表示与的数,offset表示偏移量
// 十进制 decimal 十六进制 hex 八进制 octal 二进制 binary
public static String toBinary(int number){
return trans(number, 1, 1);
}
public static String trans(int number, int base, int offset){
// 查表法
char[] chs = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
String result="";
do {
result = chs[number & base] + result;
number = number >> offset;
} while (number != 0);
return result;
}

// 如果这里有负数,可以乘-1然后再算,也可以限定运算的次数,避免无限运算.
public static void toHex(int number, int base, int offset){
// 查表法
char[] chs = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
String result = "";
int index;
if (number < 0) {
index = 0;
} else {
index = -1;
}
do {
if (index != -1) {
if (index <= 32) {
index++;
} else {
break;
}
}
result = chs[number & base] + result;
number = number >> offset;
} while (number != 0);
System.out.println(result);
}

匿名内部类

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
匿名内部类是否赋值给父类对象的区别
不赋值给父类,相当于调用子类对象自己的方法
赋值给父类,就只能调用父类中有的方法,不能调用子类特有方法
*/
class Test{
public static void main(String[] args){
// 编译出错,因为Object中没有show()方法
new Outer().method();
// 编译运行成功,因为用的是子类对象来访问方法
new Outer1().method();
}
}
class Outer{
void method(){
Object obj = new Object(){
public void show(){
System.out.println(1);
}
}
// 编译出错,因为Object中没有show()方法
// 父类调用没有的方法会报错
obj.show();
}
}
class Outer1{
void method(){
// 编译运行成功,因为用的是子类对象来访问方法
// 子类创建了自己的方法,并由子类自己调用,故没问题
new Object(){
public void show(){
System.out.println(1);
}
}.show();
}
}

键盘数据操作

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 获取用户键盘录入数据,并转为大写显示在控制台。用户输入over,结束键盘录入。

// 键盘每次只能录入一个字节,可以把用户输入回车前的数据进行存储
// 检测有回车了,判断存储的数据是否是over来进行输出或退出。容器可以使用StringBuilder。

StringBuilder sb = new StringBuilder();
// 使用了字节流,读入中文字符会有问题。
InputStream is = System.in;
int ch = 0;

while((ch = is.read()) != -1) {
if (ch == '\r')
continue;
if (ch == '\n') {
if ("over".equals(sb.toString())) {
break;
} else {
System.out.println(sb.toString().toUpperCase());
sb.delete(0, sb.length());
}
} else {
sb.append((char)ch);
}
}

字符串截取指定长度字节

截取字符串指定长度字节,转为新字符串
GB2312,中文两个字节表示,都是开头为1,都是负数
GBK,因为字太多,中文两个字节中第一个为负数,可能第二个字节是正数。
例如(琲:bei)。但用这个程序判断一定没问题,只要最后有异常字符,就抛弃掉

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public void cutTest() throws UnsupportedEncodingException {
String str = "ab你好cd谢谢";
int len = str.getBytes("GBK").length;
for (int i = 0; i < len; i++) {
System.out.println(cutString(str, i + 1));
}

}

/**
* str 传入字符串
* len 取得的字节数
*/
public String cutString(String str, int len) throws UnsupportedEncodingException {
// 将字符串转为字节数组
byte[] buf = str.getBytes("GBK");
// 记录字节中负数的个数
int count = 0;
// 只会最后一个字符出现问题
// 如果bbb琲b,最后一个字符是正的,正常输出
// 如果bbb琲,读取完整 琲 的时候,最后一个字符是正的,正常输出
// 如果只读取前一个字节,count为1,如果前边还有 琲 ,读到正的,退出,抛弃最后一个字节即可
// 否则一定是两个负的,count始终为奇数,所以最后一定会去掉最后一个字节,保证正确
for (int i = len - 1; i >= 0; i--) {
if (buf[i] < 0) {
count++;
} else {
break;
}
}
if (count % 2 == 0) {
return new String(buf, 0, len, "GBK");
} else {
return new String(buf, 0, len - 1, "GBK");
}
}

字符串截取指定长度字节2

截取字符串指定长度字节,转为新字符串
使用UTF-8编码,错误字符不输出
UTF-8中,中文都是三个字节,每个字节都是负数
英文都是正数,所以没有问题,前边的中文肯定是三个字节,没有问题
只有最后一个字,如果字节不够会出问题

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public void cutTest() throws UnsupportedEncodingException {
String str = "ab你好cd谢谢";
int len = str.getBytes("UTF-8").length;
for (int i = 0; i < len; i++) {
System.out.println(cutString(str, i + 1));
}

}

/**
* str 传入字符串
* len 取得的字节数
*/
public String cutString(String str, int len) throws UnsupportedEncodingException {
// 将字符串转为字节数组
byte[] buf = str.getBytes("UTF-8");
// 记录字节中负数的个数
int count = 0;
for (int i = len - 1; i >= 0; i--) {
if (buf[i] < 0) {
count++;
} else {
break;
}
}
// UTF-8中文三个字节,满三个负数则输出,否则不输出。
if (count % 3 == 0) {
return new String(buf, 0, len, "UTF-8");
} else if (count % 3 == 1) {
return new String(buf, 0, len - 1, "UTF-8");
} else {
return new String(buf, 0, len - 2, "UTF-8");
}

}

正则处理

QQ号校验

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 需求:对QQ号校验
* 要求:长度为5-10位,0不能开头,只能是数字
* 思路:
* 1 先判断长度,然后判断字符串是不是0开头,然后打散字符串,判断每一位是不是数字(这里也可以将字符串转为long型,用异常判断)
* 2 正则
*/
String qq = "123456789";
// 正则底层对应了代码
// 大括号控制次数,中括号放数据范围
String regex = "[1-9][0-9]{4,9}";
System.out.println(qq.matches(regex));

治疗口吃

JAVA
1
2
3
4
5
6
7
8
9
// 治疗口吃
String str = "我我我...我我我我我我我要...要要要要要要要...要要要要要要要......学学学学学学学学..学学编编编编编....编编编编编编编编...编...程程程程程....程程程程程...程程程程程程..";
// 1 替换掉点
String regex = "\\.+";
str = str.replaceAll(regex, "");
// 2 替换掉叠词
regex = "(.)\\1+";
str = str.replaceAll(regex, "$1");
System.out.println(str);

对IP地址排序

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 因为是字符串排序,所以正常排序和字符串排序结果不同.所以可以想到每个都补成3位,然后按照字符串顺序排序.
String ipStr = "192.168.10.34 127.0.0.1 3.3.3.3 105.70.11.55";

// 每一段都加两个0
String regex = "(\\d+)";
ipStr = ipStr.replaceAll(regex, "00$1");
// 每一段都保留三位数字
regex = "0*(\\d{3})";
ipStr = ipStr.replaceAll(regex, "$1");

regex = "\\s+";
String[] ipArr = ipStr.split(regex);

TreeSet<String> ts = new TreeSet<String>();
for (String ip : ipArr) {
ts.add(ip);
}

for (String ip : ts) {
// 输出的时候,因为有0,至少要保留一位数字,所以用了\d+
System.out.println(ip.replaceAll("0*(\\d+)", "$1"));
}

对邮件地址校验

JAVA
1
2
3
4
5
6
// 可以用于关键字屏蔽.
String mail = "asdf@qq.com.cn";
// 在这里如果只有一个组,可以不写组号
String regex = "[\\w]+@[\\w-&&[^_]]+(\\.[a-zA-Z]{2,3}){1,3}";
boolean b = mail.matches(regex);
System.out.println(b);

爬虫

获取互联网中符合指定规则的数据.百度早期爬keywords,现在爬内容

JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 爬取邮箱地址
// 读取源文件,本地文件
BufferedReader br = new BufferedReader(new FileReader("mail.html"));
// 爬取网络内容
// URL url = new URL("网络地址");
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
// 对读取的数据进行规则的匹配.从中获取符合规则的数据
String mailRegex = "\\w+@\\w+(\\.\\w+){1,2}";
List<String> list = new ArrayList<String>();
Pattern p = Pattern.compile(mailRegex);
String line = null;
while ((line = br.readLine()) != null) {
Matcher m = p.matcher(line);
while (m.find()) {
// 将符合规则的数据存储到集合
list.add(m.group());
}
}

for (String mail : list) {
System.out.println(mail);
}
 评论