系列文章:
写给大忙人看的 Java SE 8练习题(一)
写给大忙人看的 Java SE 8练习题(二)
写给大忙人看的 Java SE 8练习题(五)
代码地址:
题目:
1、编写一个程序,跟踪一组线程中最长的字符串。你可以使用一个 AtomicReference 和一个合适的累加器。
public static void main(String[] args) {
String[] list = new String[] {
"djkfs",
"weriew",
"vcnxbm",
"sdnwqer",
"odrteworoe",
"dfowe[er[w",
"pqwrj3221rmgbg,",
"owieroiewtw",
"ropweiotyer",
"oidshfdslfd"
};
AtomicReference<String> atomicReference = new AtomicReference<String>();
BinaryOperator<String> operator = (first, second) -> {
try {
return first.length() > second.length() ? first : second;
} catch (Exception e) {
return first != null ? first : second;
}
};
ExecutorService pools = Executors.newCachedThreadPool();
Arrays.stream(list).forEach(item -> {
pools.submit(() -> {
atomicReference.accumulateAndGet(item, operator);
});
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("the longest world is " + atomicReference.get());
}
2、LongAdder 对生成一个递增的 ID 序列有帮助吗?请说明原因。
没有帮助。LongAdder 是针对高并发场景而设计的,每个线程的数据存放于数组不同的下标位置,当需要总数时才调用 sum 接口计算结果,即 LongAdder 并不关心中间过程的结果。如果生成一个递增的 ID 序列,意味着每次加法都必须得到结果,那么就失去了 LongAdder 的意义。
3、生成 1000 个线程,每个线程会将计数器累加 100000 次,比较使用 AtomicLong 和 LongAdder 所获得的性能。
private static AtomicLong atomicLong = new AtomicLong();
private static LongAdder longAdder = new LongAdder();
public static void main(String[] args) {
BiConsumer<String, Runnable> biConsumer = (item, runnable) -> {
long begin = System.currentTimeMillis();
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
pool.submit(() -> {
for (int j = 0; j < 100000; j++) {
runnable.run();
}
});
}
long end = System.currentTimeMillis();
System.out.println("item = " + item + ": " + (end - begin));
};
testAtomicLong(biConsumer);
testLongAddder(biConsumer);
}
private static void testAtomicLong(BiConsumer<String, Runnable> item) {
item.accept("testAtomicLong", () -> atomicLong.incrementAndGet());
}
private static void testLongAddder(BiConsumer<String, Runnable> item) {
item.accept("testLongAddder", () -> longAdder.add(1));
}
4、使用 LongAccumulator 来计算累加元素的最大值或最小值。
public static void main(String[] args) throws InterruptedException {
long[] list = new long[] {
1,
32453465,
65756,
3432,
65875,
2343245,
56765,
24354,
43654,
76,
8978,
43288888888883l,
9876666666666l
};
LongAccumulator longAccumulator = new LongAccumulator(Math::max, 0);
ExecutorService executorService = Executors.newCachedThreadPool();
Arrays.stream(list).forEach(item -> {
executorService.submit(() -> {
longAccumulator.accumulate(item);
});
});
Thread.sleep(100);
System.out.println(longAccumulator.get());
}
5、编写一个应用程序,使用多个线程来读取一组文件中的所有单词。使用 ConcurrentHashMap<String,Set< File>> 对象来跟踪每个单词都出现在哪些文件中。使用 merge 方法来更新 map。
private static ExecutorService pool = Executors.newCachedThreadPool();
private static ConcurrentHashMap<String, Set<File>> map = new ConcurrentHashMap<String, Set<File>>();
public static void main(String[] args) throws InterruptedException {
getDirectory(new File("E:\\Documents\\Downloads\\SampleVideoPlayer"));
Thread.sleep(5000);
map.forEach((first, second) -> {
second.stream().forEach(item -> {
System.out.println("key = " + first + ", value = " + item
.getName());
});
});
System.out.println(map.size());
}
private static void getDirectory(File parent) {
Optional.of(parent).ifPresent(item -> {
if (item.isDirectory()) {
File[] childs = item.listFiles();
Arrays.stream(childs).forEach(child -> getDirectory(child));
} else if (item.isFile()) {
getFile(item);
}
});
}
private static void getFile(File parent) {
pool.submit(() -> {
Optional.of(parent)
.ifPresent(item -> {
if (item.isDirectory()) {
return;
}
try {
String content = new String(Files
.readAllBytes(Paths.get(item
.getCanonicalPath())), StandardCharsets.UTF_8);
String[] contents = content.split("[\\P{L}]+");
Arrays.stream(contents)
.forEach(s -> {
map.merge(s, new HashSet<File>(), (first, second) -> {
second.add(item);
first.addAll(second);
return first;
});
});
} catch (Exception e) {
e.printStackTrace();
}
});
});
}
6、重复上一个练习,但是这一次请使用 computeIfAbsent。这种方式的好处是什么?
不知道有什么好处。但使用这种方法,Set 里面只有一个值。
private static ExecutorService pool = Executors.newCachedThreadPool();
private static ConcurrentHashMap<String, Set<File>> map = new ConcurrentHashMap<String, Set<File>>();
public static void main(String[] args) throws InterruptedException {
getDirectory(new File("E:\\Documents\\Downloads\\SampleVideoPlayer"));
Thread.sleep(5000);
map.forEach((first, second) -> {
second.stream().forEach(item -> {
System.out.println("key = " + first + ", value = " + item
.getName());
});
});
System.out.println(map.size());
}
private static void getDirectory(File parent) {
Optional.of(parent).ifPresent(item -> {
if (item.isDirectory()) {
File[] childs = item.listFiles();
Arrays.stream(childs).forEach(child -> getDirectory(child));
} else if (item.isFile()) {
getFile(item);
}
});
}
private static void getFile(File parent) {
pool.submit(() -> {
Optional.of(parent)
.ifPresent(item -> {
if (item.isDirectory()) {
return;
}
try {
String content = new String(Files
.readAllBytes(Paths.get(item
.getCanonicalPath())), StandardCharsets.UTF_8);
String[] contents = content.split("[\\P{L}]+");
Arrays.stream(contents)
.forEach(s -> {
map.computeIfAbsent(s, key -> {
Set<File> result = new HashSet<File>();
result.add(item);
return result;
});
});
} catch (Exception e) {
e.printStackTrace();
}
});
});
}
7、在一个 ConcurrentHashMap<String,Long> 中,找到含有最大值的键(可以自由发挥)。提示:reduceEntries。
public static void main(String[] args) {
ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<String, Long>();
map.put("dsfhk", 3245l);
map.put("dsfhfdgklk", 567l);
map.put("dsfhdfgfdk", 123l);
map.put("dsfjhkhlwerhk", 7654l);
map.put("dswerwefhk", 2345l);
map.put("dsfhkjhklk", 9976l);
map.put("dsfcxzcxchk", 6786l);
map.put("dwtiuertyusfhk", 234l);
map.put("dsfhwerwerk", 067554l);
map.put("dsdfgdfhk", 905433l);
map.put("dsfzcxzvchk", 978032421111111113l);
map.put("dsvczfhk", 5679343l);
map.put("dsxvczzhk", 6587670l);
map.put("dsasafhk", 890543l);
map.put("dsfasdhk", 324765l);
System.out.println(max(map));
}
private static Map.Entry<String, Long> max(ConcurrentHashMap<String, Long> map) {
return map.reduceEntries(5, item -> {
return item;
}, (first, second) -> {
return first.getValue() > second.getValue() ? first : second;
});
}
8、在你的计算机上使用多大的数组才能让
Arrays.parallelSort()
的运算速度比Arrays.sort()
更快。
在我的机器中,Arrays.sort() 一直比 Arrays.parallelSort() 快。
public static void main(String[] args) {
int[] a = new int[100000000];
for (int i = 0; i < a.length; i++) {
a[i] = 100000000 - i;
}
long begin = System.currentTimeMillis();
Arrays.sort(a);
// Arrays.parallelSort(a);
long end = System.currentTimeMillis();
System.out.println("time = " + (end - begin));
}
9、你可以使用 parallelPrefix 方法来并行计算斐波那契数字。我们假设第 n 个斐波那契数字是 F n F^n Fn左上角的系数,其中 F = [ 1 1 1 0 ] F = \begin{bmatrix} {1}&{1}\\ {1}&{0}\\ \end{bmatrix} F=[1110]构造一个 2 * 2 矩阵的数组。定义一个含有乘法方法的 Matrix 类,使用 parallelSetAll 方法来构造矩阵数组,并使用 parallelPrefix 方法将它们相乘起来。
public static void main(String[] args) {
Matrix[] result = new Matrix[10];
Arrays.parallelSetAll(result, i -> new Matrix());
Matrix.parallelPrefix(result);
// 注意
// |f(n+1), f(n)|
// |f(n), f(n-1)|
System.out.println("1");
Arrays.stream(result).forEach(item -> {
System.out.println(item.result[0][0]);
});
}
static class Matrix {
long[][] result = new long[][] { { 1, 1 }, { 1, 0 } };
static void parallelPrefix(Matrix[] data) {
Arrays.parallelPrefix(data, (first, second) -> {
second.result[0][0] = second.result[0][0] * first.result[0][0] + second.result[0][1] * first.result[1][0];
second.result[0][1] = second.result[0][0] * first.result[1][0] + second.result[1][0] * first.result[1][1];
second.result[1][0] = second.result[1][0] * first.result[0][0] + second.result[1][1] * first.result[1][0];
second.result[1][1] = second.result[1][0] * first.result[0][1] + second.result[1][1] * first.result[1][1];
return second;
});
}
}
10、编写一个程序,要求用户提供一个 URL,然后读取该 URL 的 Web 页面,并显示页面中所有的超链接。可以为每个阶段使用一个 CompletableFuture 对象。不要调用 get 方法。为了防止你的程序过早借宿,可以调用下面的函数
ForkJoinPool.commonPool().awaitQuiescence(10,TimeUnit.SECONDS);
不想做。
11、编写一个方法:
public static <T> CompletableFuture<T> repeqt(Supplier<T> action, Predicate<T> until)
,它会异步重复执行 action 函数,知道产生一个可以被 until 函数(同样也是异步的)接受的值为止。你需要使用一个从控制台读取 java.net.PasswordAuthentication 的函数,以及一个模拟验证过程的函数来执行测试,其中验证模拟函数需要先睡眠 1 秒,然后再检查密码是否为“secert”。提示:使用递归。
public static void main(String[] args) {
repeat(() -> {
System.out.println("请输入密码:");
String result = null;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
try {
result = bufferedReader.readLine();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}, item -> {
return "secret".equals(item);
});
}
private static <T> CompletableFuture<T> repeat(Supplier<T> action, Predicate<T> until) {
CompletableFuture<T> result = CompletableFuture.supplyAsync(action);
result.thenAcceptAsync(t -> {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
if (until.test(t)) {
System.out.println("密码正确");
} else {
System.out.println("密码错误,请重试。");
repeat(action, until);
}
});
ForkJoinPool.commonPool().awaitQuiescence(4, TimeUnit.SECONDS);
return result;
}