四种编程语言中散列表的遍历顺序比较

测试方法

我们使用Ruby, JavaScript, Lua和Go这四种编程语言分别实现了以下步骤:

  1. 创建一个空的散列表H
  2. 以26个英文字母为key并按a-z的顺序插入H
  3. 遍历H并打印遍历的顺序,重复两次
  4. 从H删除"r", "p", "t", "k"这四个key
  5. 遍历H并打印遍历的顺序
  6. 将"r", "p", "t", "k"这四个key重新插入H
  7. 遍历H并打印遍历的顺序

测试环境

  • Ruby 2.3 for Ruby
  • Node.js 6.9.1 for JavaScript
  • Lua 5.2.2 for Lua
  • Go 1.7.1 for Go

Ruby

测试代码

def keys(t)
    r = ""
    t.each { |k, v| r += k }
    r
end

s = "abcdefghijklmnopqrstuvwxyz"
t = {}
s.each_char { |c| t[c] = 1 }
puts(keys(t))
puts(keys(t))

d = "rptk"
d.each_char { |c| t.delete(c) }
puts(keys(t))

d.each_char { |c| t[c] = 100 }
puts(keys(t))

输出及其分析

反复运行多次,输出无变化,内容如下:

abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyz
abcdefghijlmnoqsuvwxyz
abcdefghijlmnoqsuvwxyzrptk

可见,Ruby的散列表是一个有序结构,其遍历顺序和插入顺序完全一致。

JavaScript

测试代码

var print = console.log

function keys(t) {
    r = ""
    for (k in t) {
        r += k
    }
    return r
}

s = "abcdefghijklmnopqrstuvwxyz"
t = {}
for (i in s) {
    t[s[i]] = i
}
print(keys(t))
print(keys(t))

d = "rptk"
for (i in d) {
    delete t[d[i]]
}
print(keys(t))

for (i in d) {
    t[d[i]] = 100
}
print(keys(t))

输出及其分析

反复运行多次,输出无变化,和Ruby版本的输出完全一样(在此略去)。

这说明JavaScript的散列表也是一个有序结构,其遍历顺序与插入顺序相同。

Lua

测试代码

local function keys(t)
    r = ""
    for k in pairs(t) do
        r = r .. k
    end
    return r
end

s = "abcdefghijklmnopqrstuvwxyz"
t = {}
for i = 1, #s do
    t[s:sub(i, i)] = i
end
print(keys(t))
print(keys(t))

d = "rptk"
for i = 1, #d do
    t[d:sub(i, i)] = nil
end
print(keys(t))

for i = 1, #d do
    t[d:sub(i, i)] = 100
end
print(keys(t))

输出及其分析

第一次运行输出:

yzwxuvstabijghefcdqropmnkl
yzwxuvstabijghefcdqropmnkl
yzwxuvsabijghefcdqomnl
yzwxuvstabijghefcdqropmnkl

第二次运行输出:

srqpwvutzyxcbagfedkjihonml
srqpwvutzyxcbagfedkjihonml
sqwvuzyxcbagfedjihonml
srqpwvutzyxcbagfedkjihonml

不难看出,每次运行的输出都不一样,但在单次运行中,对散列表的遍历顺序是确定的,且与插入顺序无关。

Go

测试代码

package main

import "fmt"

func keys(t map[byte]int) string {
    r := ""
    for k := range t {
        r += string(k)
    }
    return r
}

func main() {
    s := "abcdefghijklmnopqrstuvwxyz"
    t := make(map[byte]int)
    for i := range s {
        t[s[i]] = i
    }
    fmt.Println(keys(t))
    fmt.Println(keys(t))

    d := "rptk"
    for i := range d {
        delete(t, d[i])
    }
    fmt.Println(keys(t))

    for i := range d {
        t[d[i]] = 100
    }
    fmt.Println(keys(t))
}

输出及其分析

第一次运行输出:

prbgioquvxeknjlmswcdfzyaht
bgipruvxeknoqmswcdfjlzahty
gibnoquvxedfjlmswczhya
noquvxekfjlmswcdztyahirpbg

第二次运行输出:

motbcghlrvzdeijsuyakpqfnwx
deijlrvzakpqsuyfnwxbcghmot
qsuyawxfnghmobcijlvzde
tbcghmovzdeijlryapkqsufnwx

不仅每次运行的输出都不一样,甚至每一行的输出都不一样。这说明Go的散列表遍历操作是完全随机的。

总结

  • Ruby和JavaScript竟然将散列表这种经典的无序数据结构选择了一种有序的实现,令人深感意外。两者的遍历顺序都和插入顺序一致。
  • Lua的散列表的遍历顺序和插入顺序无关,但表的遍历顺序在表内元素无变化时是确定的。
  • Go的散列表遍历顺序则是完全随机的,非常符合散列表是一种无序数据结构的特点。
  • 但不论编程语言采用了哪种具体的散列表实现办法,除非情况及其特殊,我们在编写代码时都应将散列表作为一种无序数据结构来使用,即在内心机器中,总是将散列表的遍历顺序当成是完全随机的。

问题

  1. Ruby是如何将散列表这种经典的无序数据结构实现为一种有序结构的?
  2. 上面Lua的测试代码每次运行的输出都不一样,这是为什么?
  3. Go的散列表实现极具个性,并完全体现了散列表作为“无序数据结构”的特点。它是如何做到这一点的?
时间: 2024-12-31 06:52:38

四种编程语言中散列表的遍历顺序比较的相关文章

AES加密CBC模式兼容互通四种编程语言平台【PHP、Javascript、Java、C#】

原文:AES加密CBC模式兼容互通四种编程语言平台[PHP.Javascript.Java.C#] 由于本人小菜,开始对AES加密并不了解,在网络上花了比较多时间查阅资料整理: 先简单从百度找来介绍: 1     密码学中的高级加密标准(Advanced Encryption Standard,AES),又称高级加密标准Rijndael加密法, 2 是美国联邦政府采用的一种区块加密标准.这个标准用来替代原先的DES,已经被多方分析且广为全世界 3 所使用.经过五年的甄选流程,高级加密标准由美国国

python 字典(dict)遍历的四种方法性能测试报告_python

python中,遍历dict的方法有四种.但这四种遍历的性能如何呢?我做了如下的测试 l = [(x,x) for x in xrange(10000)] d = dict(l) from time import clock t0=clock() for i in d: t = i + d[i] t1=clock() for k,v in d.items(): t = k + v t2=clock() for k,v in d.iteritems(): t = k + v t3=clock()

java map遍历的四种方法总结_java

整理了关于java中map的遍历的四种方法: import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;import java.util.Set;publicclassMapTest{privateMap<String,String> map;publicMapTest(){  map =newHashMap<String,String>();

四种JS遍历数组方法

<!doctype html public "-//w3c//dtd xhtml 1.0 transitional//en" "http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd"> <html xmlns="http://www.111cn.net/1999/xhtml"> <head> <meta http-equiv="conte

遍历map的四种方法

public static void main(String[] args) { Map<String, String> map = new HashMap<String, String>(); map.put("1", "value1"); map.put("2", "value2"); map.put("3", "value3"); //第一种:普遍使用,二次

Java中四种XML解析技术之不完全测试

xml 在平时工作中,难免会遇到把XML作为数据存储格式.面对目前种类繁多的解决方案,哪个最适合我们呢?在这篇文章中,我对这四种主流方案做一个不完全评测,仅仅针对遍历XML这块来测试,因为遍历XML是工作中使用最多的(至少我认为). 预备 测试环境: AMD毒龙1.4G OC 1.5G.256M DDR333.Windows2000 Server SP4.Sun JDK 1.4.1+Eclipse 2.1+Resin 2.1.8,在Debug模式下测试. XML文件格式如下: <?xml ver

java中四种操作xml方式的比较

xml|比较   1. 介绍 1)DOM(JAXP Crimson解析器)         DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信息片断的集合.这个层次结构允许开发人员在树中寻找特定信息.分析该结构通常需要加载整个文档和构造层次结构,然后才能做任何工作.由于它是基于信息层次的,因而DOM被认为是基于树或基于对象的.DOM以及广义的基于树的处理具有几个优点.首先,由于树在内存中是持久的,因此可以修改它以便应用程序能对数据和结构作出更改.它还

PHP四种基本排序算法示例_php实例

许多人都说算法是程序的核心,算法的好坏决定了程序的质量.作为一个初级phper,虽然很少接触到算法方面的东西.但是对于基本的排序算法还是应该掌握的,它是程序开发的必备工具.这里介绍冒泡排序,插入排序,选择排序,快速排序四种基本算法,分析一下算法的思路. 前提:分别用冒泡排序法,快速排序法,选择排序法,插入排序法将下面数组中的值按照从小到大的顺序进行排序. $arr(1,43,54,62,21,66,32,78,36,76,39); 1. 冒泡排序 思路分析:在要排序的一组数中,对当前还未排好的序

四种生成和解析XML文档的方法详解(介绍+优缺点比较+示例)

原文链接 作者:Alexia(minmin) 众所周知,现在解析XML的方法越来越多,但主流的方法也就四种,即:DOM.SAX.JDOM和DOM4J 下面首先给出这四种方法的jar包下载地址 DOM:在现在的Java JDK里都自带了,在xml-apis.jar包里 SAX:http://sourceforge.net/projects/sax/ JDOM:http://jdom.org/downloads/index.html DOM4J:http://sourceforge.net/proj