Java语法糖系列一:可变长度参数和foreach循环

目录:
Java语法糖系列一:可变长度参数和foreach循环
//www.greatytc.com/p/628568f94ef8

Java语法糖系列二:自动装箱/拆箱和条件编译
//www.greatytc.com/p/946b3c4a5db6

Java语法糖系列三:泛型与类型擦除
//www.greatytc.com/p/4de08deb6ba4

Java语法糖系列四:枚举类型
//www.greatytc.com/p/ae09363fe734

Java语法糖系列五:内部类和闭包
//www.greatytc.com/p/f55b11a4cec2


好久没做总结,打算挖个坑整理一下Java语法糖相关的知识。文字、说明等知识均来源于互联网和自己的见解,例子都是自己写的,侵删,欢迎讨论~

语法糖

语法糖(Syntactic Sugar),也叫糖衣语法,是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语。指的是,在计算机语言中添加某种语法,这种语法能使程序员更方便的使用语言开发程序,同时增强程序代码的可读性,避免出错的机会。

几乎每种语言都提供语法糖,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了。这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高性能、或能提升语法的严谨性、或能减少编码出错的机会。Java提供给了用户大量的语法糖,比如泛型、自动装箱、自动拆箱、foreach循环、变长参数、内部类、枚举类、断言(assert)等

学习语法糖原理最好的办法就是反编译看源码~

可变长度参数

看以下代码

public static void main(String[] args){
    String [] params=new String[]{
            "111","222","333","444"
    };
    print("AAA","BBB","CCC","DDD");
    print(params);
}

 
public static void print(String... params)
{
    System.out.println();
    for (int i = 0; i < params.length; i++)
    {
        System.out.print(params[i]+"~");
    }
}

print方法的参数的意思是表示传入的params个数是不定的,代码的运行结果:

AAA~BBB~CCC~DDD~
111~222~333~444~

我用数组遍历的方式把参数遍历出来了,同时print方法也接受数组参数,这说明了可变参数是利用数组实现的。查看编译出来的源码:

public static void main(String[] paramArrayOfString)
 {
    String[] arrayOfString = { "111", "222", "333", "444" };

    print(new String[] { "AAA", "BBB", "CCC", "DDD" });
    print(arrayOfString);
  }

  public static void print(String[] paramArrayOfString)
  {
    System.out.println();
    for (int i = 0; i < paramArrayOfString.length; ++i)
    {
      System.out.print(paramArrayOfString[i] + "~");
    }
 }

发现print方法的参数部分由String... params 变成了 String[] paramArrayOfString
数组参数,说明可变长度参数是用数组实现的。

最后,注意一点,可变长度参数必须作为方法参数列表中的的最后一个参数且方法参数列表中只能有一个可变长度参数。

foreach循环原理

public static void print(String... params)
{
    System.out.println();
    for (String s:params)
    {
        System.out.print(s+"~");
    }
}

把上面的print函数换成foreach循环,查看编译出来的源码

 public static void print(String[] paramArrayOfString)
  {
        System.out.println();
        String[] arrayOfString = paramArrayOfString; 
     int i = arrayOfString.length; 
     for (int j = 0; j < i; ++j) { 
        String str = arrayOfString[j];
          System.out.print(str + "~");
        }
  }

发现foreach部分被替换成了普通的for循环,说明对于数组,foreach是用普通for循环实现的。

如果遍历的对象不是数组,而是List、Map等有实现迭代器Iterable接口的容器又是怎么实现的呢?再看一个例子

public static void main(String[] args){
    // TODO Auto-generated method stub
    List<String> list = new ArrayList<String>();
    list.add("AAA");
    list.add("BBB");
    
    for(String l : list)
    {
        System.out.print(l+"~");
    }
    
    Set<String> set=new HashSet<String>();
    set.add("CCC");
    set.add("DDD");

    for(String s : set)
    {
        System.out.print(s+"~");
    }
}
}

查看编译出来的源码

public static void main(String[] paramArrayOfString)
{

    ArrayList localArrayList = new ArrayList();
        localArrayList.add("AAA");
    localArrayList.add("BBB");
    localArrayList.add("CCC");
    localArrayList.add("DDD");

for (Object localObject1 = localArrayList.iterator();
 ((Iterator)localObject1).hasNext(); ) 
{ 
      localObject2 = (String)((Iterator)localObject1).next();
      System.out.print(((String)localObject2) + "~");
}

    localObject1 = new HashSet();
    ((Set)localObject1).add("AAA");
    ((Set)localObject1).add("BBB");
    ((Set)localObject1).add("CCC");
    ((Set)localObject1).add("DDD");

for (Object localObject2 = ((Set)localObject1).iterator(); 
((Iterator)localObject2).hasNext(); )
 { 
    String str = (String)((Iterator)localObject2).next();
    System.out.print(str + "~");
}
}

List和Set的foreach都被编译成用迭代器遍历的形式了,说明在对有实现Iterable接口的对象采用foreach语法糖的话,编译器会将这个for关键字转化为对目标的迭代器使用。
所以如果想要自己自定义的类可以采用foreach语法糖就要实现Iterable接口了。

一点拓展

ArrayList除了支持线性访问(sequential access)外还支持随机访问外(random access)

这是因为arrayList还实现了RandomAccess接口,而Map、Set等没有。
查看JDK关于RandomAccess接口的说明如下,版本是JDK1.8

It is recognized that the distinction between random and sequential

  • access is often fuzzy. For example, some <tt>List</tt> implementations
  • provide asymptotically linear access times if they get huge, but constant
  • access times in practice. Such a <tt>List</tt> implementation
  • should generally implement this interface. As a rule of thumb, a
  • <tt>List</tt> implementation should implement this interface if,
  • for typical instances of the class, this loop:
  • for (int i=0, n=list.size(); i < n; i++)
    
  •     list.get(i);
    
  • runs faster than this loop:
  • for (Iterator i=list.iterator(); i.hasNext(); )
    
  •     i.next();
    
  • @since 1.4

我们直接看最重要的部分,从JDK1.4开始,根据经验,对于实现了RandomAccess接口的List,如ArrayList、CopyOnWriteArrayList, RoleList, RoleUnresolvedList, Stack, Vector,直接使用for循环遍历runs faster than 迭代器遍历。

其实如果看过ArrayList源码的同学也可以注意到:ArrayList底层是采用数组实现的,如果采用Iterator遍历,那么还要创建许多指针去执行这些值(比如next();hasNext())等,这样必然会增加内存开销以及执行效率。

简单测试了一下,空遍历10万条数据不做其他任何操作,对于ArrayList用foreach遍历和for遍历耗时分别是5ms和1ms。例子都好简单就不贴出来了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,724评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,104评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,142评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,086评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,076评论 5 370
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,914评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,220评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,871评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,318评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,834评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,951评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,574评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,162评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,162评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,383评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,349评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,652评论 2 343

推荐阅读更多精彩内容