Lambda表达式
Arrays.asList( "a", "b", "d" ).forEach(e -> System.out.println( e ) );
在#forEach中的“e -> System.out.println( e )”,即为Lambda表达式。
上代码也可写为:
Arrays.asList( "a", "b", "d" ).forEach((String e)->{
System.out.println(e);
});
以“->”为界,之前的“e”为参数,参数的类型会自动匹配。
之后的为逻辑运算等操作。
之后的运算可以当做一个方法来写。
例如:
历遍一个String数组,将其中的值赋值到另一个数组“newArrStr”中。
ArrayList<String> newArrStr = new ArrayList<>();
Arrays.asList("a","b","c","d").forEach(e ->newArrStr.add(e));
newArrStr.forEach(e-> System.out.println(e));
接口添加静态方法和默认方法
默认方法
在接口中使用“default”关键字标注的方法为默认方法。
在其它类实现某个接口时,其中的默认方法可以直接调用,也可以重写。
例如:
新建一个接口,其中包含一个默认方法。
public interface DefaultAble {
default String notRequired(){
return "默认方法";
}
}
新建一个类直接实现此接口。
public class useDefaultImpl implements DefaultAble{
public static void main(String[] args) {
useDefaultImpl useDefault = new useDefaultImpl();
System.out.println(useDefault.notRequired());
}
}
/*
out:
默认方法
*/
再写一个类,实现此接口并重写其中的默认方法。
public class overrideDefaultImpl implements DefaultAble {
@Override
public String notRequired(){
return "重写默认方法";
}
public static void main(String[] args) {
overrideDefaultImpl overrideDefault = new overrideDefaultImpl();
System.out.println(overrideDefault.notRequired());
}
}
/*
out:
重写默认方法
*/
默认方法不强制要求被实现。
但是值得注意的是默认方法可能会引起歧义或者编译错误。
静态方法
存在于接口中的静态方法,可以直接调用。
例如:
创建一个接口,其中有一个静态方法。
public interface DefaultAbleFactory {
/**
* Supplier类用于返回T类型的一个对象
* 调用一次,返回一次。
* @param supplier
* @return
*/
static DefaultAble create(Supplier<DefaultAble> supplier){
return supplier.get();
}
}
然后,创建一个类来调用此接口中的静态方法。
public class StaticMethodsExample {
public static void main(String[] args) {
DefaultAble defaultAble = DefaultAbleFactory.create(useDefaultImpl::new);
System.out.println(defaultAble.notRequired());
defaultAble = DefaultAbleFactory.create(overrideDefaultImpl::new);
System.out.println(defaultAble.notRequired());
}
}
/*
out:
默认方法
重写默认方法
*/
方法引用
在部分时候可以代替“.”来引用方法。
使用方法及注意点如下:
public class MethodReferences {
public static void main(String[] args) {
//引用无参构造器
Apple apple = Apple.create(Apple::new);
List<Apple> apples = Arrays.asList(apple);
//引用静态方法
apples.forEach(Apple::pick);
//此处的静态方法必须带参
//apples.forEach(Apple::recover);
//引用成员方法
apples.forEach(Apple::buy);
//应用实例对象的成员方法(必须带参)
Apple seller = Apple.create(Apple::new);
apples.forEach(seller::sell);
}
//内部类如果有静态方法,则必须为静态内部类
private static class Apple{
public static Apple create(final Supplier<Apple> supplier){
return supplier.get();
}
public static void pick(final Apple apple){
System.out.println("苹果从树上摘了下来"+apple.toString());
}
public static void recover(){
System.out.println("苹果又被按回了树上");
}
public void sell(final Apple apple){
System.out.println("苹果在商店被出售"+apple.toString());
}
public void buy(){
System.out.println("苹果被卖出");
}
}
}
/*
out:
苹果从树上摘了下来Java8.MethodReferences$Apple@548c4f57
苹果被卖出
苹果在商店被出售Java8.MethodReferences$Apple@548c4f57
*/
重复注解
在引入重复注解技术之前,在很多地方同一个注解只可以使用一次。
以下是重复注解的例子:
public class RepeatingAnnotations {
@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
public @interface Filters {
Filter[] value();
}
@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
@Repeatable( Filters.class )
public @interface Filter {
String value();
}
@Filter( "filter1" )
@Filter( "filter2" )
public interface Filterable {
}
public static void main(String[] args) {
for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
System.out.println( filter.value() );
}
}
}
/*
out:
filter1
filter2
*/
在例子中可以看到“@Filter”注解被使用了两次。
引入Optional避免空指针
简单的使用方法和使用注意如下。
代码样例:
public class OptionalExample {
public static void main(String[] args) {
//#ofNullable的参数如果是空则会返回一个Empty的对象,如果不是空则会调用Optional#Of来返回一个新的对象
Optional<String> fullName = Optional.ofNullable(null);
//如果当前对象不为空则会返回true
System.out.println("fullName 不为空吗?" + fullName.isPresent());
//如果当前对象为空则返回true
System.out.println("fullName 是否为空?" + fullName.isEmpty());
//map可以接收Lambda表达式进行其它的逻辑,#orElse所属对象如果为空则会调用“other”(即参数),不为空时则会调用当前对象
System.out.println(fullName.map(s->"Hey" + s + "!").orElse("Hey Stranger!"));
//Option#of会返回新的赋值为参数的Optional对象
Optional<String> firstName = Optional.of("Tom");
//Optional<String> firstName = Optional.ofNullable(null);
System.out.println("firstName 不为空吗?" + firstName.isPresent());
System.out.println("firstName 是否为空?" + firstName.isEmpty());
//如果目标对象为空则调用参数,参数可为Lambda表达式
//Optional#orElseGet的实现是用Supplier#Get来实现的,也就是说如果目标为空则直接获取参数对象
System.out.println("firstName: "+firstName.orElseGet(()->"[none]"));
System.out.println(firstName.map(s->"Hey " + s + "!").orElse("Hey Stranger!"));
//使用样例
String[] testArr = new String[3];
testArr[0] = "aaa";
testArr[1] = null;
testArr[2] = "bbb";
for (String temp :
testArr) {
System.out.println("当前元素为空吗?" + Optional.ofNullable(temp).isEmpty());
}
}
}
/*
out:
fullName 不为空吗?false
fullName 是否为空?true
Hey Stranger!
firstName 不为空吗?true
firstName 是否为空?false
firstName: Tom
Hey Tom!
*/
引入Streams相关API
在Java8之前,如果要遍历一个集合中的数据,可以使用迭代器或者for或者while循环来进行。
Java8中则对StreamsAPI进行了优化使得其更加容易使用。
以下为简单的介绍和示例:
public class StreamExample {
public static void main(String[] args) {
//创建一个集合
final Collection<Streams.Task> tasks = Arrays.asList(
new Streams.Task(Streams.Status.OPEN, 7),
new Streams.Task(Streams.Status.OPEN, 13),
new Streams.Task(Streams.Status.CLOSED, 8)
);
//计算处于某种状态的某个属性总和
final long totalPonitsOfOpenTasks = tasks
//调用stream
.stream()
//设置条件
.filter(task -> task.getStatus() == Streams.Status.OPEN)
//获取数值(Int型)
.mapToInt(Streams.Task::getPoints)
//算加和或者其他
.sum();
System.out.println("总共处于OPEN状态的点:" + totalPonitsOfOpenTasks);
//计算某个属性的总和
final long totalPoints = tasks
.stream()
.parallel()
.map(task -> task.getPoints())
.reduce(0, Integer::sum);
System.out.println("全部点数之和:" + totalPoints);
//按照某个属性分组
final Map< Streams.Status, List<Streams.Task>> groupByStatus = tasks
.stream()
.collect(Collectors.groupingBy(Streams.Task::getStatus));
System.out.println("按照Status分组结果:" + groupByStatus);
//计算某一属性(数字)占总体的比例
final Collection<String> result = tasks
.stream()
.mapToInt(Streams.Task::getPoints)
//转换类型
.asLongStream()
.mapToDouble(points->{
//不可直接使用points和totalPoints进行运算
//因为两个变量均为Long类型,Long即为大整数类型,所以结果和int类型相除类似,没有小数位
double tempPointVal = points;
double tempTotalVal = totalPoints;
return tempPointVal/tempTotalVal;
})
//对类型进行装箱操作,即,转换为包装类
.boxed()
.mapToLong(weight->(long)(weight*100))
.mapToObj(percentage->percentage+"%")
.collect(Collectors.toList());
System.out.println("各元素所占比例:" + result);
//又一个示例
ArrayList<String> testArrInt = new ArrayList<>();
testArrInt.add("1");
testArrInt.add("2");
testArrInt.add("3");
testArrInt.add("4");
testArrInt.add("5");
testArrInt.add("10");
long sumArr = testArrInt
.stream()
.mapToInt(Integer::parseInt)
.sum();
System.out.println("数组加和:" + sumArr);
}
}
补充
并行流
在遍历某个数组时,for循环是最常见也是最普通的,但是相对于流操作来说速度也是要慢很多的。
在字面上看,似乎并行流也应该与并行数组一样,比串行处理快很多,但是从实验结果看并非如此。
串行流快还是并行流快是分场合的。
以下是示例代码:
public class StreamExampleTwo {
public static int aimInt = 0;
public static void doSome(){
aimInt++;
}
public static void main(String[] args) {
//运行次数
int runTimes = 1000;
//每次每个方法遍历的次数
int arrMaxSize = 10000;
//错误值
int lostTImes = 0;
double aveFTime = 0;
double aveFSTime = 0;
double avePSOTime = 0;
double avePSTime = 0;
boolean isFCorrect = true;
boolean isSCorrect = true;
boolean isPSOCorrect = true;
boolean isPSCorrect = true;
//检测指标
aimInt = 0;
//指标正确的值
int correctAimInt = 4*runTimes*arrMaxSize;
Integer[] arrInt = new Integer[arrMaxSize];
double startTime;
double endTime;
//测试数组赋值
startTime = System.currentTimeMillis();
Arrays.parallelSetAll(
arrInt,
index -> ThreadLocalRandom.current().nextInt(100)
);
endTime = System.currentTimeMillis();
System.out.println("parallelSetAll执行时间:" + (endTime - startTime) + "ms");
List<Integer> integerList = Arrays.asList(arrInt);
for (int i = 0; i < runTimes;i++){
System.out.println("第" + (i + 1) + "执行开始!");
//普通for循环遍历
startTime = System.currentTimeMillis();
for (Integer coursor : integerList){
doSome();
}
endTime = System.currentTimeMillis();
System.out.println("for循环遍历执行时间:" + (endTime - startTime) + "ms");
aveFTime += (endTime - startTime);
if((i * 4 * arrMaxSize + arrMaxSize - lostTImes) != aimInt){
isFCorrect = false;
lostTImes = (i * 4 * arrMaxSize + arrMaxSize) - aimInt;
System.out.println("for循环出错,No." + (i+1) + ",错误值:" + aimInt);
}
System.out.println("for循环后的目标Integer为:" + aimInt + ",本次耗时:" + (endTime - startTime) + "ms");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//普通流遍历
startTime = System.currentTimeMillis();
integerList.stream().forEach(coursor ->doSome());
endTime = System.currentTimeMillis();
System.out.println("普通流遍历执行时间:" + (endTime - startTime) + "ms");
aveFSTime += (endTime - startTime);
if(i * 4 * arrMaxSize + arrMaxSize * 2 - lostTImes!= aimInt){
isSCorrect = false;
lostTImes = (i * 4 * arrMaxSize + arrMaxSize * 2) - aimInt;
System.out.println("普通流出错,No." + (i+1) + ",错误值:" + aimInt);
}
System.out.println("普通流遍历执行后的目标Integer为:" + aimInt + ",本次耗时:" + (endTime - startTime) + "ms");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//并行流遍历
startTime = System.currentTimeMillis();
integerList.parallelStream().forEachOrdered(coursor ->doSome());
endTime = System.currentTimeMillis();
System.out.println("并行流顺序遍历执行时间:" + (endTime - startTime) + "ms");
avePSOTime += (endTime - startTime);
if(i * 4 * arrMaxSize + arrMaxSize * 3 - lostTImes!= aimInt){
isPSOCorrect = false;
lostTImes = (i * 4 * arrMaxSize + arrMaxSize * 3) - aimInt;
System.out.println("并行流顺序出错,No." + (i+1) + ",错误值:" + aimInt);
}
System.out.println("并行流顺序遍历执行后的目标Integer为:" + aimInt + ",本次耗时:" + (endTime - startTime) + "ms");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
startTime = System.currentTimeMillis();
integerList.parallelStream().forEach(coursor ->doSome());
endTime = System.currentTimeMillis();
System.out.println("并行流非顺序遍历执行时间:" + (endTime - startTime) + "ms");
avePSTime += (endTime - startTime);
if(i * 4 * arrMaxSize + arrMaxSize * 4 - lostTImes!= aimInt){
isPSCorrect = false;
lostTImes = (i * 4 * arrMaxSize + arrMaxSize * 4) - aimInt;
System.out.println("并行流非顺序出错,No." + (i+1) + ",错误值:" + aimInt);
}
System.out.println("并行流非顺序遍历执行的目标Integer为:" + aimInt + ",本次耗时:" + (endTime - startTime) + "ms");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第" + (i + 1) + "执行结束!");
System.out.println();
}
System.out.println("普通for循环遍历 " + runTimes + " 次,平均用时为:" + aveFTime/runTimes + "ms" + ",总耗时为:" + aveFTime + "ms");
System.out.println("普通流循环遍历 " + runTimes + " 次,平均用时为:" + aveFSTime/runTimes + "ms" + ",总耗时为:" + aveFSTime + "ms");
System.out.println("并行流顺序循环遍历 " + runTimes + " 次,平均用时为:" + avePSOTime/runTimes + "ms" + ",总耗时为:" + avePSOTime + "ms");
System.out.println("并行流非顺序循环遍历 " + runTimes + " 次,平均用时为:" + avePSTime/runTimes + "ms" + ",总耗时为:" + avePSTime + "ms");
System.out.println("最终应为Integer为:" + correctAimInt);
System.out.println("最终实际Integer为:" + aimInt);
System.out.println("是否有错误?" + (aimInt == correctAimInt ? "没有错误":"有错误,错误次数:" + (correctAimInt - aimInt)));
if(!isFCorrect){
System.out.println("普通for循环出错");
}
if(!isSCorrect){
System.out.println("普通流出错");
}
if(!isPSOCorrect){
System.out.println("并行流顺序遍历出错");
}
if(!isPSCorrect){
System.out.println("并行流非顺序遍历出错");
}
}
}
以下是执行3次,每次循环10次的结果:
普通for循环遍历 3 次,平均用时为:0.3333333333333333ms,总耗时为:1.0ms
普通流循环遍历 3 次,平均用时为:0.0ms,总耗时为:0.0ms
并行流顺序循环遍历 3 次,平均用时为:1.3333333333333333ms,总耗时为:4.0ms
并行流非顺序循环遍历 3 次,平均用时为:0.3333333333333333ms,总耗时为:1.0ms
最终应为Integer为:120
最终实际Integer为:120
是否有错误?没有错误
可以看到差别并不明显。
以下是执行10次,每次循环10000次的结果:
普通for循环遍历 10 次,平均用时为:0.1ms,总耗时为:1.0ms
普通流循环遍历 10 次,平均用时为:0.1ms,总耗时为:1.0ms
并行流顺序循环遍历 10 次,平均用时为:0.6ms,总耗时为:6.0ms
并行流非顺序循环遍历 10 次,平均用时为:0.1ms,总耗时为:1.0ms
最终应为Integer为:400000
最终实际Integer为:378000
是否有错误?有错误,错误次数:22000
并行流非顺序遍历出错
可以看到,这种情况下并行流开始出现错误,但是在是在时间上开始出现。
当执行次数为1000次,每次循环次数为10000次的结果:
普通for循环遍历 1000 次,平均用时为:0.003ms,总耗时为:3.0ms
普通流循环遍历 1000 次,平均用时为:0.003ms,总耗时为:3.0ms
并行流顺序循环遍历 1000 次,平均用时为:0.017ms,总耗时为:17.0ms
并行流非顺序循环遍历 1000 次,平均用时为:0.004ms,总耗时为:4.0ms
最终应为Integer为:40000000
最终实际Integer为:37033984
是否有错误?有错误,错误次数:2966016
并行流非顺序遍历出错
此时的并行流的执行速度总体不如串行流或者 for循环。
但是当循环次数为1,数组循环次数为100,000,000时的结果为:
普通for循环遍历 1 次,平均用时为:183.0ms,总耗时为:183.0ms
普通流循环遍历 1 次,平均用时为:92.0ms,总耗时为:92.0ms
并行流顺序循环遍历 1 次,平均用时为:1654.0ms,总耗时为:1654.0ms
并行流非顺序循环遍历 1 次,平均用时为:865.0ms,总耗时为:865.0ms
最终应为Integer为:400000000
最终实际Integer为:335420276
是否有错误?有错误,错误次数:64579724
并行流非顺序遍历出错
此时的耗时出现了比较大的差异,普通串行流耗时最小,其次for循环,最慢且容易出错的为并行流顺序访问。
但是,这就说明并行流就一定不好了吗?
这次,将要做的任务为求一个数组值的和。
这次的任务更适合划分为小任务来做。
以下是另一段代码示例:
public class StreamExampleThree {
public static void main(String[] args) {
int arrMaxSize = 1000000000;
Integer[] arrInt = new Integer[arrMaxSize];
double startTime;
double endTime;
//测试数组赋值
startTime = System.currentTimeMillis();
Arrays.parallelSetAll(
arrInt,
index -> ThreadLocalRandom.current().nextInt(3)
);
endTime = System.currentTimeMillis();
System.out.println("parallelSetAll执行时间:" + (endTime - startTime) + "ms");
List<Integer> integerList = Arrays.asList(arrInt);
//for循环
long sumOne = 0;
startTime = System.currentTimeMillis();
for (Integer coursor : integerList){
sumOne += coursor;
}
endTime = System.currentTimeMillis();
System.out.println("for循环计算结果:" + sumOne);
System.out.println("for循环执行时间:" + (endTime - startTime) + "ms");
//串行流
long sumTwo = 0;
startTime = System.currentTimeMillis();
sumTwo = integerList
.stream()
.mapToInt(coursor -> coursor)
.sum();
endTime = System.currentTimeMillis();
System.out.println("串行流计算结果:" + sumTwo);
System.out.println("串行流执行时间:" + (endTime - startTime) + "ms");
//并行流
long sumThree = 0;
startTime = System.currentTimeMillis();
sumThree = integerList
.parallelStream()
.mapToInt(coursor -> coursor)
.sum();
endTime = System.currentTimeMillis();
System.out.println("并行流计算结果:" + sumThree);
System.out.println("并行流执行时间:" + (endTime - startTime) + "ms");
}
}
当要求和数组大小为10时,实验结果:
parallelSetAll执行时间:10.0ms
for循环计算结果:9
for循环执行时间:0.0ms
串行流计算结果:9
串行流执行时间:1.0ms
并行流计算结果:9
并行流执行时间:1.0ms
当要求和数组大小为10000时,实验结果:
parallelSetAll执行时间:9.0ms
for循环计算结果:10114
for循环执行时间:1.0ms
串行流计算结果:10114
串行流执行时间:3.0ms
并行流计算结果:10114
并行流执行时间:1.0ms
当要求和数组大小为10000000时,实验结果:
parallelSetAll执行时间:96.0ms
for循环计算结果:10001226
for循环执行时间:24.0ms
串行流计算结果:10001226
串行流执行时间:17.0ms
并行流计算结果:10001226
并行流执行时间:17.0ms
当要求和数组大小为1000000000时,实验结果:
parallelSetAll执行时间:6130.0ms
for循环计算结果:999995720
for循环执行时间:1407.0ms
串行流计算结果:999995720
串行流执行时间:868.0ms
并行流计算结果:999995720
并行流执行时间:288.0ms
从这四组实验可以看出,当面对超大数组求和时,并行流的处理效率是非常高的。
第一次实验选择的任务,是对并行处理极不友好的,即求递增数值,这种任务不容易划分为多个小任务。
而第二次实验,是对并行处理比较友好的,各个小任务之间没有直接的联系,最后的目标只需要将每个小任务的结果相加即可。
在真正的开发时,应该注意选择。
引入新的Date/Time相关API
处理时间和日期一直是最令Java开发者头疼的问题。
在Java8中引入了新的Date-Time API(JSR 310)来改进时间和日期的处理。
以下是示例代码和一部分说明:
public class TimeAndDateExample {
public static void main(String[] args) {
//获取系统世界统一时间
final Clock clock = Clock.systemUTC();
System.out.println("clock#instant:" + clock.instant());
System.out.println("clock#millis:" + clock.millis());
//获取本地日期
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now(clock);
System.out.println("LocalDate#now:" + date);
System.out.println("LocalDate#now(clock):" + dateFromClock);
//获取本地时间
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now(clock);
System.out.println("LocalTime#now:" + time);
System.out.println("LocalTime#now(clock):" + timeFromClock);
//同时获取本地日期和时间
final LocalDateTime dateTime = LocalDateTime.now();
final LocalDateTime dateTimeFromClock = LocalDateTime.now(clock);
System.out.println("LocalDateTime#now:" + dateTime);
System.out.println("LocalDateTime#now(clock):" + dateTimeFromClock);
//获取特定时区的时间和日期
final ZonedDateTime zonedDateTime = ZonedDateTime.now();
final ZonedDateTime zonedDateTimeFromClock = ZonedDateTime.now(clock);
final ZonedDateTime zonedDateTimeFromZone = ZonedDateTime.now(ZoneId.of("America/Chicago"));
System.out.println("ZonedDateTime#now;" + zonedDateTime);
System.out.println("ZonedDateTime#now(clock):" + zonedDateTimeFromClock);
System.out.println("ZonedDateTime#now(ZoneId#of);" + zonedDateTimeFromZone);
//计算两日期之间相差天数
final LocalDateTime from = LocalDateTime.of(1999, Month.JULY, 18, 16, 0,0);
//可以直接指定目标或者起始时间
//final LocalDateTime to = LocalDateTime.of(2019, Month.AUGUST, 18, 16, 0,0);
//也可以直接从系统中获取
final LocalDateTime to = LocalDateTime.now();
final Duration duration = Duration.between(from, to);
System.out.println("相差天数:" + duration.toDays());
System.out.println("相差小时数:" + duration.toHours());
}
}
Nashorn JavaScript引擎
Java8提供了新的Nashorn JavaScript引擎,这允许我们在JVM开发并运行JS应用。
这使得Java可以与JavaScript进行交互。
以下是示例代码和一部分说明:
public class NashornJavaScriptEngineExample {
public static void main(String[] args) {
//新建实例对象
ScriptEngineManager manager = new ScriptEngineManager();
//获取JavaScript引擎
ScriptEngine engine = manager.getEngineByName("JavaScript");
System.out.println("获取的Class名称:" + engine.getClass().getName());
String scriptStr = "" +
"function f(){" +
" return 20" +
"};" +
"f() + 20";
try {
System.out.println("简单的运行结果:" + engine.eval(scriptStr));
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
/*
out:
Warning: Nashorn engine is planned to be removed from a future JDK release
获取的Class名称:jdk.nashorn.api.scripting.NashornScriptEngine
简单的运行结果:40.0
*/
在JDK 12.0.2版本中可以看到这样的警告:
Warning: Nashorn engine is planned to be removed from a future JDK.
意为在未来的JDK的发行版本Nashorn JavaScript引擎将会被移除。
新增Base64 API
什么是Base64编码?
Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。
Base64编码是从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息。采用Base64编码具有不可读性,需要解码后才能阅读。
在Java8中开发者可以直接进行Base64编码,而不需要第三方库。
以下是示例代码:
public class Base64Example {
public static void main(String[] args) {
final String text = "Base64 in Java 8~~~~";
System.out.println("初始字符串:" + text);
//获取编码器,并设置要转换字符串的字符集,最后将目标字符串转换为Byte数组
final String encoded = Base64
.getEncoder()
.encodeToString(text.getBytes(StandardCharsets.UTF_8));
System.out.println("编码之后的字符串:" + encoded);
//获取解码器,调用解码方法解码目标字符串,设置解码之后字符串的字符集
final String decoded = new String(
Base64.getDecoder().decode(encoded),
StandardCharsets.UTF_8
);
System.out.println("解码后的字符串:" + decoded);
}
}
值得注意的是:BASE64不是用来加密的,是BASE64编码后的字符串,全部都是由标准键盘上面的常规字符组成,这样编码后的字符串在网关之间传递不会产生UNICODE字符串不能识别或者丢失的现象。
并行数组
Java8版本新增了很多新的方法,用于支持并行数组处理。最重要的方法是parallelSort(),可以显著加快多核机器上的数组排序。
以下为示例代码:
public class ParabllelArrayExample {
public static void main(String[] args) {
int arrMaxSize = 400000;
int[] longArr = new int[arrMaxSize];
int[] comparisonArr = new int[arrMaxSize];
long startTime;
long endTime;
MergeSort mergeSort = new MergeSort();
startTime = System.currentTimeMillis();
Arrays.parallelSetAll(
longArr,
index -> ThreadLocalRandom.current().nextInt(1000000)
);
endTime = System.currentTimeMillis();
System.out.println("parallelSetAll执行时间:" + (endTime - startTime) + "ms");
startTime = System.currentTimeMillis();
for (int i=0;i<arrMaxSize;i++){
comparisonArr[i] = ThreadLocalRandom.current().nextInt(1000000);
}
endTime = System.currentTimeMillis();
System.out.println("使用for循环SetAll执行时间:" + (endTime - startTime) + "ms");
startTime = System.currentTimeMillis();
Arrays.parallelSort(longArr);
endTime = System.currentTimeMillis();
System.out.println("parallelSort执行时间:" + (endTime - startTime) + "ms");
//复杂度为nlogn的归并排序作为对比
startTime = System.currentTimeMillis();
mergeSort.mergeSort(comparisonArr, 0, comparisonArr.length-1);
endTime = System.currentTimeMillis();
System.out.println("归并排序执行时间:" + (endTime - startTime) + "ms");
}
}
/*
out:
parallelSetAll执行时间:24ms
使用for循环SetAll执行时间:10ms
parallelSort执行时间:104ms
归并排序执行时间:61174ms
*/
可以通过控制台输出的数据看到,虽然在赋值的时候使用Arrays#parallelSetAll可能比普通的for循环还要慢,但是在排序时与相对较低复杂度的归并算法比较,Arrays#parallelSort比归并算法快近600倍的。
补充
归并算法代码来自:
https://blog.csdn.net/qq_36442947/article/details/81612870
代码:
public class MergeSort {
/**
* 两路归并算法,两个排好序的子序列合并为一个子序列
* @param a
* @param left
* @param mid
* @param right
*/
public void merge(int []a,int left,int mid,int right){
//辅助数组
int []tmp=new int[a.length];
//p1、p2是检测指针,k是存放指针
int p1=left,p2=mid+1,k=left;
while(p1<=mid && p2<=right){
if(a[p1]<=a[p2]){
tmp[k++]=a[p1++];
}
else{
tmp[k++]=a[p2++];
}
}
while(p1<=mid){
//如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
tmp[k++]=a[p1++];
}
while(p2<=right){
//同上
tmp[k++]=a[p2++];
}
//复制回原素组
for (int i = left; i <=right; i++){
a[i]=tmp[i];
}
}
public void mergeSort(int [] a,int start,int end){
//当子序列中只有一个元素时结束递归
if(start<end){
//划分子序列
int mid=(start+end)/2;
//对左侧子序列进行递归排序
mergeSort(a, start, mid);
//对右侧子序列进行递归排序
mergeSort(a, mid+1, end);
//合并
merge(a, start, mid, end);
}
}
}
Array转List
数组转成List集合,最常见的有四种方法,其中第四种方法需要在Java 9或者更高版本使用。
以下是示例代码:
public class ArrayToListExample {
public static void main(String[] args) {
int arrMaxSize = 400000;
Integer[] arrInt = new Integer[arrMaxSize];
long startTime;
long endTime;
//测试数组赋值
startTime = System.currentTimeMillis();
Arrays.parallelSetAll(
arrInt,
index -> ThreadLocalRandom.current().nextInt(1000000)
);
endTime = System.currentTimeMillis();
System.out.println("parallelSetAll执行时间:" + (endTime - startTime) + "ms");
//第一种转换方法
List<Integer> aimIntListOne = new ArrayList<>(arrInt.length);
startTime = System.currentTimeMillis();
for (int temp : arrInt){
aimIntListOne.add(temp);
}
endTime = System.currentTimeMillis();
System.out.println("使用for循环将数组转换为List执行时间:" + (endTime - startTime) + "ms");
//第二种转换方法
startTime = System.currentTimeMillis();
//注意类型,即使是Integer和int的关系也不可以,都要用包装类
List<Integer> aimIntListTwo = Arrays.asList(arrInt);
endTime = System.currentTimeMillis();
System.out.println("使用Arrays#asList将数组转换为List执行时间:" + (endTime - startTime) + "ms");
System.out.println("List是否为空?" + (aimIntListTwo.isEmpty()? "空" : "否,大小:" + aimIntListTwo.size()));
//第三种转换方式
List<Integer> aimIntListThree = new ArrayList<>(arrInt.length);
startTime = System.currentTimeMillis();
Collections.addAll( aimIntListThree, arrInt);
endTime = System.currentTimeMillis();
System.out.println("使用Collections#addAll将数组转换为List执行时间:" + (endTime - startTime) + "ms");
//第四种方式,Java版本要求大于等于9
startTime = System.currentTimeMillis();
List<Integer> aimIntListFour = List.of(arrInt);
endTime = System.currentTimeMillis();
System.out.println("使用List#of将数组转换为List执行时间:" + (endTime - startTime) + "ms");
}
}
/*
out:
parallelSetAll执行时间:33ms
使用for循环将数组转换为List执行时间:33ms
使用Array#asList将数组转换为List执行时间:0ms
List是否为空?否,大小:400000
使用Collections#addAll将数组转换为List执行时间:10ms
使用List#of将数组转换为List执行时间:9ms
*/
List->Array
将List集合转换为数组,最常见的方式只有一种。
此种方法在使用时需要注意。
示例代码如下:
public class ListToArrayExample {
public static void main(String[] args) {
int arrMaxSize = 400000;
Integer[] arrInt = new Integer[arrMaxSize];
long startTime;
long endTime;
//测试数组赋值
startTime = System.currentTimeMillis();
Arrays.parallelSetAll(
arrInt,
index -> ThreadLocalRandom.current().nextInt(1000000)
);
endTime = System.currentTimeMillis();
List<Integer> integerList = Arrays.asList(arrInt);
System.out.println("parallelSetAll执行时间:" + (endTime - startTime) + "ms");
//List转数组的第一种方法
startTime = System.currentTimeMillis();
//List#toArray返回的默认类型是Object类型,因此需要进行类型转换
//这样可能会抛出java.lang.ClassCastException
//Integer[] aimArrIntOne = (Integer[]) integerList.toArray();
//更推荐这样使用
Integer[] aimArrIntOne = integerList.toArray(new Integer[integerList.size()]);
endTime = System.currentTimeMillis();
System.out.println("使用List#toArray时List转数组执行时间:" + (endTime - startTime) + "ms");
}
}
/*
out:
parallelSetAll执行时间:44ms
使用List#toArray时List转数组执行时间:1ms
*/
``