Immutable类介绍和guava之ImmutableMap使用
1. 什么是Immutable(不可变对象)
按照Effective Java的说明,需要满足下面几条规则:
- 保证类不能被继承 - 为了避免其继承的类进行mutable的操作
- 移除所有setter/update等修改对象实例的操作
- 保证所有的field是private和final的
immutable Objects就是那些一旦被创建,它们的状态就不能被改变的Objects,每次对他们的改变都是产生了新的immutable的对象,
而mutable Objects就是那些创建后,状态可以被改变的Objects.
举个例子:
String和StringBuilder:
String是immutable的,每次对于String对象的修改都将产生一个新的String对象,而原来的对象保持不变;
StringBuilder是mutable,因为每次对于它的对象的修改都作用于该对象本身,并没有产生新的对象;
但有的时候String的immutable特性也会引起安全问题,这就是密码应该存放在字符数组中而不是String中的原因!
immutable objects 比传统的mutable对象在多线程应用中更具有优势,它不仅能够保证对象的状态不被改变,而且还可以不使用锁机制就能被其他线程共享。
注意:如果 immutable 集合中包含了 mutable 元素,比如 mutable 集合,那么该 immutable 集合在这种情况下是非线程安全的。
实际上JDK本身就自带了一些immutable类,比如String,Integer以及其他包装类。
为什么说String是immutable的呢?比如:java.lang.String 的trim,uppercase,substring等方法,它们返回的都是新的String对象,而并不是直接修改原来的对象。
1.1 如何在Java中写出Immutable的类?
要写出这样的类,需要遵循以下几个原则:
1)immutable对象的状态在创建之后就不能发生改变,任何对它的改变都应该产生一个新的对象。
2)Immutable类的所有的属性都应该是final的。
3)对象必须被正确的创建,比如:对象引用在对象创建过程中不能泄露(leak)。
4)对象应该是final的,以此来限制子类继承父类,以避免子类改变了父类的immutable特性。
5)如果类中包含mutable类对象,那么返回给客户端的时候,返回该对象的一个拷贝,而不是该对象本身(该条可以归为第一条中的一个特例)
当然不完全遵守上面的原则也能够创建immutable的类,比如String的hashcode就不是final的,但它能保证每次调用它的值都是一致的,无论你多少次计算这个值,它都是一致的,因为这些值的是通过计算final的属性得来的!
有时候你要实现的immutable类中可能包含mutable的类,比如java.util.Date,尽管你将其设置成了final的,但是它的值还是可以被修改的,为了避免这个问题,我们建议返回给用户该对象的一个拷贝,这也是Java的最佳实践之一。
1.2 使用Immutable类的好处:
1)Immutable对象是线程安全的,可以不用被synchronize就在并发环境中共享
2)Immutable对象简化了程序开发,因为它无需使用额外的锁机制就可以在线程间共享
3)Immutable对象提高了程序的性能,因为它减少了synchroinzed的使用
4)Immutable对象是可以被重复使用的,你可以将它们缓存起来重复使用,就像字符串字面量和整型数字一样。你可以使用静态工厂方法来提供类似于valueOf()这样的方法,它可以从缓存中返回一个已经存在的Immutable对象,而不是重新创建一个。
immutable也有一个缺点就是会制造大量垃圾,由于他们不能被重用而且对于它们的使用就是”用“然后”扔“,字符串就是一个典型的例子,它会创造很多的垃圾,给垃圾收集带来很大的麻烦。当然这只是个极端的例子,合理的使用immutable对象会创造很大的价值
2. jdk9以上自带的不可变集合方法
使用
- 使用 List.of 工厂方法创建 immutable list,list 中的元素是有序的,允许重复,但不允许为 null。
- 使用 Set.of 工厂方法创建 immutable set,set 中的元素是无序的,且不重复,不允许为 null。
- 使用 Map.of 和 Map.ofEntries 方法创建 immutable map,map 中的 key 不允许重复,key 与 value 都不能为 null。
//----------------------- List ------------------------
// In JDK 8:
List<String> stringList = Arrays.asList("a", "b", "c");
stringList = Collections.unmodifiableList(stringList);
// In JDK 9:
List stringList = List.of("a", "b", "c");
//----------------------- Set ------------------------
// In JDK 8:
Set<String> stringSet = new HashSet<>(Arrays.asList("a", "b", "c"));
stringSet = Collections.unmodifiableSet(stringSet);
// In JDK 9:
Set<String> stringSet = Set.of("a", "b", "c");
//----------------------- Map ------------------------
// In JDK 8:
Map<String, Integer> stringMap = new HashMap<String, Integer>();
stringMap.put("a", 1);
stringMap.put("b", 2);
stringMap.put("c", 3);
stringMap = Collections.unmodifiableMap(stringMap);
// In JDK 9:
Map stringMap = Map.of("a", 1, "b", 2, "c", 3);
Map <Integer, String> friendMap = Map.ofEntries(
entry(1, "Tom"),
entry(2, "Dick"),
entry(3, "Harry"),
...
entry(99, "Mathilde"));
拷贝集合:
- JDK10 之后,可以使用 copyOf 方法来创建集合的拷贝。
- 如果原 list 为 mutable,copyOf 方法会创建一个 immutable 集合,然后拷贝原 list 中的元素,之后,对原 list 的编辑和更新,不会影响 immutable list。如果原 list 为 immutable,copyOf 方法直接返回该 list 的引用。
List<Item> list= new ArrayList<>();
list.addAll(getItemsFromSomewhere());
list.addAll(getItemsFromElsewhere());
list.addAll(getItemsFromYetAnotherPlace());
List<Item> snapshot = List.copyOf(list);
使用 Streams 创建 immutable 集合
JDK 10 之后,java.util.stream.Collectors 类可根据 stream 元素创建 immutable 集合:
- Collectors.toUnmodifiableList()
- Collectors.toUnmodifiableSet()
- Collectors.toUnmodifiableMap(keyMapper, valueMapper)
- Collectors.toUnmodifiableMap(keyMapper, valueMapper, mergeFunction)
Set<Item> immutableSet = sourceCollection.stream()
.map(...)
.collect(Collectors.toUnmodifiableSet());
3. guava之ImmutableMap使用
- 对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象
- 线程安全的:immutable对象在多线程下安全,没有竞态条件
- 不需要支持可变性, 可以尽量节省空间和时间的开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis)
- 可以被使用为一个常量,并且期望在未来也是保持不变的,immutable对象可以很自然地用作常量,因为它们天生就是不可变的对于immutable对象的运用来说,它是一个很好的防御编程(defensive programming)的技术实践。
3.1 使用方法
初始化
// 初始化不可变map
Map<String,Object> immutableMap = new ImmutableMap.Builder<String,Object>().build();
// 或者
ImmutableMap<String, Object> immutableMap1 = new ImmutableMap.Builder<String, Object>()
.put("name", "immutableMap")
.put("describe", "immutable map")
.build();
// 或者
ImmutableMap.Builder<String, Object> mapBuilder = ImmutableMap.builder();
mapBuilder.put("one","1");
mapBuilder.put("two","2");
mapBuilder.put("three","3");
Map<String, Object> immutableMap2 = mapBuilder.build();
// 或者
ImmutableMap<String, Object> immutableMap0 =
ImmutableMap.<String, Object>builder()
.put("name", "immutableMap")
.put("describe", "immutable map")
.build();
// 不可变list用法
List<String> immutableList = new ImmutableList.Builder<String>().build();
// 或者
ImmutableList<Object> immutableList1 = new ImmutableList.Builder<>()
.add("name")
.add(20)
.add("不可变集合")
.build();
// 或者
ImmutableList.<Object>builder()
.add("name")
.add(20)
.add("不可变集合")
.build();
// 不可变set
Set immutableSet = new ImmutableSet.Builder<>().build();
// 或者
Set immutableSet1 = new ImmutableSet.Builder<>()
.add("name")
.add(20)
.add("不可变集合")
.build();
// 或者
ImmutableSet.<Object>builder()
.add("name")
.add(20)
.add("不可变集合")
.build();
3.2 ImmutableMap的使用场景:
适合
1、确定性的配置, 比如根据不同的key值得到不同的请求url
2、写单元测试
不适合
1、key, value为未知参数, 可能有null产生的情况
3.3 具体使用方法
// 1. 使用copyOf()方法: 它不能直接修改,但是可以改变其内部可变的Map:
Map<String, Object> buildMap = buildMap();
ImmutableMap<String, Object> immutableMapCopy = ImmutableMap.copyOf(buildMap);
// 2. 使用builder()方法
ImmutableMap immutableMapBuilder = ImmutableMap.<String, Object>builder()
.putAll(buildMap)
.put("Costa Rica", "North America")
.build();
// 3. 使用of()
// 我们可以使用ImmutableMap.of() 方法创建一个不可变的Map,其中包含动态提供的一组条目。它最多支持五个键/值对:
Map map = ImmutableMap.<String, Object>of(
"genName", "暂无",
"falseNo", 123,
"discernNo", "discernNo",
"status", 1,
"reviewStatus", 0);
private Map<String, Object> buildMap() {
Map<String, Object> result = new HashMap<String, Object>();
result.put("id", 1);
result.put("type", "type");
result.put("key", "key");
result.put("value", "value");
result.put("status", 0);
result.put("createDate", new Date());
result.put("updateDate", new Date());
result.put("sort", "亚瑟");
return result;
}
欢迎一起来学习和指导,谢谢关注!
本文来自博客园,作者:xiexie0812,转载请注明原文链接:https://www.cnblogs.com/mask-xiexie/p/16223699.html