关于Java容器的一些便捷写法

前言

本文使用jdk17,如果你使用的是jdk8,那么本文可能会有部分对你没用。
如有更简洁的写法希望大家能讨论讨论学习学习

容器创建

基本容器

才学Java时都是先创建好一个空的List然后一个一个add,特别麻烦,也许老版本可以使用Arrays.asList来创建,但是创建出来的不是java.util包下的而是java.util.Arrays包下的,有时使用可能会出点意想不到的bug。
在这里插入图片描述
此时在外面包一层java.util.ArrayList就好了

public class Main {
    public static void main(String[] args) {
        var list = new ArrayList<>(Arrays.asList(1, 2, 3));
    }
}

那么有没有MapsSets 来方便我们创建呢,好像没有😂,不过咱们的Set有接收List的构造函数,就像这样。

public class Main {
    public static void main(String[] args) {
        var list = new ArrayList<>(Arrays.asList(1, 2, 3, 2));
        var set = new HashSet<>(list);
        // 或者直接写成一行
        var set1 = new HashSet<>(Arrays.asList(1, 2, 3, 2));
    }
}

至于Map嘛,只能上点java9的新特性了。
在java9中,List, Map, Set 都有个叫of的方法,可以传入多个元素直接构建。但是默认创建的都是不可变的,如果想要可变的可以在外面包一层我们以前常用的即可

public class Main {
    public static void main(String[] args) {
        var list = List.of(1, 2, 3, 4, 5);
        var mutableList = new ArrayList<>(List.of(1, 2, 3, 4, 5));
        var map = Map.of("k1", "v1", "k2", "v2");
        var mutableMap = new HashMap<>(Map.of("k1", "v1", "k2", "v2"));
        var set = Set.of(1, 1, 2, 3);
        var mutableSet = new HashSet<>(Set.of(1, 1, 2, 3));
    }
}

递增数组

需求:创建x~y的递增序列,比如 0 到 10
这在写并查集时创建parent数组以及下标排序时会有这个需求
一般的写法是创建一个数组,然后使用一个for循环一个一个添加,虽说可以,但对我来说感觉不够优雅,我们可以使用java8的stream流来一行搞定。

import java.util.*;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
//        IntStream.range(0, 11)  // range是不包含末尾,rangeClosed包含末尾
        int[] p = IntStream.rangeClosed(0, 10).toArray();
        System.out.println(Arrays.toString(p));
        //~out: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    }
}

递减数组

如果想要递减数组怎么办(虽然不知道哪儿有这种需求😂),可以使用map映射

import java.util.*;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int x = 0, y = 11;
        int[] p = IntStream.range(x, y).map(i -> y - i).toArray();
        System.out.println(Arrays.toString(p));
        //~out: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    }
}

使用map之后你还可以创建等比数列,我这里先实现一下等差数列,等比数列可以自己照着摸索摸索。

等差数列

创建一个首项为1,公差为5的等差数列的前十项

public class Main {
    public static void main(String[] args) {
        int a0 = 1, d = 5, len = 10;
        int[] a = IntStream.range(0, len).map(i -> a0 + d * i).toArray();
        System.out.println(Arrays.toString(a));
        //~out:[1, 6, 11, 16, 21, 26, 31, 36, 41, 46]
    }
}

随机数组

生成10~20以内的随机数
使用IntStream.generate会生成一个无限流,使用limit来指定取出流的前len个,这个limit就像mysql的limit一样。

import java.util.*;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int len = 10;
        var random = new Random();
        int[] a = IntStream.generate(() -> random.nextInt(10, 21))
                .limit(len)
                .toArray();
        System.out.println(Arrays.toString(a));
        //~out: [15, 15, 14, 12, 12, 10, 13, 20, 17, 11]
    }
}

Map优化

一步步优化词频统计

这里有一段文本

Look at the sky, I'm still here
I'll be alive next year
I can make something good, oh
Something good
Look at the sky, I'm still here
I'll be alive next year
I can make something good, oh
Something good

需求是统计每个单词出现的次数。首先是最朴素的写法。

import java.util.*;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        String s = "Look at the sky, I'm still here\n" +
                "I'll be alive next year\n" +
                "I can make something good, oh\n" +
                "Something good\n" +
                "Look at the sky, I'm still here\n" +
                "I'll be alive next year\n" +
                "I can make something good, oh\n" +
                "Something good";
        var cnt = new HashMap<String, Integer>();
        String[] words = s.split("\\PL+");
        for (String word: words) {
            if (cnt.containsKey(word)) {
                cnt.put(word, cnt.get(word) + 1);
            } else {
                cnt.put(word, 1);
            }
        }
        System.out.println(cnt);
        //~out:
        // {sky=2, here=2, ll=2, next=2, still=2, be=2, alive=2, year=2, I=6, m=2, good=4, something=2, the=2, can=2, at=2, oh=2, Look=2, Something=2, make=2}
    }
}

s.split("\\PL+")的作用是将字符串按照非字符进行分割(来自Java核心技术卷二)
这个if else判断显得有点繁琐,这里优化方式有点多。
把if else优化成下面两行,

cnt.putIfAbsent(word, 0);
cnt.put(word, cnt.get(word) + 1);

putIfAbsent 是键不存在则put

优化成一行。

cnt.put(word, cnt.getOrDefault(word, 0) + 1);

这就完了?这一行还不够优雅。更好的方法是使用merge

cnt.merge(word, 1, Integer::sum);

这句的意思是跟键原有的值按照第三个参数进行合并,如果键不存在则会与值类型的默认值进行操作,比如Integer默认值就是0.

然后我们就把这个if else四合一了,就这样。

import java.util.*;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        String s = \...\;
        var cnt = new HashMap<String, Integer>();
        String[] words = s.split("\\PL+");
        for (String word: words) {
            cnt.merge(word, 1, Integer::sum);
        }
        System.out.println(cnt);
    }
}

优雅。
当然,这还不是最终版本,我的终极目标是一行流。

import java.util.*;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        String s = \...\;
        Map<String, Long> cnt = Arrays.stream(s.split("\\PL+")).collect(Collectors.groupingBy(k -> k, Collectors.counting()));
        System.out.println(cnt);
    }
}

至此整个词频统计的核心变为一行。

其他分组需求

我想将下标按照数组中的元素值进行分组,即每个值在哪些下标出现过。
这也可以使用一行流实现。

import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        var a = List.of(1, 1, 4, 5, 1, 4);
        Map<Integer, List<Integer>> map = IntStream.range(0, a.size()).boxed().collect(Collectors.groupingBy(a::get));
        System.out.println(map);
        //~out: {1=[0, 1, 4], 4=[2, 5], 5=[3]}
    }
}

这个Collectors的功能挺强大的,可以自己摸索一下。

最后

等我发现有什么更简单的写法时再回来更新。

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bloom__❀;

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值