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、扫描指定文件夹下面所有以.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.javapackage 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(主键)
用户名
主页
电话
联系地址
创建时间
表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.javapackage 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)运行结果: