好得很程序员自学网

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

新手了解java 集合基础知识

一、概述

集合是一种长度可变,存储数据的数据结构多样,存储对象多样的一种数据容器。Java中集合可分为:List集合、Set集合、HashMap集合,等。

Java集合体系结构:

二、collection

collection是Java中所有值存储集合的顶级接口,因此它的所有直接或者间接实现类都有它的非私有方法,我们可以从它的方法开始了解这个体系的功能实现。

?

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

boolean add(E e)

           确保此 collection 包含指定的元素。

  boolean addAll(Collection<? extends E> c)

           将指定 collection 中的所有元素都添加到此 collection 中。

  void clear()

           移除此 collection 中的所有元素。

  boolean contains(Object o)

           如果此 collection 包含指定的元素,则返回 true 。

  boolean containsAll(Collection<?> c)

           如果此 collection 包含指定 collection 中的所有元素,则返回 true 。

  boolean equals(Object o)

           比较此 collection 与指定对象是否相等。

  int hashCode()

           返回此 collection 的哈希码值。

  boolean isEmpty()

           如果此 collection 不包含元素,则返回 true 。

  Iterator<E> iterator()

           返回在此 collection 的元素上进行迭代的迭代器。

  boolean remove(Object o)

           从此 collection 中移除指定元素的单个实例,如果存在的话)。

  boolean removeAll(Collection<?> c)

           移除此 collection 中那些也包含在指定 collection 中的所有元素。

  boolean retainAll(Collection<?> c)

           仅保留此 collection 中那些也包含在指定 collection 的元素。

  int size()

           返回此 collection 中的元素数。

  Object[] toArray()

           返回包含此 collection 中所有元素的数组。

<T> T[]

  toArray(T[] a)

           返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。

1、List

List,是单列集合,存储的是一组插入有序的数据,并且数据可以重复。

List集合

LinkedList ArrayList

1)ArrayList

示例:

?

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

public class CollectionTest {

     public static void main(String[] args) {

         List list = new ArrayList();

         //添加元素,boolean add(E e) 确保此 collection 包含指定的元素

         list.add( "张三" );

         list.add( 1 );

         list.add( 'A' );

         System.out.println(list); //[张三, 1, A]

         //boolean addAll(Collection<? extends E> c)

         //          将指定 collection 中的所有元素都添加到此 collection 中

         List list1 = new ArrayList();

         list.add( "java" );

         list.add( "MySQL" );

         list.addAll(list1);

         System.out.println(list); //[张三, 1, A, java, MySQL]

         //boolean contains(Object o)

         //          如果此 collection 包含指定的元素,则返回 true。

         System.out.println(list.contains( "java" )); //true

         //boolean remove(Object o)

         //          从此 collection 中移除指定元素的单个实例,如果存在的话)。

         System.out.println(list.remove( "java" )); //true

         // int size()

         //          返回此 collection 中的元素数。

         System.out.println(list.size()); //4

         //set(int index, E element)

         //          用指定的元素替代此列表中指定位置上的元素。

         //并返回被修改的值

         System.out.println(list.set( 1 , "李四" ));

         //get(int index)

         //          返回此列表中指定位置上的元素。

         System.out.println(list.get( 1 ));

         // Iterator<E> iterator()

         //          返回在此 collection 的元素上进行迭代的迭代器。

         //集合的遍历

         Iterator iterator = list.iterator();

         while (iterator.hasNext()){

             System.out.println(iterator.next());

         }

     }

说明:ArrayList底层是使用数组的形式创建集合的,因此基于数组的特性,此集合对数据的查找很快速,但是在删除或移动大量数据操作上会显得缓慢。它适合用于快速查找,但不适合做删除多的操作。

2)LinkedList

LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first 和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中 保存数据的基本结构。Node除了保存数据,还定义了两个变量:

prev变量记录前一个元素的位置 next变量记录下一个元素的位置

特点:

数据有序 底层结构为链表

ArrayList比较:

LinkedList的添加元素速度比ArrayList快; LinkedList的查询速度比ArrayList慢; 底层数据结构不同:LinkedList用的是链表结构,而ArrayList底层使用 的是数组结构;

说明:LinkedList一般用于添加频繁的操作,ArrayList一般用于频繁查询 的操作。

示例:

?

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

public class Stack {

     private LinkedList data = null ;

     public Stack(){

         data = new LinkedList();

     }

     // 添加元素

     public boolean push(Object element) {

         data.addFirst(element);

         return true ;

     }

     // 获取元素

     public Object pop() {

         return data.pollFirst();

     }

     // 判断集合是否为空

     public boolean isEmpty() {

         return data.isEmpty();

     }

     // 迭代元素

     public void list() {

         Iterator it = data.iterator();

         while (it.hasNext()){

             System.out.println(it.next());

         }

     }

}

public class MyStack {

     public static void main(String[] args) {

         Stack stack = new Stack();

         stack.push( "张三" );

         stack.push( "李四" );

         stack.push( "王五" );

         stack.list();

         System.out.println( "-------------" );

         Object pop = stack.pop();

         System.out.println(pop);

     }

}

2、set

1)HashSet

HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用 这个实现类。

HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、 查找、删除性能。 HashSet 具有以下特点:不能保证元素的排列顺序 HashSet 不是线程安全的 集合元素可以是 null 不能添加重复元素 HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。 对于存放在Set容器中的对象,对应的类一定要重写equals()和 hashCode(Object obj)方法,以实现对象相等规则。即:[相等的对象必须具有相等的散列码]。

示例:

?

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

public static void main(String[] args) {

        Set set = new HashSet();

        // 添加

        // boolean add(E e) :把指定的元素添加到集合中

        set.add( "hello" );

        set.add( "world" );

        set.add( "world" );

        set.add( null );

        System.out.println(set);

        // 注:Set集合中元素是无序,并且不能重复

        // boolean addAll(Collection<? extends E> c) :把指定的集合添加到集合中

        Set set1 = new HashSet();

        set1.add( "aaa" );

        set1.add( "linux" );

        ;

        set.addAll(set1);

        System.out.println(set);

        // boolean remove(Object o) :从集合中删除指定元素

        set.remove( "hello" );

        System.out.println(set);

        // boolean removeAll(Collection<?> c) :从集合中删除指定集合中的所有元素

        set1.add( "aaa" );

        set1.add( "linux" );

        set.removeAll(set1);

        System.out.println(set);

        // void clear() :清空集合中所有元素

        set.clear();

        System.out.println(set);

        // int size() :获取集合的元素个数

        int size = set.size();

        System.out.println(size);

        // boolean contains(Object o) :判断集合中是否包含指定元素,包含为true,否则为false;

        System.out.println(set.contains( "aaa" ));

 

        // boolean isEmpty() :判断集合是否为空

        System.out.println(set.isEmpty());

    }

说明:在HashSet添加元素时,会首先比较两个元素的hashCode值是不相等,如 果不相等则直接添加;如果相等再判断两个元素的equals的值是否相等, 如果相等则不添加,如果不相等则添加。

2)TreeSet

TreeSet和TreeMap采用红黑树的存储结构 特点:有序,查询速度比List快

使用TreeSet集合是,对象必须具有可比较性。而要让对象具有可比较性有 两种方式:

第一种:实现 Comparable 接口,并重写 compareTo() 方法:

第二种:写一个比较器类,让该类去实现 Comparator 接口,并重写 comare() 方法。

示例:

1.实体类

?

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

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

public class Student implements Comparable<Student>{

     private String name;

     private int age;

     private String sex;

     private int height;

 

     public Student() {

     }

     public Student(String name, int age, String sex, int height) {

         this .name = name;

         this .age = age;

         this .sex = sex;

         this .height = height;

     }

     public String getName() {

         return name;

     }

     public void setName(String name) {

         this .name = name;

     }

     public int getAge() {

         return age;

     }

     public void setAge( int age) {

         this .age = age;

     }

     public String getSex() {

         return sex;

     }

     public void setSex(String sex) {

         this .sex = sex;

     }

     public int getHeight() {

         return height;

     }

     public void setHeight( int height) {

         this .height = height;

     }

     @Override

     public boolean equals(Object o) {

         if ( this == o) return true ;

         if (o == null || getClass() != o.getClass()) return false ;

         Student student = (Student) o;

         return age == student.age &&

                 height == student.height &&

                 Objects.equals(name, student.name) &&

                 Objects.equals(sex, student.sex);

     }

     @Override

     public int hashCode() {

         return Objects.hash(name, age, sex, height);

     }

     @Override

     public String toString() {

         return "Student{" +

                 "name='" + name + '\ '' +

                 ", age=" + age +

                 ", sex='" + sex + '\ '' +

                 ", height=" + height +

                 '}' ;

     }

 

     @Override

     public int compareTo(Student stu) {

         if (stu.getAge() > this .getAge()){

             return 1 ;

         }

         if (stu.getAge() < this .getAge()){

             return - 1 ;

         }

         return stu.getName().compareTo( this .getName());

     }

}

2.测试类:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public class TreeSetTest {

     public static void main(String[] args) {

 

         TreeSet treeSet = new TreeSet();

         Student student1 = new Student( "张三" , 20 , "男" , 165 );

         Student student2 = new Student( "李四" , 21 , "男" , 170 );

         Student student3 = new Student( "王五" , 19 , "女" , 160 );

         Student student4 = new Student( "赵六" , 18 , "女" , 165 );

         Student student5 = new Student( "田七" , 20 , "男" , 175 );

         treeSet.add(student1);

         treeSet.add(student2);

         treeSet.add(student3);

         treeSet.add(student4);

         treeSet.add(student5);

         System.out.println(treeSet);

     }

}

3.实体类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public class Teacher {

     private String name;

     public Teacher(){}

     public Teacher(String name){

         this .name = name;

     }

     public String getName() {

         return name;

     }

     public void setName(String name) {

         this .name = name;

     }

     @Override

     public String toString() {

         return "Teacher{" +

                 "name='" + name + '\ '' +

                 '}' ;

     }

}

4.测试类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public class TreeSetTest2 {

     public static void main(String[] args) {

         Teacher teacher1 = new Teacher( "11" );

         Teacher teacher2 = new Teacher( "12" );

         Teacher teacher3 = new Teacher( "13" );

         Teacher teacher4 = new Teacher( "14" );

         Teacher teacher5 = new Teacher( "15" );

         TreeSet treeSet1 = new TreeSet( new   Comparator() {

             @Override

             public int compare(Object o1, Object o2) {

                 return o1.hashCode() - o2.hashCode();

             }

         });

         treeSet1.add(teacher1);

         treeSet1.add(teacher2);

         treeSet1.add(teacher3);

         treeSet1.add(teacher4);

         treeSet1.add(teacher5);

         System.out.println(treeSet1);

     }

}

说明: HashSet 去重是依靠 hashCode 和 equals() 方法,而TreeSet去重则 依靠的是比较器。

三、Map

​ 存储的双列元素,Key是无序的,不可重复,而Value是无序,可重复的。

1、HashMap

?

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

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

public class HashMapDemo {

     private Map map = null ;

     public void init() {

         map = new HashMap();

         map.put( "a" , "aaa" );

         map.put( "b" , "bbb" );

         map.put( "c" , "ccc" );

         System.out.println(map);

     }

     // 添加元素

     public void testPut() {

         // V put(K key, V value) :把指定的key和value添加到集合中

         map.put( "a1" , "aaa" );

         map.put( "b1" , "bbb" );

         map.put( "c1" , "ccc" );

         System.out.println(map);

         // void putAll(Map<? extends K,? extends V>m) :把指定集合添加集合中

         Map map1 = new HashMap();

         map1.put( "e" , "eee" );

         map1.put( "f" , "fff" );

         map.putAll(map1);

         System.out.println(map);

         // default V putIfAbsent(K key, V value) :如果key不存在就添加

         map.putIfAbsent( "a" , "hello" );

         System.out.println(map);

         map.putIfAbsent( "g" , "ggg" );

         System.out.println(map);

     }

     // 修改元素

     public void testModify() {

         // V put(K key, V value) :把集合中指定key的值修改为指定的值

         map.put( "a" , "hello" );

         map.put( "a" , "world" );

         System.out.println(map);

         // 说明,当key相同时,后面的值会覆盖前面的值。

         // default V replace(K key, V value) :根据key来替换值,而不做增加操作

         Object replace = map.replace( "b1" , "java" );

         System.out.println(replace);

         System.out.println(map);

         //default boolean replace(K key, V oldValue,V newValue)

     }

     // 删除元素

     public void testRemove() {

         // V remove(Object key) :根据指定key删除集合中对应的值

         Object c = map.remove( "c" );

         System.out.println(c);

         System.out.println(map);

         // default boolean remove(Object key, Objectvalue) :根据key和value进行删除

         map.remove( "b" , "bbb1" );

         System.out.println(map);

         // void clear() :清空集合中所有元素

         map.clear();

         System.out.println(map);

     }

     // 判断元素

     public void testJudge() {

         // boolean isEmpty() :判断集合是否为空,如果是返回true,否则返回false

         System.out.println(map.isEmpty());

         // boolean containsKey(Object key) :判断集合中是否包含指定的key,包含返回true,否则返回false

         boolean flag = map.containsKey( "a" );

         System.out.println(flag); // true

         flag = map.containsKey( "a1" );

         System.out.println(flag); // false

         // boolean containsValue(Object value) :判断集合中是否包含指定的value,包含返回true,否则返回false

         flag = map.containsValue( "aaa" );

         System.out.println(flag); // true

         flag = map.containsValue( "aaa1" );

         System.out.println(flag); // false

     }

     // 获取元素

     public void testGet() {

         // int size() :返回集合的元素个数

         int size = map.size();

         System.out.println(size);

         // V get(Object key) :根据Key获取值,如果找到就返回对应的值,否则返回null

         Object val = map.get( "a" );

         System.out.println(val);

         val = map.get( "a1" );

         System.out.println(val); // null

         // default V getOrDefault(Object key, VdefaultValue) :根据Key获取值,如果key不存在,则返回默认值

         val = map.getOrDefault( "a1" , "hello" );

         System.out.println(val);

         // Collection<V> values() :返回集合中所有的Value

         Collection values = map.values();

         for (Object value : values) {

             System.out.println(value);

         }

         // Set<K> keySet() :返回集合中所有的Key

         Set set = map.keySet();

         for (Object o : set) {

             System.out.println(o);

         }

     }

     // 迭代元素

     public void testIterator() {

         // 第一种:通过key获取值的方式

         Set keySet = map.keySet();

         Iterator it = keySet.iterator();

         while (it.hasNext()) {

             Object key = it.next();

             Object val = map.get(key);

             System.out.println(key + "=" + val);

         }

         System.out.println( "------------------------ " );

         // 第二种:使用for循环

         for (Object key : map.keySet()) {

             System.out.println(key + "=" +

                                map.get(key));

         }

         System.out.println( "------------------------ " );

         // 第三种:使用Map接口中的内部类来完成,在框架中大量使用

         Set entrySet = map.entrySet();

         for (Object obj : entrySet) {

             Map.Entry entry = (Map.Entry) obj;

             System.out.println(entry.getKey() + "=" +

                                entry.getValue());

         }

     }

}

说明:在HashMap中键-值允许为空,但键唯一,值可重复。hashMap不是线程安全的。

2、TreeMap

是一个有序的集合,默认使用的是自然排序方式。

?

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

public class Person implements Comparable {

     private String name;

     private int age;

     @Override

     public int compareTo(Object o) {

     if (o instanceof Person) {

     Person p = (Person) o;

     return this .age - p.age;

     }

     return 0 ;

}

     public Person() {}

     public Person(String name, int age) {

         this .name = name;

         this .age = age;

     }

     public int getAge() {

         return age;

     }

     public void setAge( int age) {

         this .age = age;

     }

     @Override

     public String toString() {

         return "Person{" +

             "name='" + name + '\ '' +

             ", age=" + age +

             '}' ;

     }

}

测试

?

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

public class TeeMapDemo {

     @Test

     public void testInteger() {

         TreeMap tm = new TreeMap();

         tm.put( 3 , 333 );

         tm.put( 2 , 222 );

         tm.put( 11 , 111 );

         tm.put( 2 , 222 );

         System.out.println(tm);

     }

     @Test

     public void testString() {

         TreeMap tm = new TreeMap();

         tm.put( "hello" , "hello" );

         tm.put( "world" , "world" );

         tm.put( "about" , "" );

         tm.put( "abstract" , "" );

         System.out.println(tm);

     }

     @Test

     public void testPerson() {

         TreeMap tm = new TreeMap( new Comparator(){

             @Override

             public int compare(Object o1, Object o2) {

                 if (o1 instanceof Person && o2

                     instanceof Person) {

                     Person p1 = (Person) o1;

                     Person p2 = (Person) o2;

                     return p1.getAge() - p2.getAge();

                 }

                 return 0 ;

             }

         });

         tm.put( new Person( "张三" , 18 ), null );

         tm.put( new Person( "李四" , 17 ), null );

         System.out.println(tm);

     }

}

说明:从上面的代码可以发现,TreeMap的使用和TreeSet的使用非常相似,观察HashSet集合的源代码可以看出,当创建 HashSet集合时,其实是底层使用的是HashMap。

?

1

2

3

public HashSet() {

     map = new HashMap<>();

}

HashSet实际上存的是HashMap的Key。

3.ConcurrentHashMap

在Map集合中我们介绍了 HashMap , TreeMap ,在多线程的情况下这些集合都不是线程安全的,因此可能出现线程安全的问题。

在Java中Hashtable是一种线程安全的 HashMap , Hashtable 在方法上与 HashMap 并无区别,仅仅只是在方法使用了 synchronized 以此来达到线程安全的目的,我们观察Hashtable的源码。

?

1

2

3

4

5

6

7

8

9

10

11

public synchronized V get(Object key) {

     Entry<?,?> tab[] = table;

     int hash = key.hashCode();

     int index = (hash & 0x7FFFFFFF ) % tab.length;

     for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {

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

             return (V)e.value;

         }

     }

     return null ;

}

以上是Hashtable的get源码,可以看出它仅仅只是在方法上添加了锁,这大大降低了线程的执行效率,以牺牲效率的形式来达到目的,这显然不是我们在实际中想要的,因此我们需要一种既能在线程安全方面有保障,在效率上还可以的方法。

ConcurrentHashMap采用的是分段锁的原理,我们观察源码。

?

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

53

54

55

56

57

58

59

60

61

62

63

64

65

66

public V put(K key, V value) {

         return putVal(key, value, false );

     }

final V putVal(K key, V value, boolean onlyIfAbsent) {

         if (key == null || value == null ) throw new NullPointerException();

         int hash = spread(key.hashCode());

         int binCount = 0 ;

         for (Node<K,V>[] tab = table;;) {

             Node<K,V> f; int n, i, fh;

             if (tab == null || (n = tab.length) == 0 )

                 tab = initTable();

             else if ((f = tabAt(tab, i = (n - 1 ) & hash)) == null ) {

                 if (casTabAt(tab, i, null ,

                              new Node<K,V>(hash, key, value, null )))

                     break ;                   // no lock when adding to empty bin

             }

             else if ((fh = f.hash) == MOVED)

                 tab = helpTransfer(tab, f);

             else {

                 V oldVal = null ;

                 synchronized (f) {

                     if (tabAt(tab, i) == f) {

                         if (fh >= 0 ) {

                             binCount = 1 ;

                             for (Node<K,V> e = f;; ++binCount) {

                                 K ek;

                                 if (e.hash == hash &&

                                     ((ek = e.key) == key ||

                                      (ek != null && key.equals(ek)))) {

                                     oldVal = e.val;

                                     if (!onlyIfAbsent)

                                         e.val = value;

                                     break ;

                                 }

                                 Node<K,V> pred = e;

                                 if ((e = e.next) == null ) {

                                     pred.next = new Node<K,V>(hash, key,

                                                               value, null );

                                     break ;

                                 }

                             }

                         }

                         else if (f instanceof TreeBin) {

                             Node<K,V> p;

                             binCount = 2 ;

                             if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,

                                                            value)) != null ) {

                                 oldVal = p.val;

                                 if (!onlyIfAbsent)

                                     p.val = value;

                             }

                         }

                     }

                 }

                 if (binCount != 0 ) {

                     if (binCount >= TREEIFY_THRESHOLD)

                         treeifyBin(tab, i);

                     if (oldVal != null )

                         return oldVal;

                     break ;

                 }

             }

         }

         addCount(1L, binCount);

         return null ;

     }

从源码中可以看出 ConcurrentHashMap 仅仅是在当有线程去操作当前数据的时候添加了锁,因此效率大大提高了。

在线程安全的情况下提高了效率。

总结

本篇文章就到这里了,希望能对你有所帮助,也希望您能够多多关注的更多内容!

原文链接:https://blog.csdn.net/ww741258963123/article/details/117901124

查看更多关于新手了解java 集合基础知识的详细内容...

  阅读:13次