文章目录

  • 背景
  • 汇总实现方法
  • jdk 集合类型接口的of方法
  • jdk Collections工具类
  • guava collection
  • apache common collections
  • 小结


背景

最近遇到这样一个场景, 接口会提供一个只读的查找表(Map类型),实现类可以在这个默认查找表中根据key查询相关value.笔者用的是java 11,如下代码实现了这个查找表.

public interface Test {
  Map<String, String> DEFAULT_TBL = Map.of(
    "k1", "v1",
    "k2", "v2"
  );
}

接口中的字段是public final static的.所以这里涉及到final static map 字段的初始化以及添加初始元素的步骤. 如果是在一个类里面实现,我们可以不必一行代码实现.因为可以在类的静态初始化代码块中完成这个过程.

public class TestClass {

  public final static Map<String, String> DEFAULT_TBL;

  static {
    DEFAULT_TBL = Map.of(
      "k1", "v1",
      "k2", "v2"
    );
  }
}

如果在接口里用静态初始化代码块则直接报错Not allowed in interface

public interface Test {
  Map<String, String> DEFAULT_TBL;
  // 接口里面没静态初始化代码块这种语法
  static  {
    DEFAULT_TBL = Map.of("k1", "v1");
  }
}

所以在接口里面要实现这个过程就是置顶的那个方法,一行代码搞定.
但是这里有一个值得注意的细节, Map.of方法是java 9引入

/**
     * Returns an unmodifiable map containing two mappings.
     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
     *
     * @param <K> the {@code Map}'s key type
     * @param <V> the {@code Map}'s value type
     * @param k1 the first mapping's key
     * @param v1 the first mapping's value
     * @param k2 the second mapping's key
     * @param v2 the second mapping's value
     * @return a {@code Map} containing the specified mappings
     * @throws IllegalArgumentException if the keys are duplicates
     * @throws NullPointerException if any key or value is {@code null}
     *
     * @since 9
     */
    static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2) {
        return new ImmutableCollections.MapN<>(k1, v1, k2, v2);
    }

如果你还是使用的java 8那么应该是这样一个实现

public interface Test {
  Map<String, String> DEFAULT_TBL = new HashMap<String, String>() {
	  {
	    put("k1", "v1");
	    put("k2", "v2");
	  }
  };
}

这里使用匿名类初始化代码块实现给map里面添加默认的元素.
至此这个问题就解决了, java 8 以上使用Map.of进行默认值添加, java 8 可使用匿名类以及初始代码块实现默认值添加默认元素.

汇总实现方法

上面只是解决Map类型集合声明即初始化添加默认元素,那么发散一下,java里面的集合类型包括List,Queue,Map以及Set四种类型.那么针对这些集合类型jdk有提供哪些便捷方法去一步实现初始化添加默认元素呢?同时apache commons 以及google guava这两款重要的工具集中是否也提供了便捷方法方便我们完成本文提到的这类task呢,下面总结一下

jdk 集合类型接口的of方法

List<String> t1 = List.of("a", "b");
t1.stream().forEach(System.out::println);
// Immutable 不可以新增或者删除以及更改元素
// t1.add("c");
// t1.remove("a");
// t1.set(0, "c");

// 可作为集合类型构造器入参,构建可变类型
ArrayList<String> mutable = new ArrayList<>(t1);
mutable.add("c");
mutable.stream().forEach(System.out::println);

// 不可传递相同的值 方法自身不带去重功能
Set<String> t2 = Set.of("1", "2");
t2.stream().forEach(System.out::println);

Map<String, String> t3 = Map.of("k1", "v1", "k2", "v2");
// 不可更改
// t3.put("k1", "update");
t3.entrySet().stream().forEach(System.out::println);

of 方法创建的集合类型都是不可变(Immutable)的. 不能增加,减少以及更改集合元素,但是可以将其作为构造器参数构建通用的可变类型.注意Queue没有of方法.
其中还有一个特例Arrays工具类提供的非常常用的方法:Arrays.asList(T…) 将数组转换为列表,这个方法产生的列表同样是Immutable的.

jdk Collections工具类

Collections工具类在此处也需要提及一下. 也提供了一些便捷静态方法方便创建各种集合类型对象. 但是它focus的点不是创建后立即添加初始元素.所以看它的跟创建对象相关的静态方法基本上都是创建元素为空的对象.

guava collection

guava collection 是jdk collection的扩展, 它提供了丰富的集合相关的api看来帮助开发.真对每一种集合类型都提供了相应的工具类(比如Sets,Lists,Queues等,命名规则: {集合类型}s).

// 可传入重复值不报错
Set<Integer> set = Sets.newHashSet(1, 2, 2, 3);
// 生成的是mutable set 可以删减元素
set.add(3);
set.add(4);
set.stream().forEach(System.out::println);

// immutable to mutable
Map<String, String> map = Maps.newHashMap(Map.of("k1", "v1"));
map.put("k2", "v2");
map.entrySet().stream().forEach(System.out::println);

List<Integer> list = Lists.newArrayList(1, 2, 3);
list.add(6);
list.stream().forEach(System.out::println);

且这些便捷方法能用于创建可变的集合对象.因此可以用jdk提供的集合接口(Map, Set,List)创建不可变的对象然后传递给guava集合类函数即可一行代码得到有初始元素的可变集合对象. guava 本身功能极其强大,我会在后面专门详解guava模块,此处就不再赘述了.

apache common collections

apache common collections common collections 提供了一些集合工具类(xxxUtils 命名规则: {集合类型}Utils) 来扩展jdk集合相关的api.当然它的关注点也不是创建集合对象的时候就添加初始化元素.后面我也会专门讲解一下apache commons相关的模块,此处也不再赘述.

List<Integer> list = ListUtils.fixedSizeList(List.of(1, 2, 3));
list.stream().forEach(System.out::println);

Map<String, String> map = MapUtils.fixedSizeMap(Map.of("k1", "v1"));
map.entrySet().stream().forEach(System.out::println);

小结

如果需要创建的集合对象是immutable的,且创建时就添加一些初始元素,使用jdk本身的集合接口即可实现.如果要求创建后的对象是mutable的那么可以结合guava包和jdk来实现,或者用匿名类和初始化代码块来实现.

public final static Map<String, String> IMMUTABLE_TBL = Map.of(
    "k1", "v1"
  );

  public final static Map<String, String> MUTABLE_TBL_A = Maps.newHashMap(Map.of(
    "k1", "v1"
  ));

  public final static Map<String, String> MUTABLE_TBL_B = new HashMap<>() {
    {
      put("k1", "v1");
    }
  };

  public static void main(String[] args) {
    System.out.println("---------------------");
    IMMUTABLE_TBL.entrySet().stream().forEach(System.out::println);
    System.out.println("---------------------");
    MUTABLE_TBL_A.put("k2", "v2");
    MUTABLE_TBL_A.entrySet().stream().forEach(System.out::println);
    System.out.println("---------------------");
    MUTABLE_TBL_B.put("k2", "v2");
    MUTABLE_TBL_B.entrySet().stream().forEach(System.out::println);
  }

result:

---------------------
k1=v1
---------------------
k1=v1
k2=v2
---------------------
k1=v1
k2=v2