手写实现java栈结构,并实现简易的计算器(基于后缀算法)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介:

手写实现java栈结构,并实现简易的计算器(基于后缀算法)

一、定义
栈是一种线性表结构,栈结构中有两端,对栈的操作都是对栈的一端进行操作的,那么被操作的一端称为栈顶,另一端则为栈底。对栈的操作其实就是只有两种,分别是入栈(也称为压栈)和出栈(也称为弹栈)。入栈,将新元素压入栈中,那么此时这个栈元素就成为了栈顶元素,栈深度相应的+1。出栈,将栈中的栈顶元素弹出来,此时栈顶的下一个元素就会成为新的栈顶元素,栈深度也相应的-1。根据入栈和出栈的规则,也可以得到栈数据的顺序是后进先出(LIFO,LAST IN FIRST OUT)的特性。栈结构的效率是非常高的,因此无论栈中数据的量有多大,操作的也只有栈顶元素一个数据,因此栈的时间复杂度为O(1),这里不考虑栈溢出扩容的问题。栈结构示意图下图:

  二、栈的应用场景
栈的数据接口在编程中的应用是非常广泛的,其中包括:

1、浏览器页面的浏览记录。我们在浏览器中浏览的页面的时候后退和前进能够正确的跳转到对应的页面,就是利用了栈的特性来实现的。

2、java虚拟机中栈的操作数栈也是利用栈实现的。java在编译的时候讲操作的代码压入操作数栈中,进行运算时就将栈顶元素弹出来,然后进行运算后将结果再压进操作数栈中。

3、计算器的实现。我们在计算的时候一般都是从左往右计算的,但是这种方式对于计算机是非常不友好的,它需要进行大量的判断才可以。但是利用栈以及一些算法就能轻松实现计算的功能。我们下面的例子也将会利用栈结构来实现简易的计算器的功能。

  三、入栈和出栈
栈结构可以使用数组结构和链表结构来实现,因此不同的实现方式,入栈和出栈的实现方式也会有所区别。基于数组实现的栈的入栈和出栈操作实质是在内部维护了一个指针,这个指针指向的元素即为栈顶元素,入栈时讲指针向上移动一位,相反地则向下移动一位。基于链表的栈就没有了指针的概念。链表使用单向链表即可实现。链表的出栈和入栈实质上维护的链表头部元素。下面使用示意图来简单的演示一下两种结构的入栈和出栈的流程:

  1、基于数组的入栈和出栈:

   2.链表的入栈和出栈

  四、代码实现
  1.数组
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
/**
 * 基于数组实现的栈
 */
public class ArrayStack implements Stack {

    private Object[] data;
    private int index = -1;
    private int deep;
    private static final int DEFAULT_CAPACITY = 1 << 4;

    public ArrayStack() {
        deep = DEFAULT_CAPACITY;
        data = new Object[deep];
    }

    public ArrayStack(int capacity) {
        if (capacity <= 0) {
            capacity = DEFAULT_CAPACITY;
        }
        deep = capacity;
        data = new Object[deep];
    }

    /**
     * 添加数据,数组满了就不添加
     * @param e 入栈元素
     * @return
     */
    @Override
    public E push(E e) {
        if (isFull()) {
            System.out.println("栈已满");
            return null;
        }
        data[++index] = e;
        return e;
    }

    /**
     * 弹出元素
     * @return 栈顶元素
     */
    @Override
    public E pop() {
        if (isEmpty()) {
            System.out.println("栈为空");
            return null;
        }
        return (E) data[index--];
    }

    /**
     * 查看栈顶元素
     * @return
     */
    @Override
    public E peek() {
        if (isEmpty()) {
            System.out.println("栈为空");
            return null;
        }
        return (E) data[index];
    }

    /**
     * 栈深度
     * @return
     */
    @Override
    public int size() {
        return index + 1;
    }

    private boolean isEmpty() {
        return index <= -1;
    }

    private boolean isFull() {
        return deep == index + 1;
    }
  2.链表
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
/**
 * 基于链表实现的栈
 * @param
 */
public class LinkStack implements Stack {

    private Node head;
    private int size;

    @Override
    public E push(E e) {
        Node node = new Node<>(e);
        if (head == null) {
            head = node;
        }else {
            Node n = head;
            head = node;
            head.next = n;
        }
        size++;
        return e;
    }

    @Override
    public E pop() {
        if (head == null) {
            return null;
        }
        E data = head.data;
        head = head.next;
        size--;
        return data;
    }

    @Override
    public E peek() {
        return head == null ? null : head.data;
    }

    @Override
    public int size() {
        return size;
    }

    private static class Node {
        E data;
        Node next;

        public Node(E e) {
            data = e;
        }
    }
}

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
/**
 * 栈结构
 */
public interface Stack {

    /**
     * 入栈
     * @param e 入栈元素
     * @return 入栈元素
     */
    E push(E e);

    /**
     * 将栈顶元素弹出
     * @return 栈顶元素
     */
    E pop();

    /**
     * 查看栈顶元素,该方法不会弹出栈顶元素
     * @return 栈顶元素
     */
    E peek();

    /**
     * 查看栈深度
     * @return 栈深度
     */
    int size();
}

  五、应用实例:简易计算器
在进入写代码之前需要知道的前置知识是:前缀表达式(也叫波兰表达式),中缀表达式和后缀表达式(也叫逆波兰表达式)。

前缀、中缀、后缀表达式是对表达式的不同记法,其区别在于运算符相对于操作数的位置不同,前缀表达式的运算符位于操作数之前,中缀和后缀同理

举例:
中缀表达式:1 + (2 + 3) × 4 - 5
前缀表达式:- + 1 × + 2 3 4 5
后缀表达式:1 2 3 + 4 × + 5 -

中缀表达式 中缀表达式是一种通用的算术或逻辑公式表示方法,操作符以中缀形式处于操作数的中间。中缀表达式是人们常用的算术表示方法。 虽然人的大脑很容易理解与分析中缀表达式,但对计算机来说中缀表达式却是很复杂的,因此计算表达式的值时,通常需要先将中缀表达式转换为前缀或后缀表达式,然后再进行求值。对计算机来说,计算前缀或后缀表达式的值非常简单。

我下面讲解的例子中是利用后缀表达式的算法来实现的,因此,代码中回涉及到 运算字符串转中缀表达式,中缀表达式转后缀表达式的过程。

  后缀表达式步骤
从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 和 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果。

例如: (3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 - , 针对后缀表达式求值步骤如下:

1.从左至右扫描,将3和4压入堆栈;
2.遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
3.将5入栈;
4.接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
5.将6入栈;
6.最后是-运算符,计算出35-6的值,即29,由此得出最终结果

根据上面的步骤,我们可以使用代码来实现:

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
/**
     * 根据后缀表达式计算值
     * @param items 后缀表达式
     * @return 计算结果
     */
    public int calculate(List items) {
        /**
         * 用于保存过程变量和操作符等
         */
        Stack stack = new LinkStack<>();
        //便利
        for (String item : items) {
            //如果为数字,直接放入栈中
            if (item.matches("\d+")) {
                stack.push(item);
            }else {
                //弹出栈顶元素进行运算
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res;
                switch (item) {
                    case "+" :
                        res = num1 + num2;
                        break;
                    case "-" :
                        res = num1 - num2;
                        break;
                    case "*" :
                        res = num1 * num2;
                        break;
                    case "/" :
                        res = num1 / num2;
                        break;
                    default:
                        throw new RuntimeException("非法的运算符:" + item);
                }
                //运算完将结果压入栈中
                stack.push("" + res);
            }
        }
        //整个表达式扫描结束后,此时栈中只剩一个元素,该元素即为结算结果,从栈中弹出即可
        return Integer.parseInt(stack.pop());
    }
测试结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {
        Calculator calculator = new Calculator();
        List items = new ArrayList<>();
        items.add("3");
        items.add("4");
        items.add("+");
        items.add("5");
        items.add("*");
        items.add("6");
        items.add("-");
        System.out.println(calculator.calculate(items));
    }

//结果:29
虽然后面可以计算出表达式的最终结果,但是的实际的应用中计算的表达式往往都是按照我们的计算习惯来书写的(即中缀表达式,如(3+4)×5-6)。因此,想要正确的得到结果我们需要再多一个步骤,就是人们习惯的计算方式的中缀表达式转换成对计算机友好的后缀表达式。具体步骤如下:

1)初始化两个栈:运算符栈s1和储存中间结果的栈s2;
2)从左至右扫描中缀表达式;
3)遇到操作数时,将其压s2;
4)遇到运算符时,比较其与s1栈顶运算符的优先级:
4.1)如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
4.2)否则,若优先级比栈顶运算符的高,也将运算符压入s1;
4.3)否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较;

5)遇到括号时:
5.1) 如果是左括号“(”,则直接压入s1
5.2) 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
6)重复步骤2至5,直到表达式的最右边
7)将s1中剩余的运算符依次弹出并压入s2
8)依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

具体代码如下:

  (一)、解析算式
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
/**
     * 将表达式解析成List
     * @param expression 表达式
     * @return
     */
    private List parseExpr(String expression) {
        char[] chars = expression.toCharArray();
        int len = chars.length;
        List items = new ArrayList<>(len);
        int index = 0;
        while (index < len) {
            char c = chars[index];
            //数字
            if (c >= 48 && c <= 57) {
                String tmp = "";
                //操作数大于一位数,主要是通过判断下一位是否为数字
                while (index < chars.length && chars[index] >= 48 && chars[index] <= 57) {
                    tmp += chars[index];
                    index++;
                }
                items.add(tmp);
            }else {
                items.add(c + "");
                index++;
            }
        }
        return items;
    }
  (二)、获取运算符的优先级
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
     * 获取运算符的优先级,数字越大优先级越大
     * @param operateChar 运算符
     * @return 优先级
     */
    private int priority(String operateChar) {
        if ("*".equals(operateChar) || "/".equals(operateChar)) {
            return 2;
        }else if ("+".equals(operateChar) || "-".equals(operateChar)) {
            return 1;
        }else {
            //throw new RuntimeException("非法的操作符:" + operateChar);
            return 0;
        }
    }

  (三)、中缀转后缀表达式
/**

  • 1)初始化两个栈:运算符栈operateStack和储存中间结果的栈tmp;
  • 2)从左至右扫描中缀表达式;
  • 3)遇到操作数时,将其压tmp;
  • 4)遇到运算符时,比较其与operateStack栈顶运算符的优先级:
  •   4.1)如果operateStack为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
  •   4.2)否则,若优先级比栈顶运算符的高,也将运算符压入operateStack;
  •   4.3)否则,将operateStack栈顶的运算符弹出并压入到tmp中,再次转到(4-1)与operateStack中新的栈顶运算符相比较;
  • 5)遇到括号时:
  •   5.1) 如果是左括号“(”,则直接压入operateStack
  •   5.2) 如果是右括号“)”,则依次弹出operateStack栈顶的运算符,并压入tmp,直到遇到左括号为止,此时将这一对括号丢弃
  • 6)重复步骤2至5,直到表达式的最右边
  • 7)将operateStack中剩余的运算符依次弹出并压入tmp
  • 8)依次弹出tmp中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
  • @param expr 中缀表达式
  • @return 后缀表达式
    */

public List midfixToSuffix(String expr) {

/**
 * 将表达式的操作数和运算符转换成集合
 */
List<String> items = parseExpr(expr);
//操作数栈
Stack<String> operateStack = new LinkStack<>();
//临时变量的保存集合,这里使用了List集合
//如果用栈也可以实现,但是需要在最后将弹出栈元素的逆序进行运算
//所以使用List集合避免了这个转换的问题
List<String> tmp = new ArrayList<>();
//操作的位置
int index = 0;
//表达式长度
int len = items.size();
while (index < len) {
    String item = items.get(index);
    //遇到操作数时,将其压tmp;
    if (item.matches("\\d+")) {
        tmp.add(item);
    }else if (item.equals("(")) {//如果是左括号“(”,则直接压入operateStack
        operateStack.push(item);
    } else if (item.equals(")")) {//如果是右括号“)”,则依次弹出operateStack栈顶的运算符,并压入tmp,直到遇到左括号为止,此时将这一对括号丢弃
        while (!operateStack.peek().equals("(")) {
            tmp.add(operateStack.pop());
        }
        //直接弹出栈顶元素即可
        operateStack.pop();
    } else {//遇到运算符时,比较其与operateStack栈顶运算符的优先级
        //如果operateStack为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
        if (operateStack.isEmpty() || "(".equals(operateStack.peek())) {
            operateStack.push(item);
        }else if (priority(item) > priority(operateStack.peek())) {//否则,若优先级比栈顶运算符的高,也将运算符压入operateStack;
            tmp.add(item);
        } else {//否则,将operateStack栈顶的运算符弹出并压入到tmp中,再次转到(4-1)与operateStack中新的栈顶运算符相比较;
            while (!operateStack.isEmpty() && priority(item) <= priority(operateStack.peek())) {
                tmp.add(operateStack.pop());
            }
            //将运算符压入栈
            operateStack.push(item);
        }
    }
    //没一轮结束需要将操作位置往后移动一位
    index++;
}
//解析结束后需要将剩下的栈元素全部弹出来放入到tmp中
while (!operateStack.isEmpty()) {
    tmp.add(operateStack.pop());
}
return tmp;

}
  (四)、计算结果
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
/**
     * 根据后缀表达式计算值
     * @param items 后缀表达式
     * @return 计算结果
     */
    public int calculate(List items) {
        /**
         * 用于保存过程变量和操作符等
         */
        Stack stack = new LinkStack<>();
        //便利
        for (String item : items) {
            //如果为数字,直接放入栈中
            if (item.matches("\d+")) {
                stack.push(item);
            }else {
                //弹出栈顶元素进行运算
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res;
                switch (item) {
                    case "+" :
                        res = num1 + num2;
                        break;
                    case "-" :
                        res = num1 - num2;
                        break;
                    case "*" :
                        res = num1 * num2;
                        break;
                    case "/" :
                        res = num1 / num2;
                        break;
                    default:
                        throw new RuntimeException("非法的运算符:" + item);
                }
                //运算完将结果压入栈中
                stack.push("" + res);
            }
        }
        //整个表达式扫描结束后,此时栈中只剩一个元素,该元素即为结算结果,从栈中弹出即可
        return Integer.parseInt(stack.pop());
    }

  (五)、测试
1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
        Calculator calculator = new Calculator();
        List items = calculator.midfixToSuffix("(3+4)*5-6");
        System.out.println("后缀表达式为:" + items);
        int result = calculator.calculate(items);
        System.out.println("运算结果为: " + result);
    }

//后缀表达式为:[3, 4, +, 5, *, 6, -]
//运算结果为: 29

完整的代码如下:

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
public class Calculator {

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        List items = calculator.midfixToSuffix("(3+4)*5-6");
        System.out.println("后缀表达式为:" + items);
        int result = calculator.calculate(items);
        System.out.println("运算结果为: " + result);
    }

    /**
     * 1)初始化两个栈:运算符栈operateStack和储存中间结果的栈tmp;
     * 2)从左至右扫描中缀表达式;
     * 3)遇到操作数时,将其压tmp;
     * 4)遇到运算符时,比较其与operateStack栈顶运算符的优先级:
     *   4.1)如果operateStack为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
     *   4.2)否则,若优先级比栈顶运算符的高,也将运算符压入operateStack;
     *   4.3)否则,将operateStack栈顶的运算符弹出并压入到tmp中,再次转到(4-1)与operateStack中新的栈顶运算符相比较;
     * 5)遇到括号时:
     *   5.1) 如果是左括号“(”,则直接压入operateStack
     *   5.2) 如果是右括号“)”,则依次弹出operateStack栈顶的运算符,并压入tmp,直到遇到左括号为止,此时将这一对括号丢弃
     * 6)重复步骤2至5,直到表达式的最右边
     * 7)将operateStack中剩余的运算符依次弹出并压入tmp
     * 8)依次弹出tmp中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
     * @param expr 中缀表达式
     * @return 后缀表达式
     */
    public List midfixToSuffix(String expr) {
        /**
         * 将表达式的操作数和运算符转换成集合
         */
        List items = parseExpr(expr);
        //操作数栈
        Stack operateStack = new LinkStack<>();
        //临时变量的保存集合,这里使用了List集合
        //如果用栈也可以实现,但是需要在最后将弹出栈元素的逆序进行运算
        //所以使用List集合避免了这个转换的问题
        List tmp = new ArrayList<>();
        //操作的位置
        int index = 0;
        //表达式长度
        int len = items.size();
        while (index < len) {
            String item = items.get(index);
            //遇到操作数时,将其压tmp;
            if (item.matches("\d+")) {
                tmp.add(item);
            }else if (item.equals("(")) {//如果是左括号“(”,则直接压入operateStack
                operateStack.push(item);
            } else if (item.equals(")")) {//如果是右括号“)”,则依次弹出operateStack栈顶的运算符,并压入tmp,直到遇到左括号为止,此时将这一对括号丢弃
                while (!operateStack.peek().equals("(")) {
                    tmp.add(operateStack.pop());
                }
                //直接弹出栈顶元素即可
                operateStack.pop();
            } else {//遇到运算符时,比较其与operateStack栈顶运算符的优先级
                //如果operateStack为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
                if (operateStack.isEmpty() || "(".equals(operateStack.peek())) {
                    operateStack.push(item);
                }else if (priority(item) > priority(operateStack.peek())) {//否则,若优先级比栈顶运算符的高,也将运算符压入operateStack;
                    tmp.add(item);
                } else {//否则,将operateStack栈顶的运算符弹出并压入到tmp中,再次转到(4-1)与operateStack中新的栈顶运算符相比较;
                    while (!operateStack.isEmpty() && priority(item) <= priority(operateStack.peek())) {
                        tmp.add(operateStack.pop());
                    }
                    //将运算符压入栈
                    operateStack.push(item);
                }
            }
            //没一轮结束需要将操作位置往后移动一位
            index++;
        }
        //解析结束后需要将剩下的栈元素全部弹出来放入到tmp中
        while (!operateStack.isEmpty()) {
            tmp.add(operateStack.pop());
        }
        return tmp;
    }

    /**
     * 获取运算符的优先级,数字越大优先级越大
     * @param operateChar 运算符
     * @return 优先级
     */
    private int priority(String operateChar) {
        if ("*".equals(operateChar) || "/".equals(operateChar)) {
            return 2;
        }else if ("+".equals(operateChar) || "-".equals(operateChar)) {
            return 1;
        }else {
            //throw new RuntimeException("非法的操作符:" + operateChar);
            return 0;
        }
    }

    /**
     * 将表达式解析成List
     * @param expression 表达式
     * @return
     */
    private List parseExpr(String expression) {
        char[] chars = expression.toCharArray();
        int len = chars.length;
        List items = new ArrayList<>(len);
        int index = 0;
        while (index < len) {
            char c = chars[index];
            //数字
            if (c >= 48 && c <= 57) {
                String tmp = "";
                //操作数大于一位数,主要是通过判断下一位是否为数字
                while (index < chars.length && chars[index] >= 48 && chars[index] <= 57) {
                    tmp += chars[index];
                    index++;
                }
                items.add(tmp);
            }else {
                items.add(c + "");
                index++;
            }
        }
        return items;
    }

    /**
     * 根据后缀表达式计算值
     * @param items 后缀表达式
     * @return 计算结果
     */
    public int calculate(List items) {
        /**
         * 用于保存过程变量和操作符等
         */
        Stack stack = new LinkStack<>();
        //便利
        for (String item : items) {
            //如果为数字,直接放入栈中
            if (item.matches("\d+")) {
                stack.push(item);
            }else {
                //弹出栈顶元素进行运算
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res;
                switch (item) {
                    case "+" :
                        res = num1 + num2;
                        break;
                    case "-" :
                        res = num1 - num2;
                        break;
                    case "*" :
                        res = num1 * num2;
                        break;
                    case "/" :
                        res = num1 / num2;
                        break;
                    default:
                        throw new RuntimeException("非法的运算符:" + item);
                }
                //运算完将结果压入栈中
                stack.push("" + res);
            }
        }
        //整个表达式扫描结束后,此时栈中只剩一个元素,该元素即为结算结果,从栈中弹出即可
        return Integer.parseInt(stack.pop());
    }

}

简单的计算器基本已经写完了。如果你感兴趣的话,可以将上面的代码拿下来自己显现更多的功能,比如说支持小数,其他的运算,表达式中允许有空格等。这些功能实现起来是不难的,只要在上面的基础上做一些改动即可。

原文地址https://www.cnblogs.com/rainple/p/12731553.html

相关文章
|
9天前
|
存储 IDE Java
java设置栈内存大小
在Java应用中合理设置栈内存大小是确保程序稳定性和性能的重要措施。通过JVM参数 `-Xss`,可以灵活调整栈内存大小,以适应不同的应用场景。本文介绍了设置栈内存大小的方法、应用场景和注意事项,希望能帮助开发者更好地管理Java应用的内存资源。
22 1
|
1月前
|
存储 算法 Java
解锁“分享文件”高效密码:探秘 Java 二叉搜索树算法
在信息爆炸的时代,文件分享至关重要。二叉搜索树(BST)以其高效的查找性能,为文件分享优化提供了新路径。本文聚焦Java环境下BST的应用,介绍其基础结构、实现示例及进阶优化。BST通过有序节点快速定位文件,结合自平衡树、多线程和权限管理,大幅提升文件分享效率与安全性。代码示例展示了文件插入与查找的基本操作,适用于大规模并发场景,确保分享过程流畅高效。掌握BST算法,助力文件分享创新发展。
|
2月前
|
存储 人工智能 算法
解锁分布式文件分享的 Java 一致性哈希算法密码
在数字化时代,文件分享成为信息传播与协同办公的关键环节。本文深入探讨基于Java的一致性哈希算法,该算法通过引入虚拟节点和环形哈希空间,解决了传统哈希算法在分布式存储中的“哈希雪崩”问题,确保文件分配稳定高效。文章还展示了Java实现代码,并展望了其在未来文件分享技术中的应用前景,如结合AI优化节点布局和区块链增强数据安全。
|
2月前
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
95 16
|
2月前
|
算法 搜索推荐 Java
【潜意识Java】深度解析黑马项目《苍穹外卖》与蓝桥杯算法的结合问题
本文探讨了如何将算法学习与实际项目相结合,以提升编程竞赛中的解题能力。通过《苍穹外卖》项目,介绍了订单配送路径规划(基于动态规划解决旅行商问题)和商品推荐系统(基于贪心算法)。这些实例不仅展示了算法在实际业务中的应用,还帮助读者更好地准备蓝桥杯等编程竞赛。结合具体代码实现和解析,文章详细说明了如何运用算法优化项目功能,提高解决问题的能力。
77 6
|
2月前
|
算法 Java C++
【潜意识Java】蓝桥杯算法有关的动态规划求解背包问题
本文介绍了经典的0/1背包问题及其动态规划解法。
58 5
Java计算器的简易实现(+-*/)
java计算器的简易实现(+-*/) import java.util.Scanner; /* 写一个计算器 实现加减乘除四个功能 并且能够用循环接收新的数据,通过用户交互实现 写四个方法 + - * / 利用循环加switch进行用户交互 传递操作的两个数 输出结果 */ public class
|
9天前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
121 60
【Java并发】【线程池】带你从0-1入门线程池
|
5天前
|
Java 调度
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
51 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
|
21天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
87 14