Java EE知识储备(九)

本文深入探讨了JavaEE中的线程安全、数组操作、文件扫描、数据库设计等关键知识点,提供实用代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java EE知识储备(九)

目录:

1、i++线程安全类AtomicInteger:

2、快速输出a[i] = i的数:

3、输出指定命名的文件的绝对路径:

4、二分查找与快排应用:

5、数据库表结构符合三大范式设计:

6、数组旋转算法实现:


1、在int i = 0; i = i++语句中,i = i++为什么不是线程安全的?JDK中哪个类能达到以上程序的效果,并且是线程安全的。

int i = 0; i = i++的执行过程:

生成整数0

将i指向的整数0赋值给1号数据存储单元(i = 0)

将i指向的1号存储单元的值取出,放到数据栈栈顶(i = 0,栈顶值 = 0)

执行自增操作,此时1号存储单元的值 = 1(i = 1)

将数据栈顶的值取出,赋值给1号存储单元(i = 0)

共包括三个独立操作:

获得变量当前值(读)

为该值+1(改)

写回新的值(写)

因为是三个操作,而不是原子操作,所以不是线程安全的。

JDK中的java.util.concurrent.atomic.AtomicInteger类,提供了原子操作的Integer类,提供了线程安全且高效的原子操作,底层是CAS(Compare and Set),检测栈中的值是否被其它线程改变,如果被改变,则CAS操作失败。

附注:

(1)原子操作:

一个事务包含多个操作,这些操作要么全部执行,要么全都不执行。实现事务的原子性,要支持回滚操作,在某个操作失败后,回滚到事务执行之前的状态。

(2)CAS特点:

在CPU指令级别实现了原子操作。通过CPU的一条指令来完成三个步骤,因此不存在其执行某一个步骤时被中断的可能性。

CAS(v,e,u):v表示内存地址(变量),e表示变量的期望值,u表示变量的新值。操作时,当地址v上存放的值等于期望值e,则将地址v上的值赋值为新值u,否则,不做任何操作,但是要返回原值是多少。


2、定义数组int a[200] = {1, 2, 3, 3, …},数组元素都是正整数,且a[i + 1] >= a[i],请快速输出a[i] = i的数。

(1)QuickOutputService.java

package com.remoa.algorithm.quickOutput.service;

/**
 * 思路:如果a[i] > i,则接下来直接遍历i + (a[i] - i)的元素,从而减少遍历次数。
 * @author Dengqinyi
 * @since 2018/02/05
 */
public class QuickOutputService {
    public static void quickOutput(int[] a){
        for (int i = 0; i < a.length; ) {
            if(a[i] == i){
                System.out.print(a[i++] + " ");
            }else if(i < a[i]){
                i = a[i];
            }else{
                i++;
            }
        }
    }

}
(2)QuickOutputAction.java

package com.remoa.algorithm.quickOutput.action;

import com.remoa.algorithm.quickOutput.service.QuickOutputService;

import java.util.Random;

/**
 * @author Dengqinyi
 * @since 2018/02/05
 */
public class QuickOutputAction {
    public static void main(String[] args) {
        Random random = new Random();
        int[] a = new int[10000];
        a[0] = 0;
        for (int i = 1; i < a.length; i++) {
            a[i] = random.nextInt(3) + a[i - 1];
        }
        QuickOutputService.quickOutput(a);
    }
    
}

(3)运行结果:
图2.1 运行结果截图

3、扫描指定文件夹下面所有以.txt或.log结尾的文件,并将其绝对路径输出。

 (1)ScanFileService.java

package com.remoa.algorithm.ScanFile.service;

import java.io.File;

/**
 * 思路:首先找到指定路径下的所有文件,然后判断是否符合结尾,是则输出,否则,则继续递归向下查找
 * @author Dengqinyi
 * @since 2018/02/05
 */
public class ScanFileService {
    public static void getFilePath(String filePath){
        File files = new File(filePath);
        if(files.exists()) {
            File[] fileArray = files.listFiles();
            for (int i = 0; i < fileArray.length; i++) {
                if(fileArray[i].isDirectory()){
                    getFilePath(fileArray[i].getAbsolutePath());
                }else{
                    if(fileArray[i].getAbsolutePath().endsWith(".txt") || fileArray[i].getAbsolutePath().endsWith(".log")){
                        System.out.println(fileArray[i].getAbsoluteFile());
                    }
                }
            }
        }else{
            System.out.println("文件或目录均不存在");
        }
    }

}
(2)ScanFileAction.java

package com.remoa.algorithm.ScanFile.action;

import com.remoa.algorithm.ScanFile.service.ScanFileService;

/**
 * @author Dengqinyi
 * @since 2018/02/05
 */
public class ScanFileAction {
    public static void main(String[] args) {
        ScanFileService.getFilePath("D:\\用户目录\\我的文档");
    }
}
(3)运行结果:


图3.1 运行结果截图


4、定义有数组int[n] a = {1,2,3,3,4,3,2,…},数组a中的数均为正整数,当满足a[i] + a[t] = a[x]时,其中i、t、x均为正数,且小于等于n,求最大的a[x]。

 (1)BiggestNumberService.java

package com.remoa.algorithm.BiggestNumber.service;

/**
 * 思路:先排序,然后从最大的数开始向前遍历,判断该元素之前的元素是否存在二者相加等于它(这里排序算法使用快排)
 * @author Dengqinyi
 * @since 2018/02/05
 */
public class BiggestNumberService {
    public static int getBiggest(int[] a){
        a = quickSort(a, 0, a.length - 1);
        for(int i = a.length - 1; i >= 0; i--){
            for(int j = 0; j < i; j++){
                if(binarySearch(a, j, i - 1, a[i] - a[j])){
                    return a[i];
                }
            }
        }
        return -1;
    }

    public static boolean binarySearch(int[] a, int low, int high, int number){
        while(low < high){
            int tmp = (low + high) / 2;
            if(a[tmp] == number){
                return true;
            }else if(a[tmp] > number){
                high = tmp - 1;
            }else{
                low = tmp + 1;
            }
        }
        return false;
    }

    public static int[] quickSort(int[] a, int s, int t){
        if(s < t) {
            int tmp = partition(a, s, t);
            quickSort(a, s, tmp - 1);
            quickSort(a, tmp + 1, t);
        }
        return a;
    }

    public static int partition(int[] a, int low, int high){
        int pivlot = a[low];
        while(low < high){
            while(low < high && a[high] >= pivlot){
                high--;
            }
            a[low] = a[high];
            while(low < high && a[low] <= pivlot){
                low++;
            }
            a[high] = a[low];
        }
        a[low] = pivlot;
        return low;
    }
    
}
(2)BiggestNumberAction.java
package com.remoa.algorithm.BiggestNumber.action;

import com.remoa.algorithm.BiggestNumber.service.BiggestNumberService;

import java.util.Random;

/**
 * @author Dengqinyi
 * @since 2018/02/05
 */
public class BiggestNumberAction {
    public static void main(String[] args) {
        int[] testArray = new int[20];
        Random random = new Random();
        for (int i = 0; i < testArray.length; i++) {
            testArray[i] = random.nextInt(100);
            System.out.print(testArray[i] + " ");
        }
        System.out.println();
        int result = BiggestNumberService.getBiggest(testArray);
        if(result == -1){
            System.out.println("不存在最大的a[x]");
        }else{
            System.out.println("最大的a[x]为" + result);
        }
    }
    
}
(3)运行结果:

图4.1 运行结果截图


5、假设论坛系统数据库需要存储如下的一些数据:用户名、email、主页、电话、联系地址、发帖标题、发帖内容、回复标题以及回复内容。每天论坛的访问量达到百万级,更新帖子十万级。请给出数据库表结构设计并结合范式简要说明设计思路。

表1 用户表

用户id(主键)

用户名

email

主页

电话

联系地址

创建时间

表2 主题帖表

帖子id(主键)

帖子标题

帖子内容

用户id(外键)

创建时间

表3 回复帖表

回复帖id(主键)

回复标题

回复内容

父主帖id(外键)

用户id(外键)

父回复帖id

回复时间

附注:

(1)遵循的规则:

一个1:1的关系可以用两种转换方式:

①创建单独的关系表,表中存两个表的主键;

②两个实体合并成一张表,把两个表的属性合并,创建为一张大表;

一个1:n的关系可以用两种转换方式:

①创建单独的关系表,表中存两个表的主键;

②通过在n端的表中引入一列(1端表的主键)作为外键,一般采用这种方式来减少表的个数,从而提高查询效率;

一个m:n的关系转换为一个关系模式。只能创建单独的关系表,关系表中的主要内容为两个表的主键。

(2)三大范式:

第一范式:数据库表中的所有字段都是不可分解的原子值;

第二范式:数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言);

第三范式:数据库表中的每一列数据都和主键直接相关,而不能间接相关。即该表中不包含其它表中已经存在的非主键的字段。


6、一个长度为n的Integer数组中存着整型对象,实现时间复杂度为O(n),空间复杂度为O(1)的旋转m位至数组末尾的算法设计,其中n>m。

(1)ReverseArrayService.java

package com.remoa.algorithm.reverseArray.service;

/**
 * 一个长度为n的Integer数组中存着整型对象,实现时间复杂度为O(n),空间复杂度为O(1)的旋转m位至数组末尾的算法设计,其中n>m
 * 示例:数组1,2,3,4,5,6,7,翻转m=2位得到的结果为3,4,5,6,7,1,2
 * @author Dengqinyi
 * @since 2018/02/05
 */
public class ReverseArrayService {
    //时间复杂度为O(n),空间复杂度为O(1)
    public static Integer[] reverse(Integer[] array, int m){
        int tmp, i , j;
        //首先根据数组中间轴进行全部旋转,如1,2,3,4,5,6,7,旋转为7,6,5,4,3,2,1,时间复杂度O(n)
        for (i = 0, j = array.length - 1; i < j; i++, j--) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
        }
        //将前n - m个数字根据中间轴进行翻转,如此时m = 2,则7,6,5,4,3,2,1翻转为3,4,5,6,7,2,1,时间复杂度O(n)
        for (i = 0, j  = array.length - m - 1; i < j; i++, j--) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
        }
        //将剩余的m个数字根据中间轴进行翻转,时间复杂度O(n),则3,4,5,6,7,2,1翻转为3,4,5,6,7,1,2,时间复杂度O(n)
        for (i = array.length - m, j = array.length - 1; i < j; i++, j--) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
        }
        return array;
    }

    public static void main(String[] args) {
        int i = 0;
        i++;
        System.out.println(i);
    }
    
}
(2)ReverseArrayAction.java
package com.remoa.algorithm.reverseArray.action;

import com.remoa.algorithm.reverseArray.service.ReverseArrayService;

import java.util.Random;
import java.util.Scanner;

/**
 * @author Dengqinyi
 * @since 2018/02/05
 */
public class ReverseArrayAction {
    public static void main(String[] args) {
        boolean flag = true;
        int length = 0, m = 0;
        while(flag) {
            try {
                System.out.print("请输入测试数组长度:");
                Scanner scanner = new Scanner(System.in);
                length = scanner.nextInt();
                System.out.print("请输入m的值:");
                m = scanner.nextInt();
                if (m > length || m < 0 || length <= 0) {
                    System.out.println("输入存在错误,请重新输入~");
                    flag = true;
                } else {
                    flag = false;
                }
            }catch (Exception e){
                System.out.println("输入存在错误,请重新输入~");
            }
        }
        Integer[] testArray = new Integer[length];
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            testArray[i] = random.nextInt(100);
        }
        System.out.println("原数组为:");
        printResult(testArray);
        testArray = ReverseArrayService.reverse(testArray, m);
        System.out.println("现有数组:");
        printResult(testArray);
    }

    public static void printResult(Integer[] array){
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }
    
}
(3)运行结果:


图6.1 运行结果截图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值