关于map:map的两种遍历方式是什么

学了Map后,咱们都晓得Map有两种遍历形式,keySet遍历个entrySet遍历, 这里简略介绍一下这两种遍历形式。 首先对于一个Map来说,右key列和value列组成,想遍历这个Map,有两种抉择 第一种keyset的想法是先失去其key列, 应用Map的get(key)办法来获取其对应的值,如下图: 对应的代码是: 第二种思维是这样的,想方法失去Key和Value的映射关系,再从这个关系中失去对应的key和value值,也就是第二种遍历形式,entrySet 如图: 对应的代码是: 以上就是Map的两种遍历形式,心愿对大家有帮忙 这外面顺便介绍下Map.Entry的构造 Map.Entry Entry是一个Map的外部接口,等着Map的子类对象来实现它, 子类对象怎么实现呢?应用外部类的模式,这个外部类通过实现Map.Entry的接口 实现其getKey和getValue办法,实现本人的遍历办法,最初map的子类对象再通过 EntrySet办法将这个外部类对象返回,所有有了

April 6, 2023 · 1 min · jiezi

关于map:Map集合之底层解析

HashMap(jdk8)特点数组+链表+红黑树key非反复双列元素key和value能够为空key只能有一个null非平安结构器无参结构器应用无参结构,第一次put时,会先去校验table表中的长度是否>0,如果小于0,则回去查看初始容量threshold是否大于0,如果没有指定threshold初始容量,则会应用默认的初始容量 16作为table表的长度,默认的加载因子为0.75,只有当汇合put时,才会真正的将table表的长度进行扩容,且下次扩容是达到 初始容量*加载因子=临界值时 会再次触发扩容。 /** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }指定初始化容量和加载因子的结构器应用初始化容量和加载因子的结构器初始容量不得小于0如果初始化容量的大小大于 1 << 30(1的30次方),会间接应用1的30次方作为初始容量的大小。加载因子不可小于等于0或者不是一个float类型。public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); }应用单参初始容量结构器初始容量不可大于1的30次方,且不可小于0默认的加载因子为0.75public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); }应用传入map汇合的结构器public HashMap(Map<? extends K, ? extends V> m) { this.loadFactor = DEFAULT_LOAD_FACTOR; // 计算出容量与加载因子 // 如果容量超过加载因子,则进行扩容。 // 随后遍历Map顺次进行put操作 putMapEntries(m, false); }扩容机制hashMap默认初始化是16个长度,其中默认的加载因子是0.75,当汇合中增加的元素长度达到一个临界值--汇合内元素总数 * 0.75=临界值,即12,当增加完一个元素此时汇合内的长度>12时会进行一个扩容,扩容依照以后容量的两倍进行扩容,并且依据以后的加载因子计算出临界值,当下一次再次触发,则会进行雷同的操作。当咱们在table(即hashmap中的数组)表中,存储元素时,会先去依据key,获取对应的hash值(非hashcode值),是依据按位算法--(h = key.hashCode()) ^ (h >>> 16)--,拿到这个值后,会和表中的长度-1进行按位运算,失去一个索引值,如果表中对应的索引值的元素为为空,则间接将元素增加至数组中的索引,如果存在,则会比拟新元素key的hash值与曾经存在的元素key的hash值是否相等,并且两个元素的key是否相等,如果相等阐明元素反复,则会进行替换操作,如果不相等,则会先去判断,这个索引处的对象类型是不是一颗红黑树,如果是红黑树,则会依照红黑树的形式存储,如果是一条链表,则会顺次比拟链中元素是否相等,如果相等,间接退出,如果不相等,则会在链的最初面减少一个元素,如果创立完后该链的长度>=8,则会判断表table长度是否是64,如果不是64,则会优先扩容table,再往链尾增加新元素的Node节点,如果是64,则会将该链造成红黑树结构。EntitySet对于HashMap汇合的外部类EntitySet解析已知,Map汇合是键值对存储,且经源码剖析,其实,每个k-v元素实质就是一个Node节点对象(HashMap外部类Node<K,V>),且这个Node对象实现了Map接口的Entity接口,其实当咱们初始化一个Node节点时,newNode(hash, key, value, null),实际上Map接口的外部类Entity<K,V>保留了Node对象的援用,因为多态的关系,Node对象即是Node类型,又能够向上转型Entity<K,V>,即Entity<K,V>又能够向下转型。为了不便对HashMap汇合的遍历,即会把保留在HashMap中的Node节点的援用保留在EntitySet一份,该汇合寄存的元素类型是Entity,而一个Entity对象就有K-V,EntitySet<Entity<K,V>>,即:Set<Map.Entity<K,V>>,EntitySet中,定义的类型是Map.Entity,然而实际上寄存的还是 HashMap$Node,这是因为Node<K,V> implements Map.Entity<K,V>,当把HashMap$Node 对象寄存到entitySet中后 不便了咱们的遍历和取值。 ...

August 11, 2021 · 3 min · jiezi

关于map:Golang底层实现系列map的底层实现

Golang底层实现系列——map的底层实现本文基于golang 1.14.13map的底层数据结构map的底层实现是一个散列表,map的实现过程实际上就是实现散列表的过程。map次要蕴含两个构造:hmap和bmap。 hmap构造: bmap构造: map的创立map的创立通过生成汇编码能够晓得,调用的时runtime.makemap创立的。 ps:如果你的map初始容量小于等于8会发现走的是runtime.fastrand是因为容量小于8时不须要生成多个桶,一个桶的容量就能够满足(单桶容量通过bucketCnt常量定义)。 这里次要阐明overLoadFactor这个计算B的函数。 func overLoadFactor(count int, B uint8) bool { return count > bucketCnt && uintptr(count) > loadFactorNum*(bucketShift(B)/loadFactorDen)}参数含意: count:以后map容量 B:以后B的值 bucketCnt:单个桶数量(默认为8) loadFactorNum:13 bucketShift : 1<<B loadFactorDen:2 当overLoadFactor返回true时则代表须要扩容,B++。初始时在晓得容量的状况下会间断减少B直到overLoadFactore返回false。 在申请桶空间时用到的函数是makeBucketArry,这个函数会返回指向多个桶空间数组的第一个元素的指针(buckets)和第一个移除桶的指针(nextOverflow)。 <center>桶及溢出桶的散布</center> 如上图(桶及溢出桶的分布图),makeBucketArry实际上就是从内存中申请了一个间断的空间内存,内存大小为bucket.size * buckets总数(根底桶和溢出桶),其中前局部是根底桶(baseBucket)而后是溢出桶。 map的赋值 map的赋值会附带着map的扩容和迁徙,这两局部是map底层实现的要害,会在前面来独自说,除去这两局部的话,map赋值绝对简略,次要是一个hash分为两局部(低位bash和高位hash)低位用于查找bucket,而后tophash疾速判断bucket中各个地位是为空。找到key对应的地位,而后设置key及elem(如果key曾经存在则会间接返回elem的地位,汇编底层会将值赋值到elem对应地位)。 有一点须要特地留神,bmap中的key/value是如下图这样散布的: 其中dataOffset为bmap构造的大小。 漏了一个问题:当查找到所有桶发现没有可插入地位时,阐明所有的桶都满了,须要申请一个溢出桶(可能是事后申请好的,也可能须要从新申请),并且将溢出桶接到以后bucket的前面(如果已有溢出桶则为最初一个溢出桶的前面) map的删除map的删除调用的时mapdelete函数。 删除的逻辑绝对比较简单,大多函数在赋值操作中曾经用到过,所以这里只列除了一些特有的代码(删除数据)。 map的查问map的查问调用的是mapaccess函数。 查问这里次要有一个在扩容过程中查问时须要确认以后key是否曾经迁徙到新的bucket中。次要是通过bmap.tophash来确认。 map的扩容map的扩容有量中状况: 第一种:容量有余,“以后容量+1”之后计算出来扩容因子超出了规定值,即overLoadFactr函数返回true; 第二种:溢出桶过多。溢出桶数量 ≥ (B&15)。 注:这里的扩容因子的规定值是一个定值,是通过教训得出的论断,咱们不做探讨。 当是第一种扩容时,会将map容量扩充一倍。如果是第二种状况则代表可能是空的kv占用的空间过多,这次的扩容不会拓展空间,buckets的数量和原来是一样的然而会对map中的kv进行整顿,去除空的kv。 map的扩容只是将底层数组扩充了一倍,并没有进行数据的转移,数据的转移是在扩容后逐渐进行的,在迁徙的过程中每进行一次赋值(access或者delete)会至多做一次迁徙工作。 map的迁徙在map的赋值与删除中咱们都有说道迁徙,这是扩容后的一部分,迁徙的根底构造是evacDst数据结构如下: 整个迁徙的流程如下: 注: 这里只是简略的举例子,理论中hash值不能够小于5,因为小于5的hash都被用于tophash的标识。 ...

July 1, 2021 · 1 min · jiezi

关于map:Java容器-基于源码分析Map集合体系

一、容器之Map汇合汇合体系的源码中,Map中的HashMap的设计堪称最经典,波及数据结构、编程思维、哈希计算等等,在日常开发中对于一些源码的思维进行参考借鉴还是很有必要的。 根底:元素增查删、容器信息;进阶:存储构造、容量、哈希;API体系 在整个Map和Set的API体系中,最重要的就是HashMap的实现原理: HashMap:基于哈希表治理元素;LinkedHashMap:基于HashMap和双向链表;HashSet:底层保护HashMap构造;LinkedHashSet:继承HashSet,双向链表;所以Map和Set的系列中,除非凡API之外,基本原理都依赖HashMap,只是在各自具体实现时,实用于不同特点的元素治理。 二、数据结构在看HashMap之前,先了解一种数据结构:数组+链表的构造。 基于数组治理元素的地位,元素的存储造成链表构造,既然是链表那么就能够是单双向的构造,这须要针对具体的API去剖析,通过这个构造能够失去几个要害信息: 扩容:基于数组则面对扩容问题;链表:造成链表构造的机制;哈希:哈希值计算与抵触解决;三、HashMap详解1、构造封装既然下面简略形容了数组+链表的构造,那么从源码角度看看是如何封装的: transient Node<K,V>[] table;在HashMap中数组构造的变量命名为table(表),并且是基于Node<K,V>的节点: static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next;}实现Map.Entry接口,并定义节点的构造变量,和节点本身的相干办法。 2、构造方法在晓得HashMap中的根底构造后,能够看其相干的构造方法,初始化哪些变量: 无参结构 public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR;}float DEFAULT_LOAD_FACTOR = 0.75f;this.loadFactor = DEFAULT_LOAD_FACTOR;实际上还要关注一个外围参数: static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16即数组默认的初始化容量DEFAULT_INITIAL_CAPACITY为16,扩容的阈值loadFactor为0.75,即示意当数组中元素达到12个便会进行扩容操作。 有参结构 当然也能够通过有参构造方法去设置两个参数:即容量和扩容的阈值: public HashMap(int initialCapacity, float loadFactor) ;通过两个构造方法的源码可知:当间接创立新的HashMap的时候,不会立刻对哈希数组进行初始化,然而能够对要害变量做自定义设置。 3、装载元素顺着HashMap的应用办法,看元素增加: public V put(K key, V value) { return putVal(hash(key), key, value, false, true);}在put的时候并没有做过多间接操作,而是调用两个要害办法: ...

May 25, 2021 · 1 min · jiezi

关于map:使用TreeMap时需要注意的问题

什么是TreeMaptreemap是java.util.Map的一个实现类,TreeMap实现SortedMap接口,可能把它保留的记录依据键排序,默认是按键值的升序排序,也能够指定排序的比拟器,当用Iterator遍历TreeMap时,失去的记录是排过序的。如果应用排序的映射,倡议应用TreeMap。在应用TreeMap时,key必须实现Comparable接口或者在结构TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异样。 对于java.lang.ClassCastException异样通常遇到这个异样的起因是强制类型转换时报错,在强制类型转换时,须要留神,父类援用指向的对象的类型是子类的时候才能够进行强制类型转换,如果父类援用指向的对象的类型不是子类的时候将产生java.lang.ClassCastException异样。 Animal a1 = new Dog(); // 1Animal a2 = new Cat(); // 2 Dog d1 = (Dog)a1; //3Dog d2 = (Dog)a2; //4在这个demo中,第三行能够进行强转,然而第四行会抛出java.lang.ClassCastException异样,因为a2是animal,然而具体实现类型是cat,不能间接强转为dog类型。遇到这样的异样的时候如何解决呢?如果你晓得要拜访的的对象的具体类型,间接转换成该类型即可。如果不能确定类型能够通过上面的两种形式进行解决(假如对象为o):1、通过o.getClass().getName()失去具体的类型,能够通过输入语句输入这个类型,而后依据类型进行进行具体的解决。2、通过if(o instanceof 类型)的语句来判断o的类型是什么。

May 4, 2021 · 1 min · jiezi

关于map:golang映射Map

map是key-value数据结构,又称为字段或者关联数组。相似其余编程语言的汇合 一、根本语法var 变量名 map[keytype]valuetype // map 应用前要make// map 的key不能反复,反复了,以最初的key-value为准// map 的key-value 是无序的var a map[string]stringa = make(map[string]string, 10)a["n1"] = "a"a["n2"] = "b"a["n3"] = "c"二、应用形式先申明,再make var a map[string]stringa = make(map[string]string, 10)申明间接make a := make(map[string]string, 10)申明间接赋值 var a map[string]string = map[string]string{ "n1" : "宋江" "n2" : "卢俊义"}三、增删改查a := make(map[string]string, 10)// 没这个key就减少,有就批改a["n1"] = "aa"delete(a, "n1")val, res := a["n1"] //查找 有res为true,否则为false if res { fmt.Println("找到了") } else { fmt.Println("没到了") }

September 27, 2020 · 1 min · jiezi

关于map:Java集合Map

package com.study.java;import org.junit.jupiter.api.Test;import java.util.HashMap;/** * /----Map:双列数据 存储key-value对的数据 * /----HashMap:作为Map的次要实现类;线程不平安,效率高,存储null的key和value * /----LinkedHashMap:保障遍历map元素时,能够按增加程序实现遍历 * 起因:在原有的HashMap底层构造根底上,增加了一对指针,指向前一个和后一个元素。 * /----TreeMap:保障依照增加的key-value对进行排序,实现排序遍历。此时思考key的天然排序或订制排序 * 底层应用红黑树 * /----Hashtable:作为古老的实现类,线程平安,效率低;不能存储null的key和value * /----Properties:罕用来解决配置文件。key和value都是string类型 * * * HashMap底层:数组+链表 (jdk7及以前) * 数组+链表+红黑树 (jdk8) * * Map构造的了解: * Map中的key:无序的、不可反复的,应用Set存储所有的key key所在的类要重写equals和hashCode办法 * Map中的value:无序的,可反复,应用Collection存储所有的value value所在类要重写equals办法 * 一个键值对:key-value形成一个Entry对象 * Map中的Entry:无序的、不可反复的,应用Set存储所有的entry * * * HashMap的底层实现原理,jdk7为例: * HashMap map = new HashMap(); * 在实例化后,底层创立了长度为16的一维数组Entry[]table * ...... * map.put(k1, v1) * 首先,调用k1所在类的hashcode()计算k1的哈希值,此哈希值通过某种算法计算后,失去Entry数组寄存的地位 * 如果此地位上数据为空,此时K1-v1增加胜利 * 如果此地位上的数据不为空,(意味着此地位上寄存一个或多个数据(以链表模式存在)),比拟k1和曾经存在的一个或多个数据的哈希值:4 * 如果k1的哈希值和曾经存在的数据哈希值都不雷同,此时K1-v1增加胜利。 状况1 * 如果k1的哈希值和曾经存在的某一个数据(k2-v2)的哈希值雷同,持续比拟:调用k1所在类的equals(k2)办法,比拟: * 如果equals返回false :增加胜利 状况2 * 如果equals返回true:将v1替换雷同key的value值 * 补充:对于状况2和状况3,此时key1-value1和原来的数据以链表的模式存储 * 在一直增加过程,会波及扩容问题,默认扩容形式:扩容为原来容量的两倍,并将原有的数据复制过去 * * jdk8相较于jdk7在底层实现方面的不同: * 1. new HashMap():底层没有创立一个长度为16的数组 * 2. jdk8 底层的数组是Node[],而非Entry[] * 3. 首次调用put办法时,底层创立长度为16的数组 * 4. jdk7底层构造:数组+链表 jdk8:数组+链表+红黑树 * 当数组的某一个索引地位上的元素以链表模式存在的数据个数 > 8 ,且以后数组的长度 > 64时, * 此时此索引地位上的所有数据改为应用红黑树存储。 * * DEFAULT_INITIAL_CAPACITY :HashMap的默认容量:16 * DEFAULT_LOAD_FACTOR :HashMap的默认加载因子:0.75 * threshold :扩容的临界值:=容量*填充因子:16 * 0.75 => 12 * TREEIFY_THRESHOLD:Bucket 中链表长度大于该默认值,转化为红黑树:8 * MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量 * * * LinkedHashMap的底层实现原理:,增加数据时,还保护了两个援用,记录前一个和后一个数据 * * TreeMap 中增加key-value,要求key必须是由同一个类创立的对象 * //因为要依照key进行排序:天然排序、订制排序 * * Properties:罕用来解决配置文件。key和value都是string类型 */public class MapTest { @Test public void test1() { HashMap hashMap = new HashMap(); hashMap.put(null, 123); hashMap.put(222, "aa"); hashMap.put(223, "bb"); System.out.println(hashMap); } @Test public void test2() { HashMap hashMap = new HashMap(); hashMap.put(null, 123); hashMap.put(222, "aa"); hashMap.put(223, "bb"); System.out.println(hashMap); }}

July 22, 2020 · 1 min · jiezi

Go-map原理剖析

在使用map的过程中,有两个问题是经常会遇到的:读写冲突和遍历无序性。为什么会这样呢,底层是怎么实现的呢?带着这两个问题,我简单的了解了一下map的增删改查及遍历的实现。 结构hmaptype hmap struct { // Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go. // Make sure this stays in sync with the compiler's definition. count int // 有效数据的长度# live cells == size of map. Must be first (used by len() builtin) flags uint8 // 用于记录hashmap的状态 B uint8 // 2^B = buckets的数量log_2 of # of buckets (can hold up to loadFactor * 2^B items) noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details hash0 uint32 // 随机的hash种子 buckets unsafe.Pointer // buckets数组array of 2^B Buckets. may be nil if count==0. oldbuckets unsafe.Pointer // 老的buctedts数据,map增长的时候会用到 nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated) extra *mapextra // 额外的bmap数组optional fields}mapextra type mapextra struct { // If both key and value do not contain pointers and are inline, then we mark bucket // type as containing no pointers. This avoids scanning such maps. // However, bmap.overflow is a pointer. In order to keep overflow buckets // alive, we store pointers to all overflow buckets in hmap.extra.overflow and hmap.extra.oldoverflow. // overflow and oldoverflow are only used if key and value do not contain pointers. // overflow contains overflow buckets for hmap.buckets. // oldoverflow contains overflow buckets for hmap.oldbuckets. // The indirection allows to store a pointer to the slice in hiter. overflow *[]*bmap oldoverflow *[]*bmap // nextOverflow holds a pointer to a free overflow bucket. nextOverflow *bmap}bmaptype bmap struct { // tophash generally contains the top byte of the hash value // for each key in this bucket. If tophash[0] < minTopHash, // tophash[0] is a bucket evacuation state instead. tophash [bucketCnt]uint8 // Followed by bucketCnt keys and then bucketCnt values. // NOTE: packing all the keys together and then all the values together makes the // code a bit more complicated than alternating key/value/key/value/... but it allows // us to eliminate padding which would be needed for, e.g., map[int64]int8. // Followed by an overflow pointer.}stringStructtype stringStruct struct { str unsafe.Pointer len int}hitermap遍历时用到的结构,startBucket+offset设定了开始遍历的地址,保证map遍历的无序性 ...

October 8, 2019 · 17 min · jiezi

Golang-中使用-Slice-索引-Map-替代-Map-获得性能提升

起因在我们的多个线上游戏项目中,很多模块和服务为了提高响应速度,都在内存中存放了大量的(缓存)数据以便获得最快的访问速度。 通常情况下,为了使用方便,使用了 go 自身的 map 作为存放容器。当有超过几十万 key 值,并且 map 的 value 是一个复杂的 struct 时,额外引入的 GC 开销是无法忽视的。在 cpu 使用统计图中,我们总是观测到较为规律的短时间峰值。这个峰值在使用 1.3 版本的 go 中显得特别突出(stop the world 问题)。后续版本 go gc 不断优化,到我们现在使用的 1.10 已经是非常快速的并发 gc 并且只会有很短暂的 stw。 不过在各种 profile 的图中,我们依然观察到了大量的 runtime.scanobject 开销! 在一个14年开始的讨论中,就以及发现了 大 map 带来(特别是指针作为 value 的 map)的 gc 开销。遗憾的是在 2019 年的今天这个问题仍然存在。 在上述的讨论帖子中,有一个 Contributor randall77 提到: Hash tables will still have overflow pointers so they will still need to be scanned and there are no plans to fix that.不明白他的 overflow pointers 指的什么,但是看起来如果你有一个大的,指针作为 value 的 map 时,gc 的 scanobject 耗时就不会少。 ...

September 9, 2019 · 1 min · jiezi

深入理解GosyncMap原理剖析

Map is like a Go map[interface{}]interface{} but is safe for concurrent useby multiple goroutines without additional locking or coordination. Loads, stores, and deletes run in amortized constant time. 上面一段是官方对sync.Map 的描述,从描述中看,sync.Map 跟map 很像,sync.Map 的底层实现也是依靠了map,但是sync.Map 相对于 map 来说,是并发安全的。 1. 结构概览1.1. sync.Mapsync.Map的结构体了 type Map struct { mu Mutex // 后面是readOnly结构体,依靠map实现,仅仅只用来读 read atomic.Value // readOnly // 这个map主要用来写的,部分时候也承担读的能力 dirty map[interface{}]*entry // 记录自从上次更新了read之后,从read读取key失败的次数 misses int}1.2. readOnlysync.Map.read属性所对应的结构体了,这里不太明白为什么不把readOnly结构体的属性直接放入到sync.Map结构体里 type readOnly struct { // 读操作所对应的map m map[interface{}]*entry // dirty是否包含m中不存在的key amended bool // true if the dirty map contains some key not in m.}1.3. entryentry就是unsafe.Pointer,记录的是数据存储的真实地址 ...

September 8, 2019 · 4 min · jiezi

Set-和-Map

Set1:基本概念类数组对象, 内部元素唯一 let set = new Set([1, 2, 3, 2, 1]); console.log(set); // Set(3){ 1, 2, 3 } [...set]; // [1, 2, 3] 接收数组或迭代器对象 let set = new Set(document.getElementsByName('div')); set.size; // 0 let set = new Set([1, 2, 3, 2, 1]);不存在隐式转换 let set = new Set([5, '5']); set; // Set(2){ 5, '5' } let set = new Set([NaN, NaN]); set; // Set(1){ NaN } let set = new Set([{}, {}]); set; // Set(2){ {...}, {...} } let set = new Set('abcdabcd'); set; // Set(4){ 'a', 'b', 'c', 'd' }2:属性和方法size , 返回当前Set元素总数 ...

August 20, 2019 · 2 min · jiezi

GIS数学基础05地图投影分类

1. 根据地图投影的变形(内蕴的特征)分类等角投影(conformal projection)地球表面上无穷小图形投影后仍保持相似,或两微分线段所组成的角度在投影后仍保持不变 。在等角投影中,=0, a=b,=900 等面积投影(equivalent projection)地球面上的图形在投影后保持面积不变。在该投影中,vp=0,p=a· b =1 任意投影既不具备等角性质,又没有等面积性质的投影。其中一特例是等距离投影(equidistant projection),即该投影只在某些特定方向上没有变形,如 a=1或b=1 2. 根据投影面与地球面的相关位置分类正轴投影投影面的中心线与地轴一致 斜轴投影投影面的中心线与地轴斜交 横轴投影投影面的中心线与地轴垂直 3. 根据正轴投影时经纬网的形状分类圆锥投影投影中纬线为同心圆圆弧,经线为圆的半经 圆柱投影投影中纬线为一组平行直线,经线为垂直于纬线的另一组平行直线,且两相邻经线之间的距离相等 方位投影投影中纬线为同心圆,经线为圆的半径,且经线间的夹角等于地球面上相应的经差 伪圆锥投影投影中纬线为同心圆圆弧,经线为交于圆心的曲线 伪圆柱投影投影中纬线为一组平行直线,而经线为某种曲线 伪方位投影投影中纬线为同心圆,而经线为交于圆心的曲线 多圆锥投影投影中纬线为同轴圆圆弧,而经线为对称中央直径线的曲线

August 20, 2019 · 1 min · jiezi

为什么-1-7-11mapparseInt-的结果是-1-NaN-3

原文:https://pantao.parcmg.com/press/parseint-mystery.html Javascript 总是以超自然的方式执行我们的代码,这是一件很神奇的事情,如果不信的话,思考一下 ['1', '7', '11'].map(parseInt) 的结果是什么?你以为会是 [1, 7, 11] 吗?我都这么问了,那肯定不是: ['1', '7', '11'].map(parseInt)// 输出:(3) [1, NaN, 3]要理解为什么为会这里,首先需要了解一些 Javascript 概念,如果你是一个不太喜欢阅读长文的人,那直接跳到最后看结论吧 真值与假值请看下面示例: if (true) { // 这里将启动都会被执行} else { // 这里永远都不会被执行}在上面的示例中, if-else 声明为 true,所以 if-block 会一直都执行,而 else-block 永远都会被忽略掉,这是个很容易理解的示例,下面我们来看看另一个例子: if ("hello world") { // 这里会被执行吗? console.log("条件判断为真");} else { // 或者会执行这里吗? console.log("条件判断为假");}打开你的开发者工具 console 面板,执行上面的代码,你会得到看到会打印出 条件判断为真,也就是说 "hello world" 这一个字符串被认为是真值。 在 JavaScript 中,truthy(真值)指的是在布尔值上下文中,转换后的值为真的值。所有值都是真值,除非它们被定义为 假值(即除 false、0、""、null、undefined 和 NaN 以外皆为真值)。引用自:Mozilla Developer: Truthy(真值) 这里一定要划重点:在布尔上下文中,除 false、0、""(空字符串)、null、undefined 以及 NaN 外,其它所有值都为真值 ...

August 18, 2019 · 2 min · jiezi

学习笔记Java集合11-Map-ConcurrentSkipListMap源码分析

介绍跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。 跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。 跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。 存储结构跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。 源码分析主要内部类内部类跟存储结构结合着来看,大概能预测到代码的组织方式。 // 数据节点,典型的单链表结构static final class Node<K,V> { final K key; // 注意:这里value的类型是Object,而不是V // 在删除元素的时候value会指向当前元素本身 volatile Object value; volatile Node<K,V> next; Node(K key, Object value, Node<K,V> next) { this.key = key; this.value = value; this.next = next; } Node(Node<K,V> next) { this.key = null; this.value = this; // 当前元素本身(marker) this.next = next; }}// 索引节点,存储着对应的node值,及向下和向右的索引指针static class Index<K,V> { final Node<K,V> node; final Index<K,V> down; volatile Index<K,V> right; Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) { this.node = node; this.down = down; this.right = right; }}// 头索引节点,继承自Index,并扩展一个level字段,用于记录索引的层级static final class HeadIndex<K,V> extends Index<K,V> { final int level; HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) { super(node, down, right); this.level = level; }}(1)Node,数据节点,存储数据的节点,典型的单链表结构; ...

August 18, 2019 · 8 min · jiezi

全栈之路JAVA基础课程六集合20190615v10

欢迎进入JAVA基础课程 博客地址:https://blog.csdn.net/houjiyu...本系列文章将主要针对JAVA一些基础知识点进行讲解,为平时归纳所总结,不管是刚接触JAVA开发菜鸟还是业界资深人士,都希望对广大同行带来一些帮助。若有问题请及时留言或加QQ:243042162。 寄语:再走长征路,回顾过往峥嵘岁月,重温重要历史事件,砥砺前行,用脚步丈量新时代的长征路。工作道路上,我们也要弘扬这种长征精神,坚持不懈,一步一个脚印,脚踏实地,朝着自己的目标前行。集合1. 集合框架图(1)缩略版 (2)详细版 2.集合和数组区别 3.Collection接口 List接口:元素按进入先后有序保存,可重复 (1)LinkedList:底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素(2) ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素 (3) Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素 Set 接口: 仅接收一次,不可重复,并做内部排序 HashSet 使用hash表(数组)存储元素 LinkedHashSet 链表维护元素的插入次序TreeSet 底层实现为二叉树,元素排好序HashSet和TreeSet区别: (1)Treeset 中的数据是自动排好序的,不允许放入 null 值。 (2)HashSet 中的数据是无序的,可以放入 null,但只能放入一个 null,两者中的值都不能重复,就如数据库中唯一约束。 (3)HashSet 要求放入的对象必须实现 HashCode()方法,放入的对象,是以 hashcode 码作为标识的,而具有相同内容的 String 对象,hashcode 是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例 4.Map HashMap和HashTableHashMap 和 Hashtable 都实现了 Map 接口,因此很多特性非常相似。但是,他们有以下不同点:(1)HashMap 允许键和值是 null,而 Hashtable 不允许键或者值是 null。(2)Hashtable 是同步的,而 HashMap 不是。因此,HashMap 更适合于单线程环境,而 Hashtable 适合于多线程环境。 TreeMap 5.集合遍历(1)list遍历 public class CollectionMain { public static void main(String[] args) { List<String> testList = new ArrayList<>(); testList.add("1"); testList.add("2"); testList.add("3"); System.out.println("使用Iterator迭代....."); Iterator<String> iterator = testList.iterator(); while (iterator.hasNext()){ String value = iterator.next(); System.out.printf("%s ",value); } //在使用ListIterator迭代时,开始也需要正向迭代,然后在倒序迭代 System.out.println("\n\n使用ListIterator迭代....."); System.out.println("正向遍历....."); ListIterator<String> listIterator = testList.listIterator(); while (listIterator.hasNext()){ String value = listIterator.next(); System.out.printf("%s ",value); } System.out.println("\n反向遍历....."); while (listIterator.hasPrevious()){ String value = listIterator.previous(); System.out.printf("%s ",value); } }}输出结果 ...

June 16, 2019 · 1 min · jiezi

百度地图开放平台使用总结

背景项目中用到百度地图组件,是要做一个店铺的商家地理位置页面,使用百度地图JavaScript API,在这里梳理一下大概过程。 一、申请ak在百度地图开放平台控制台申请ak,获得商用授权。授权后就可以使用地图api了。 二、代码中引入 <script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak=*******"></script>在引入时把ak当做参数写在src里。接下来就可以愉快使用各种api了。 三、api使用下面我复制了一个文档上的例子,我的实际应用场景和这个例子差不多,现在HTML中引入map,然后创建一个地图挂载的DOM元素,命名好id,就可以根据业务场景选择合适的api使用啦,还是挺简单方便的。 <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <style type="text/css"> body, html{width: 100%;height: 100%; margin:0;font-family:"微软雅黑";} #l-map{height:300px;width:100%;} #r-result{width:100%;} </style> <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=您的密钥"></script> <title>本地搜索的结果面板</title> </head> <body> <div id="l-map"></div> <div id="r-result"></div> </body></html><script type="text/javascript"> // 百度地图API功能 var map = new BMap.Map("l-map"); // 创建Map实例 map.centerAndZoom(new BMap.Point(116.404, 39.915), 11); var local = new BMap.LocalSearch(map, { renderOptions: {map: map, panel: "r-result"} }); local.search("餐饮");</script>

April 28, 2019 · 1 min · jiezi

【已解决】mpvue使用微信小程序map中的bindregionchange失效问题

<map @regionchange=“regionChange” @begin=“regionChangeBegin” @end=“regionChangeEnd”><map>官方微信小程序只写bindregionchange即可,但是mpvue需要上面三个方法都需要写。如果不写@regionchange,则不会触发后面两个方法@begin和@end

April 18, 2019 · 1 min · jiezi

Java中遍历Map对象的四种方法

方式一 这是最常见的并且在大多数情况下也是最可取的遍历方式。在键值都需要时使用。Map<Integer, Integer> map = new HashMap<Integer, Integer>(); for (Map.Entry<Integer, Integer> entry : map.entrySet()) { System.out.println(“Key = " + entry.getKey() + “, Value = " + entry.getValue()); }方法二 在for-each循环中遍历keys或values。如果只需要map中的键或者值,你可以通过keySet或values来实现遍历,而不是用entrySet。Map<Integer, Integer> map = new HashMap<Integer, Integer>(); //遍历map中的键 for (Integer key : map.keySet()) { System.out.println(“Key = " + key); } //遍历map中的值 for (Integer value : map.values()) { System.out.println(“Value = " + value); }方法三使用Iterator遍历使用泛型:Map<Integer, Integer> map = new HashMap<Integer, Integer>(); Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<Integer, Integer> entry = entries.next(); System.out.println(“Key = " + entry.getKey() + “, Value = " + entry.getValue()); }不使用泛型:Map map = new HashMap(); Iterator entries = map.entrySet().iterator(); while (entries.hasNext()) { Map.Entry entry = (Map.Entry) entries.next(); Integer key = (Integer)entry.getKey(); Integer value = (Integer)entry.getValue(); System.out.println(“Key = " + key + “, Value = " + value); }方法四、通过键找值遍历(效率低)Map<Integer, Integer> map = new HashMap<Integer, Integer>(); for (Integer key : map.keySet()) { Integer value = map.get(key); System.out.println(“Key = " + key + “, Value = " + value); ...

March 14, 2019 · 1 min · jiezi

Map 的 getOrDefault(),putIfAbsent() 和 computeIfAbsent() 三个方法

假设我们定义下面一个 Map:Map<String, List<String>> map = new HashMap<>();如果我们要放一个元素进去,很多人会这么写:List<String> list = map.get(“list1”);if (list == null) { list = new ArrayList<>(); map.put(“list1”, list);}list.add(“A”);实际上从 Java 8 开始,Map 提供了 computeIfAbsent() 方法,我们可以写成一行即可:map.computeIfAbsent(“list1”, k -> new ArrayList<>()).add(“A”);其中变量 k 是 Map 的 key。是不是很方便?但是除此之外,Map 还有两个方法:getOrDefault() 和 putIfAbsent(),这三个方法都接受 Key 和一个“默认值”作为参数,且返回一个 Value。如果不小心把它们搞混用错了,可能会带来大问题。下面分别介绍下。▶ V computeIfAbsent(K, Function<? super K, ? extends V>)这个方法有两个参数,Key 和一个根据 Key 来产生 Value 的 Function;然后返回一个 Value。这个方法会检查 Map 中的 Key,如果发现 Key 不存在或者对应的值是 null,则调用 Function 来产生一个值,然后将其放入 Map,最后返回这个值;否则的话返回 Map 已经存在的值。▶ V getOrDefault(Object, V)这个方法同样检查 Map 中的 Key,如果发现 Key 不存在或者对应的值是 null,则返回第二个参数即默认值。要注意,这个默认值不会放入 Map。所以如果你这样写:Map<String, List<String>> map = new HashMap<>();map.getOrDefault(“list1”, new ArrayList<>()).add(“A”);执行完之后 map 仍然是空的!▶ V putIfAbsent(K, V)这个方法的逻辑完全不同,注意它不是一个 get() 方法,而是 put() 方法的变种!这个方法的逻辑是,如果 Key 不存在或者对应的值是 null,则将 Value 设置进去,然后返回 null;否则只返回 Map 当中对应的值,而不做其他操作。所以显而易见,在最开始的例子中,如果将 computeIfAbsent() 替换成其他两个方法都是错的。 ...

February 25, 2019 · 1 min · jiezi

es6的set和map学习

SetES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。因为 Set 中的值总是唯一的,所以需要判断两个值是否相等。在ECMAScript规范的早期版本中,这不是基于和===操作符中使用的算法相同的算法。具体来说,对于 Set s, +0 (+0 严格相等于-0)和-0是不同的值。然而,在 ECMAScript 2015规范中这点已被更改。有关详细信息,请参阅浏览器兼容性 表中的“value equality for -0 and 0”。另外,NaN和undefined都可以被存储在Set 中, NaN之间被视为相同的值(尽管 NaN !== NaN)。Set本身是一个构造函数,用来生成 Set 数据结构。Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。let b = new Set([‘a’,‘b’,‘c’,‘a’,‘b’]);// Set(3) {“a”, “b”, “c”}set传入参数也可以一试string;let a = new Set(‘aabbcc’);// Set(3) {a,b,c}Set 结构的实例有以下属性。Set.prototype.constructor:构造函数,默认就是Set函数。Set.prototype.size:返回Set实例的成员总数。Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。add(value):添加某个值,返回 Set 结构本身。delete(value):删除某个值,返回一个布尔值,表示删除是否成功。has(value):返回一个布尔值,表示该值是否为Set的成员。clear():清除所有成员,没有返回值。Array.from方法可以将 Set 结构转为数组。或者是扩展运算符…const items = new Set([1, 2, 3, 4, 5]);const array = Array.from(items);const test = […items];遍历操作Set 结构的实例有四个遍历方法,可以用于遍历成员。keys():返回键名的遍历器values():返回键值的遍历器entries():返回键值对的遍历器forEach():使用回调函数遍历每个成员由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。let set = new Set([‘red’, ‘green’, ‘blue’]);for (let item of set.keys()) { console.log(item);}// red// green// bluefor (let item of set.values()) { console.log(item);}// red// green// bluefor (let item of set.entries()) { console.log(item);}// [“red”, “red”]// [“green”, “green”]// [“blue”, “blue”]Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。Set.prototype[Symbol.iterator] === Set.prototype.values// true这意味着,可以省略values方法,直接用for…of循环遍历 Set。let set = new Set([‘red’, ‘green’, ‘blue’]);for (let x of set) { console.log(x);}// red// green// blueWeakSetWeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。首先,WeakSet 的成员只能是对象,而不能是其他类型的值。const ws = new WeakSet();ws.add(1)// TypeError: Invalid value used in weak setws.add(Symbol())// TypeError: invalid value used in weak setWeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在MapJavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。const map = new Map([ [’name’, ‘张三’], [’title’, ‘Author’]]);map.size // 2map.has(’name’) // truemap.get(’name’) // “张三"不仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构(详见《Iterator》一章)都可以当作Map构造函数的参数。这就是说,Set和Map都可以用来生成新的 Map。Map.prototype.clear()移除Map对象的所有键/值对 。Map.prototype.delete(key)如果 Map 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 falseMap.prototype.get(key)返回键对应的值,如果不存在,则返回undefined。Map.prototype.has(key)返回一个布尔值,表示Map实例是否包含键对应的值。Map.prototype.keys()返回一个新的 Iterator对象, 它按插入顺序包含了Map对象中每个元素的键 。Map.prototype.set(key, value)设置Map对象中键的值。返回该Map对象。遍历方法Map 结构原生提供三个遍历器生成函数和一个遍历方法。keys():返回键名的遍历器。values():返回键值的遍历器。entries():返回所有成员的遍历器。forEach():遍历 Map 的所有成员。WeakMapWeakMap与Map的区别有两点。首先,WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。 ...

January 6, 2019 · 1 min · jiezi

leetcode讲解--763. Partition Labels

题目A string S of lowercase letters is given. We want to partition this string into as many parts as possible so that each letter appears in at most one part, and return a list of integers representing the size of these parts.Example 1:Input: S = “ababcbacadefegdehijhklij"Output: [9,7,8]Explanation:The partition is “ababcbaca”, “defegde”, “hijhklij”.This is a partition so that each letter appears in at most one part.A partition like “ababcbacadefegde”, “hijhklij” is incorrect, because it splits S into less parts.Note:S will have length in range [1, 500].S will consist of lowercase letters (‘a’ to ‘z’) only.题目地址讲解这道题我居然一遍过,以前做题或多或少有些小错误。这道题我一上手就发现应该尽量阻止对数据的多次遍历,所以如果能遍历一遍得到一些有用的信息就好了。我用的解法是,建立一个map存储每个字母的首尾位置(当然实际操作中我用的是两个map分别存储字母第一次出现的位置和最后一次出现的位置)。然后如果这一段之间出现了一个字母,它的尾部位置比框住它的这个字母更大,就更新尾部位置,直到尾部位置无法再扩大为止。Java代码class Solution { public List<Integer> partitionLabels(String S) { List<Integer> result = new ArrayList<>(); char[] c = S.toCharArray(); Map<Character, Integer> mapStart = new HashMap<>(); Map<Character, Integer> mapEnd = new HashMap<>(); for(int i=0;i<c.length;i++){ if(mapStart.get(c[i])==null){ mapStart.put(c[i], i); mapEnd.put(c[i], i); }else{ mapEnd.put(c[i], i); } } int beginIndex=0; while(beginIndex<c.length){ int endIndex=mapEnd.get(c[beginIndex]); for(int i=beginIndex;i<endIndex;i++){ if(mapEnd.get(c[i])>endIndex){ endIndex = mapEnd.get(c[i]); } } result.add(endIndex-beginIndex+1); beginIndex = endIndex+1; } return result; }} ...

December 25, 2018 · 1 min · jiezi

地图绘制初探——基于maptalks的2.5D地图绘制

进行图形可视化,难免会遇到地理数据的可视化需求。通常情况下,直接使用echarts对配置项进行处理,就可以满足大部分需求。当然,更加复杂的定制化需求,可能就需要借助d3、Three.js等工具。如果对详细的地图背景有要求的话,又需要将图形库与leaflet、maptalks等地图引擎相结合。不过也许你的需求和我一样,没有那么复杂的交互需求,但对显示效果却有一些想法。那么就可以尝试阅读本文,使用一种比较偷懒的方法,仅基于maptalks本身,来绘制可交互的伪3d地图。下面,以贵州省的伪3d地图为例,进行代码的编写和相应数据的简单处理。1.基本的地图绘制maptalks(maptalks的git)的官方范例写得相当亲切,我们可以从中找到所有绘制伪3d地图需要的元素。首先,从地图底图开始。(官方入门示例)initMapTalk() { let map = new maptalks.Map(‘mapDom’, { center: [121.345, 31.2088], zoom: 9, baseLayer: new maptalks.TileLayer(‘base’, { urlTemplate: ‘http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png’, subdomains: [‘a’,‘b’,‘c’,’d’], attribution: ‘&copy; <a href=“http://osm.org”>OpenStreetMap</a> contributors, &copy; <a href=“https://carto.com/">CARTO</a>' }) });}需要注意的是,除了maptalks.js以外,maptalks.css也是必须引入的。然后,我们需要借助maptalks.Polygon添加一些地图区块(Polygon示例)。虽然地图看起来和长方体不太一样,实际上这些区块也不过是稍微复杂一些的点线集合而已。作为一切绘制的基础,我们需要找一些GeoJson格式的数据(中国各省市级json,世界主要国家json)。观察GeoJson,其中,的coordinates属性,就是地图边界的集合。需要注意的是,区域type包含Polygon和MultiPolygon两类,和maptalks的多边形函数相对应,在数组的层级上稍有区别。为了减少数据选取的麻烦,这里选择使用MultiPolygon来进行绘制。drawPolygons(idx, coordinates, properties) { const polygon = new maptalks.MultiPolygon(coordinates, { symbol: { lineWidth: 1, lineColor: edgeColor, polygonFill: polygonColors[0], polygonOpacity: 0.5 }, properties: { id: properties.id, index: idx, properties: properties } }) .on(“mouseenter”, function(e) { e.target.updateSymbol({ polygonFill: polygonColors[1] }); }) .on(“mouseout”, function(e) { e.target.updateSymbol({ polygonFill: polygonColors[0] }); }) this.polygons.push(polygon);},drawRegion() { const self = this $.getJSON(“guizhou.json”, “”, function(mapData) { const features = mapData.features; features.forEach((g, i) => { const properties = g.properties; const coordinates = g.geometry.coordinates self.drawPolygons(i, coordinates, properties) }); const polygonsLayer = new maptalks.VectorLayer( “vector-polygon”, self.polygons, ).addTo(self.mapDom); })},到现在为止,一切还只是2d的样子。不过,maptalks允许我们绘制3维的高度面(立体的线)。只需要引入一个altitude属性,并在底图上引入pitch属性使视角稍稍偏移, 我们的2.5d地图就画出来了。drawLimitLines(idx, coordinates, properties) { const outLine = new maptalks.MultiLineString(coordinates, { symbol: { lineColor: edgeColor, lineWidth: 1, textPlacement: “vertex” }, properties: { altitude: altitude, index: idx, id: properties.id, properties: properties } }); this.limitLines.push(outLine);},drawPolygons(idx, coordinates, properties) { const polygon = new maptalks.MultiPolygon(coordinates, { symbol: { lineWidth: 1, lineColor: edgeColor, polygonFill: polygonColors[0], polygonOpacity: 0.5 }, properties: { altitude: altitude, id: properties.id, index: idx, properties: properties } }) .on(“mouseenter”, function(e) { e.target.updateSymbol({ polygonFill: polygonColors[1] }); }) .on(“mouseout”, function(e) { e.target.updateSymbol({ polygonFill: polygonColors[0] }); }) this.polygons.push(polygon);},drawRegion() { const self = this $.getJSON(“guizhou.json”, “”, function(mapData) { const features = mapData.features; features.forEach((g, i) => { const properties = g.properties; const coordinates = g.geometry.coordinates self.drawPolygons(i, coordinates, properties) const pathCoordinates = g.geometry.type == “MultiPolygon” ? coordinates.map(d => { return d[0] }) : coordinates self.drawLimitLines(i, pathCoordinates, properties) }); const polygonsLayer = new maptalks.VectorLayer( “vector-polygon”, self.polygons, { enableAltitude: true } ).addTo(self.mapDom); const limitLinesLayer = new maptalks.VectorLayer( “vector-line”, self.limitLines, { enableAltitude: true, drawAltitude: { polygonFill: edgeColor, polygonOpacity: 0.3, lineWidth: 0 } } ).addTo(self.mapDom); })},2.数据和样式处理到这个时候,效果还是不太令人满意。县市间的边界太丑,有没有什么办法把他去掉呢?很简单,直接绘制地图的外沿就好。不过,网上下载的贵州省边界好像和现在带有区县划分的精度不太一样?那么,就来自己处理一下吧。根据问答如何合并区域边界,访问在线的地图数据处理网站http://mapshaper.org/,给每个县市取一个相同的别名,一番输入输出,我们就得到了贵州省的外边界。drawBorderLines(coordinates, properties) { const outLine = new maptalks.MultiLineString(coordinates, { symbol: { lineColor: edgeColor, lineWidth: 1, textPlacement: “vertex” }, properties: { altitude: altitude, id: properties.id, properties: properties } }); this.limitLines.push(outLine);},drawWall() { const self = this $.getJSON(“guizhou-border.json”, “”, function(borderMapData) { const borderFeatures = borderMapData.features[0] const properties = borderFeatures.properties; const pathCoordinates = borderFeatures.geometry.coordinates.map(d => { return d[0] }) self.drawBorderLines(pathCoordinates, properties) const limitLinesLayer = new maptalks.VectorLayer( “vector-line”, self.limitLines, { enableAltitude: true, drawAltitude: { polygonFill: edgeColor, polygonOpacity: 0.3, lineWidth: 0 } } ).addTo(self.mapDom); })}当然,mapshaper的功效不止于此,简直是区域数据处理的一大利器,非常值得探索。另一个令人不太满意的是地图的底图。打开mapbox,找到Studio然后Start With Basic,一个全新的自配地图的世界等待着你。这里,就随便先把英文的区县名换成中文好了。完成配置之后,点击share,你会得到一个链接。不过,在用他替换掉Map的urlTemplate之前,还要按照格式进行一下修整。最后,就得到了本文开头所示的地图。相关源码 ...

December 24, 2018 · 2 min · jiezi