好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Java集合定义与用法实例总结【Set、List与Map】

本文实例讲述了java 集合 定义与用法。分享给大家供大家参考,具体如下:

java集合大体可分为三类,分别是set、list和map,它们都继承了基类接口collection,collection接口定义了众多操作集合的基本方法,如下:

为了访问collection集合,不得不去了解iterator接口。该接口很简单,主要用于定义访问集合的方法,如下:

所以上述的三大类子集合必定都继承了上面2个接口。其中set集合要求元素不重复,且内部无序,所以访问时只能根据元素值来访问;list内部为动态数组,支持有序,元素也可重复,所以往往有index;map所代表的集合是具有key-value的映射关系的集合,如哈希表。

1. set

 

1.1 set不可添加相同元素

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

import java.util.collection;

import java.util.hashset;

public class testset {

   @suppresswarnings ({ "rawtypes" , "unchecked" })

   public static void main(string[] args) {

     collection c1 = new hashset();

     person p = new person();

     c1.add(p);

     c1.add(p);

     system.out.println(c1);

     collection c2 = new hashset();

     string str1 = new string( "123" );

     string str2 = new string( "123" );

     c2.add(str1);

     c2.add(str2);

     system.out.println(c2);

   }

}

class person {

   public person() {

   }

   public person(string name) {

     this .name = name;

   }

   public string name;

}

运行输出:

[demo.person@1db9742]
[123]

第一次添加了俩次p对象,集合不会重复添加,所以输出了 [person@1db9742] ,这很合理。但是第二次明明new了两个字符串,str1和str2的引用肯定是不同的,那为什么程序还是会认为是相同的元素呢。查找 add(e e) 的源码,找到了其中的关键部分,如下

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public boolean add(e e) {

   return map.put(e, present)== null ;

  }

  public v put(k key, v value) {

   if (key == null )

    return putfornullkey(value);

   int hash = hash(key.hashcode());

   int i = indexfor(hash, table.length);

   for (entry<k,v> e = table[i]; e != null ; e = e.next) {

    object k;

    if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

     v oldvalue = e.value;

     e.value = value;

     e.recordaccess( this );

     return oldvalue;

    }

   }

   modcount++;

   addentry(hash, key, value, i);

   return null ;

  }

这一句

?

1

if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

表明,当两个对象的哈希值相等并且对象的equals方法返回真时,则认为两个对象是相同的,并不会进行后面的addentry操作,即不会添加至集合。

这也就难怪 string str1=new string("123") 和 string str2=new string("123"); 被认为是同一个对象了,因为string在做equals的时候恰好很特殊,只要值相等,则euqals就返回真。

为了测试源码是否真的是这么执行的,改写程序如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

import java.util.collection;

import java.util.hashset;

public class testset {

   @suppresswarnings ({ "rawtypes" , "unchecked" })

   public static void main(string[] args) {

     collection c1 = new hashset();

     c1.add( new a());

     c1.add( new a());

     c1.add( new b());

     c1.add( new b());

     c1.add( new c());

     c1.add( new c());

     system.out.println(c1);

   }

}

class a {

   @override

   public boolean equals(object obj) {

     return true ;

   }

   @override

   public int hashcode() {

     return 1 ;

   }

}

class b {

   @override

   public int hashcode() {

     return 1 ;

   }

}

class c {

   @override

   public boolean equals(object obj) {

     return true ;

   }

}

输出:

[demo.a@1, demo.b@1, demo.b@1, demo.c@1db9742, demo.c@106d69c]

可以看到,b和c的对象都没有被集合认为是同一个对象,而a类中重写的哈希值和equals永远相等,导致a类 new 出的匿名对象也是相等的,故只添加了一个。

1.2 set不可修改元素的值

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

import java.util.collection;

import java.util.iterator;

import java.util.hashset;

public class testset {

   @suppresswarnings ({ "rawtypes" , "unchecked" })

   public static void main(string[] args) {

     collection coll = new hashset();

     coll.add( new person( "f" ));

     coll.add( new person( "l" ));

     coll.add( new person( "y" ));

     system.out.println(coll); // a

     iterator it = coll.iterator(); // b

     while (it.hasnext()) {

       person p = (person) it.next();

       if (p.name.equals( "f" )) {

         p = new person(); // c

       }

     }

     iterator it1 = coll.iterator(); // d

     while (it1.hasnext()) {

       person p = (person) it1.next();

       system.out.println(p.name);

     }

     system.out.println(coll);

   }

}

class person {

   public person() {

   }

   public person(string name) {

     this .name = name;

   }

   public string name;

}

运行输出:

[demo.person@52e922, demo.person@1db9742, demo.person@106d69c]
y
f
l
[demo.person@52e922, demo.person@1db9742, demo.person@106d69c]

代码输出表明,hashset集合的元素并不是有序的,另外在代码c处取出了元素后,为该元素重新赋值,而后输出发现集合并没有改变,这说明iterator迭代器在提供next的方法里应该是类似于copy的技术,目的就是防止在遍历set集合的时候元素被改变。

2. list

 

list作为collection的子接口,自然可以调用父接口的基本方法,但由于list集合元素是有序的,所以list接口在父接口的基础上又增加了些方法。这些方法的作用与类父接口类似,只是都会增加一个index参数做为索引。

list中最常用的就是arraylist,它在vector的基础上做了许多改进,下面代码将展示list的基本操作用法:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

import java.util.list;

import java.util.arraylist;

import java.util.listiterator;

public class testlist {

   @suppresswarnings ({ "rawtypes" , "unchecked" })

   public static void main(string[] args) {

     // 向list中添加不同类型的元素,会自动装箱

     list list = new arraylist();

     list.add( 1 );

     list.add( "123" );

     list.add( 3 .14f);

     // 列表元素:[1, 123, 3.14]

     system.out.println( "列表元素:" + list);

     // 清除列表

     list.clear();

     list.add( "我" );

     list.add( "们" );

     list.add( "交" );

     list.add( "个" );

     list.add( "朋" );

     list.add( "友" );

     list.add( "吧" );

     // 列表元素:[我, 们, 交, 个, 朋, 友, 吧]

     system.out.println( "列表元素:" + list);

     list sub1 = list.sublist( 0 , list.size() / 2 );

     // 子列表元素:[我, 们, 交]

     system.out.println( "子列表元素:" + sub1);

     // 从list中删除sub

     sub1.removeall(list);

     // 列表元素:[个, 朋, 友, 吧]

     system.out.println( "列表元素:" + list);

     // 添加至头

     list sub2 = new arraylist();

     sub2.add( "我" );

     sub2.add( "们" );

     sub2.add( "交" );

     system.out.println( "子列表元素:" + sub2);

     // 在list中添加sub2

     list.addall( 0 , sub2);

     system.out.println( "列表元素:" + list);

     // 遍历操作

     listiterator iter = list.listiterator();

     system.out.println( "--正向遍历--" );

     while (iter.hasnext()) {

       system.out.println(iter.next());

     }

     system.out.println( "--反向遍历--" );

     while (iter.hasprevious()) {

       system.out.println(iter.previous());

     }

   }

}

运行输出:

列表元素:[1, 123, 3.14]
列表元素:[我, 们, 交, 个, 朋, 友, 吧]
子列表元素:[我, 们, 交]
列表元素:[个, 朋, 友, 吧]
子列表元素:[我, 们, 交]
列表元素:[我, 们, 交, 个, 朋, 友, 吧]
--正向遍历--







--反向遍历--






list就像是一个动态且元素类型可不一的数组,它不仅具有iterator迭代器,而且还有listiterator,后者就像数组一样,支持正向和反向遍历。

3. map

 

map是具有映射关系的集合,key做为主键,可以索引到唯一的value,key和value都可以是对象。如果单独取出map里的所有值的话,map看起来就像是set,而又由于它较之set又具有索引功能,所以又似乎有些list的影子。实际上,map的key必须实现equals和hashcode方法,这也就解释了为什么可以将一个对象的引用做为key了(实际上是计算这个对象的hashcode做为主键),因此不能将同一个对象的引用存入某一个map中。hashset实现了set接口,arraylist实现了list接口,那么单从命名上就能得知,hashmap肯定实现了map接口,map接口的功能如下,

在hashset和arraylist都有一个访问迭代器的方法 iterator() ,在set接口中却没有,毕竟set是key-value组合,取而代之的是一个 keyset() 方法,用以返回一个实现了set接口的对象,从而又可以进行iterator的操作。

基本操作如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

import java.util.hashmap;

import java.util.iterator;

public class testmap {

   @suppresswarnings ({ "rawtypes" , "unchecked" })

   public static void main(string[] args) {

     hashmap hash = new hashmap();

     hash.put( "1" , "我" );

     hash.put( "2" , "们" );

     // 主键可为null,但只能有一个null值的主键

     hash.put( null , null );

     // 值可以为null,可以有很多个值为null

     hash.put( "3" , null );

     hash.put( "4" , null );

     system.out.println( "直接遍历:" + hash);

     system.out.println( "----keysey遍历----:" );

     for (object key : hash.keyset()) {

       system.out.println( "key:" + key + " value:" + hash.get(key));

     }

     system.out.println( "----iterator遍历----:" );

     iterator iter = hash.keyset().iterator();

     while (iter.hasnext()) {

       string key = (string) iter.next();

       system.out.println( "key:" + key + " value:" + hash.get(key));

     }

   }

}

输出:

直接遍历:{null=null, 1=我, 2=们, 3=null, 4=null}
----keysey遍历----:
key:null value:null
key:1 value:我
key:2 value:们
key:3 value:null
key:4 value:null
----iterator遍历----:
key:null value:null
key:1 value:我
key:2 value:们
key:3 value:null
key:4 value:null

hashmap可以有空key,但是只能有一个,,这符合唯一主键的原则,并且若主键重复了,则会覆盖之前的相同主键。而值却没有限制,有多少个null都可以。此外,在使用hashmap的时候还需要注意下面两点:

1.hashmap是非线程安全的,而hashtable是线程安全的。

对于各种集合的各种操作,其实可以依赖于collections类,该类提供了许多静态操作集合的方法,其中就可以将一个普通集合封装为线程安全的集合,如下

?

1

collection c=collections. synchronized ( new arraylist());

2.了解hashmap的性能

hashmap利用每一个key的哈希值,去为value找寻存储位置。这个存储位置往往被称为[桶],当哈希值唯一时,那么一个桶中就只有一个对象,这时情况最理想,然而若非正常情况下(比如重写hashcode强制返回相等),那么一个桶能就有放多个对象,这时性能最差。

上面说道,hashmap与set、list在某方面都很相似,做为一个强大的集合,它的内部自然也有会动态开辟内存的操作。所有就有了下面几个参数,

capacity (容量):在初始化hashmap时将会有一个默认值(好象是10吧),随着集合的大小也会自身调整。 size (元素个数):有多少个元素size就是多少。 load factor(负载因子):load factor=size/capacity,取值0~1。

当负载因子很大时,如有90个元素,而集合的容量为100,因子就是0.9,这样情况非常不利于查询操作,因为put和get操作会遍历大量的元素,时间复杂度无形就会增加,但在内存开销上确实是比较节省的,因为集合不会反复的创建,因为每一次扩充集合的操作,就意味着要将原始元素重新插入到新的集合中去,性能开销是很大的。

而当负载因子很小时,查询效率将会非常高(因为遍历少),但是却在内部进行了许多次开辟内存的操作。

因此,在系统中,要根据实际需求正确把握hashmap的用法,如一开始建立集合的时候就知道这个集合非常大,那么就有必要在初始化的时候就指明capacity,不应该使用默认值,这样效率能高点;相反只有少量集合元素时,不应该在创建的时候指定很大的capacity,这明显是在浪费内存。

希望本文所述对大家java程序设计有所帮助。

原文链接:https://blog.csdn.net/kkkkkxiaofei/article/details/18041205

查看更多关于Java集合定义与用法实例总结【Set、List与Map】的详细内容...

  阅读:45次