java核心技术卷I-映射

映射

集是一个集合,它可以快速地查找现有的元素。但是,要查看一个元素, 需要有要查找元素的精确副本。这不是一种非常通用的査找方式。通常, 我们知道某些键的信息,并想要查找与之对应的元素。 映射(map) 数据结构就是为此设计的。映射用来存放键 / 值对。如果提供了键, 就能够查找到值。

基本映射操作

Java 类库为映射提供了两个通用的实现:HashMap 和 TreeMap。这两个类都实现了Map 接口。
散列映射对键进行散列, 树映射用键的整体顺序对元素进行排序, 并将其组织成搜索树。散列或比较函数只能作用于键。与键关联的值不能进行散列或比较。应该选择散列映射还是树映射呢? 与集一样, 散列稍微快一些, 如果不需要按照排列顺序访问键,就最好选择散列。

Map<String, Employee> staff = new HashMap<>();// HashMap implements Map
Employee harry = new Employee('Harry Hacker");
staff.put(”987-98-9996", harry);

每当往映射中添加对象时, 必须同时提供一个键。在这里,键是一个字符串,对应的值是 Employee 对象。要想检索一个对象, 必须使用(因而,必须记住)一个键

String id = "987-98-9996";
e = staff,get(id);// gets harry

如果在映射中没有与给定键对应的信息, get 将返回 null。
null 返回值可能并不方便。有时可以有一个好的默认值, 用作为映射中不存在的键。然后使用 getOrDefault 方法
键必须是唯一的。不能对同一个键存放两个值。如果对同一个键两次调用 put 方法, 第二个值就会取代第一个值。实际上,put 将返回用这个键参数存储的上一个值。
remove 方法用于从映射中删除给定键对应的元素。size 方法用于返回映射中的元素数。要迭代处理映射的键和值, 最容易的方法是使用 forEach 方法。可以提供一个接收键和值的 lambda 表达式。

scores.forEach((k, v) ->
System.out.println("key=" + k + ", value:" + v));

更新映射项

处理映射时的一个难点就是更新映射项。正常情况下,可以得到与一个键关联的原值,完成更新, 再放回更新后的值。不过,必须考虑一个特殊情况, 即键第一次出现。
键第一次出现时,调用get方法会返回 null, 因此会出现一个 NullPointerException 异常。
作为一个简单的补救, 可以使用 getOrDefault 方法:

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

另一种方法是首先调用 putlfAbsent 方法。只有当键原先存在时才会放入一个值。

counts.putlfAbsent(word, 0);
counts.put(word, counts.get(word)+ 1); // Now we know that get will succeed

不过还可以做得更好。merge 方法可以简化这个常见的操作。如果键原先不存在,下面的调用:

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

映射视图

集合框架不认为映射本身是一个集合。(其他数据结构框架认为映射是一个键 / 值对
集合, 或者是由键索引的值集合。) 不过, 可以得到映射的视图(View )—这是实现了Collection 接口或某个子接口的对象。
有 3 种视图: 键集、 值集合(不是一个集) 以及键 / 值对集。键和键 / 值对可以构成一个集, 因为映射中一个键只能有一个副本。 下面的方法:

Set<K> keySet()
Collection<V> values()
Set<Map.Entry<K, V>> entrySet()

需要说明的是, keySet 不是 HashSet 或 TreeSet, 而是实现了 Set 接口的另外某个类的对象。Set 接口扩展了 Collection 接口。因此, 可以像使用集合一样使用 keySet。

Set<String> keys = map.keySet();
for (String key : keys)
{
	do something with key
}

想同时查看键和值,可以通过枚举条目来避免查找值。
f

or (Map.Entry<String, Employee> entry : staff.entrySet())
{
	String k = entry.getKey();
	Employee v = entry.getValue();
	do something with k, v
}

原先这是访问所有映射条目的最高效的方法。如今,只需要使用 forEach 方法

counts.forEach((k,v) -> {
do somethingwith k, v
});

如果在键集视图上调用迭代器的 remove方法, 实际上会从映射中删除这个键和与它关联的值。不过,不能向键集视图增加元素。

链接散列集与映射

LinkedHashSet 和 LinkedHashMap类用来记住插人元素项的顺序。这样就可以避免在散列表中的项从表面上看是随机排列的。当条目插入到表中时,就会并人到双向链表中。
链接散列映射将用访问顺序, 而不是插入顺序, 对映射条目进行迭代。每次调用 get 或put, 受到影响的条目将从当前的位置删除, 并放到条目链表的尾部(只有条目在链表中的位置会受影响, 而散列表中的桶不会受影响。一个条目总位于与键散列码对应的桶中)。要项构造这样一个的散列映射表, 请调用

LinkedHashMap<K, V>(initialCapacity, loadFactor, true)

访问顺序对于实现高速缓存的“ 最近最少使用” 原则十分重要。例如, 可能希望将访问频率高的元素放在内存中, 而访问频率低的元素则从数据库中读取。当在表中找不到元素项且表又已经满时, 可以将迭代器加入到表中, 并将枚举的前几个元素删除掉。这些是近期最少使用的几个元素。
下面的高速缓存可以存放 100 个元素:

Map<K, V> cache = new
LinkedHashMapo(128, 0.75F, true)
{
	protected boolean removeEldestEntry(Map.Entry<K, V> eldest)
	{
		return size() > 100;
	}
}();

另外,还可以对 eldest 条目进行评估,以此决定是否应该将它删除。例如,可以检査与这个条目一起存在的时间戳。

标识散列映射

类 IdentityHashMap 有特殊的作用。在这个类中, 键的散列值不是用 hashCode 函数计算的, 而是用 System.identityHashCode 方法计算的。 这是 Object.hashCode 方法根据对象的内地址来计算散列码时所使用的方式。而且, 在对两个对象进行比较时, IdentityHashMap 类使用 ==, 而不使用 equals。
也就是说, 不同的键对象, 即使内容相同, 也被视为是不同的对象。 在实现对象遍历算法(如对象串行化)时, 这个类非常有用, 可以用来跟踪每个对象的遍历状况。

该资源包含源代码 易看易懂 其实就是一发射机制 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提出很引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。其中LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基于反射机制的语言。最近,反射机制也被应用到了视窗系统、操作系统和文件系统中。 反射本身并不是一个新概念,它可能会使我们联想到光学中的反射概念,尽管计算机科学赋予了反射概念新的含义,但是,从现象上来说,它们确实有某些相通之处,这些有助于我们的理解。在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。可以看出,同一般的反射概念相比,计算机科学领域的反射不单单指反射本身,还包括对反射结果所采取的措施。所有采用反射机制的系统(即反射系统)都希望使系统的实现更开放。可以说,实现了反射机制的系统都具有开放性,但具有开放性的系统并不一定采用了反射机制,开放性是反射系统的必要条件。一般来说,反射系统除了满足开放性条件外还必须满足原因连接(Causally-connected)。所谓原因连接是指对反射系统自描述的改变能够立即反映到系统底层的实际状态和行为上的情况,反之亦然。开放性和原因连接是反射系统的两大基本要素。13700863760 Java中,反射是一种强大的工具。它使您能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代表链接。反射允许我们在编写与执行时,使我们的程序代码能够接入装载到JVM中的类的内部信息,而不是源代码中选定的类协作的代码。这使反射成为构建灵活的应用的主要工具。但需注意的是:如果使用不当,反射的成本很高。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

局外人一枚

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

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

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

打赏作者

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

抵扣说明:

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

余额充值