Java实战-不可变容器、列表遍历删除、HashMap遍历删除、巧用函数方法实现二维数组遍历

发布时间 2023-05-31 15:32:26作者: 霸道流氓

场景

Java中不可变容器的使用

Java工具库Guava的不可变集合和新集合类型Multiset、Multimap、BiMap、RangeSet、RangeMap等的使用示例:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/127900306

上面讲了在guava中的新集合类型,其中就包含不可变集合。

其应用场景

如果是应用内的接口方法,容器传参,返回容器时,尽量不要使用不可变容器,因为没法保证别人拿到你的返回容器后,

会对它进行什么操作。

如果对外提供返回结果,特别是null的场景,使用不可变的空容器优于返回null

不可变容器,用于全局公用资源,共享配置参数;多线程的数据传递时,属于比较合适的场景。

1、jdk原生提供了一些不可变容器,最大的特点就是不支持添加、删除、修改容器内的值。

        Map<Object, Object> objectObjectMap = Collections.emptyMap();
        List<Object> objects = Collections.emptyList();
        Set<Object> objects1 = Collections.emptySet();

上面是常用的三个,通常当一个方式的返回结果定义为容器类型时,为了避免空指针异常,在返回空容器时,

会如此使用。除了上面这几个,还有

        List<Integer> integerList = Collections.unmodifiableList(Arrays.asList(1, 2, 3));
        //上面创建的List,就不支持set/remove等修改操作
        //Collections.unmodifiableMap();
        //Collections.unmodifiableSet();

2、Guava中不可变容器

        ImmutableList<Integer> of = ImmutableList.of(1, 2, 3);
        ImmutableSet<Integer> of1 = ImmutableSet.of(1, 2, 3);
        ImmutableMap<String, Integer> of2 = ImmutableMap.of("badao", 11, "aa", 22);

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi

Java中列表遍历删除的方式

1、迭代器删除

        List<String> list = new ArrayList<String>(){{
            add("badao");
            add("de");
            add("cheng");
            add("xv");
            add("yuan");
        }};

        //1、迭代删除
        Iterator<String> iterator = list.iterator();
        while(iterator.hasNext()){
            String tmp = iterator.next();
            if(tmp.contains("de")){
                iterator.remove();
            }
        }
        System.out.println(list);

2、JDK8+ 流的方式实现列表遍历删除

        list.removeIf(s -> s.contains("ba"));
        System.out.println(list);

Java中HashMap遍历删除的方式

1、迭代器遍历

ap的迭代删除,和list,set不太一样,不能直接获取Iterator对象,提供的删除方法也是单个的,

根据key进行删除,如果需要将map中满足某些条件的元素删除掉,它的entrySet有,可以通过它来实现遍历删除

        Map<String,Integer> map = new HashMap<String,Integer>(){{
            put("a",1);
            put("b",3);
            put("c",4);
            put("d",6);
            put("e",8);
        }};

        Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
        Map.Entry<String,Integer> entry;
        while (iterator.hasNext()){
            entry = iterator.next();
            if((entry.getValue() & 1) == 0){
                iterator.remove();
            }
        }
        System.out.println(map);

2、到jdk8以后,针对容器提供了很多简洁的操作方式,迭代删除更简单

        Map<String,Integer> map1 = new HashMap<String,Integer>(){{
            put("a",1);
            put("b",3);
            put("c",4);
            put("d",6);
            put("e",8);
        }};
        map1.entrySet().removeIf(entrySet->(entrySet.getValue() & 1) == 0);
        System.out.println(map1);

Java中巧用函数方法实现二维数组遍历

二维数组遍历,每个元素判断是否为偶数

        int[][] cells = new int[][]{{1,2,3,4},{5,6,7,8},{9,10,11,12}};
        List<Integer> ans = new ArrayList<>();
        for (int i = 0; i < cells.length; i++) {
            for (int j = 0; j < cells[i].length; j++) {
                //&1 与操作,等于0 是偶数,需要用括号包围
                if((cells[i][j] & 1) ==0){
                    ans.add(cells[i][j]);
                }
            }
        }
        System.out.println(ans);

面的实现没啥问题,但是代码深度有三层,当if中再有其他的判定条件,

那么这个代码层级就很容易增加,如果是三维数据,一个遍历就是三层,再加其他逻辑,层数会更多

1、函数方法消减代码层级

定义一个函数方法,输入函数坐标,在这个函数体中执行我们的遍历逻辑即可

java8中常用函数式接口Supplier<T>、Consumer<T>、Function<T,R>、Predicate<T>使用示例:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/128533034

参考上面教程

函数方法直接使用了JDK默认提供的BiConsumer,两个参数,都是int数组下标;无返回值。

BiConsumer函数式接口,表示一个带有两个参数(T,U)且不返回结果的操作。

    public static void scan(int maxX, int maxY, BiConsumer<Integer,Integer> consumer){
        for (int i = 0; i < maxX; i++) {
            for (int j = 0; j < maxY; j++) {
                consumer.accept(i,j);
            }
        }
    }

那么上面的例子可以修改为

        List<Integer> ans2 = new ArrayList<>();
        scan(cells.length,cells[0].length,(i, j) -> {
            if((cells[i][j] & 1) == 0)
                ans2.add(cells[i][j]);
        });
        System.out.println(ans2);

相比于前面的,貌似只少了一层,但是当数组变成三维、四维以后,这个改动的写法层级不变

2、遍历中return支持

前面的对于正常的遍历没啥问题,但是当我们在遍历过程中,遇到某个条件直接返回,能支持吗

,比如遍历一个二维数组,判断其中是否有偶数,如果有则返回坐标。

可以在执行逻辑中添加一个额外的返回值,用于标记是否中断循环直接返回。

定义一个函数方法,接收循环的下标和返回值

当一个方法需要返回两个及以上字段时,我们一般会封装成一个临时对象返回,现在有了Pair就可以返回两个字段

首先自定义函数接口ScanProcess

import org.apache.commons.lang3.tuple.ImmutablePair;

@FunctionalInterface
public interface ScanProcess<T> {
    ImmutablePair<Boolean,T> accept(int i, int j);
}

循环通用方法就可以相应的改成

    public static <T> T scanReturn(int x,int y,ScanProcess<T> func){
        for (int i = 0; i < x; i++) {
            for (int j = 0; j < y; j++) {
                ImmutablePair<Boolean,T> ans = func.accept(i,j);
                if(ans != null && ans.left)
                    return ans.right;
            }
        }
        return null;
    }

上面的调用就可以修改为

        int[][] cells3 = new int[][]{{1,2,3,4},{5,6,7,8},{9,10,11,12}};
        String s = scanReturn(cells3.length, cells3[0].length, (i, j) -> {
            if ((cells3[i][j] & 1) == 0) {
                return ImmutablePair.of(true, i + "_" + j);
            }
            return ImmutablePair.of(false, null);
        });
        System.out.println(s);