Java集合详解(15,16--List集合,17--Set集合,18--Map集合)

一.集合的由来

  我们学习的Java是面向对象语言,而面向对象语言对事物的描述是通过对象体现的。

 为了方便对多个对象进行操作,我们就必须把这多个对象进行存储。

 而要存储多个对象,就不能是一个基本的变量,而应该是一个容器类型的变量。

 首先我们想到的是数组,但数组的长度是固定的且只能放统一类型的数据。

 另外,能想到的就是StringBuffer(StringBuilder),但其结果是一个字符串,不一定满足要求。

 所以,为了适应长度变化和多类型元素的双重要求,产生了集合。

 

二.集合与数组的区别

  主要有3点区别,具体如下图:

 

 

 三.集合的分类

  Java集合主要由两大体系构成,分别是Collection体系和Map体系,Collection和Map分别是两大体系的顶层接口

  注意:Collection和Map都是接口,包括继承自他们的List和Set也都是接口,接口是不能实例化的。

  下面首先介绍Collection接口的子接口:

  1.List和Set的区别

    Collection接口分为List接口和Set接口两大类,具体区别如下图:

  

 

 

  2.List的特点和分类

     List集合像一个数组,是有序的,可以对每个元素的插入位置进行精确地控制,并可根据索引访问和搜索元素。

    与Set最大的不同,就是List中的元素是可重复的。

     

   ArrayList举例:

 1 public static void main(String[] args) {
 2         ArrayList array = new ArrayList();
 3         
 4         Student s1=new Student("benon",28);
 5         Student s2=new Student("yy",22);
 6         Student s3=new Student("shaobao",6);
 7         
 8         array.add(s1);
 9         array.add(s2);
10         array.add(s3);
11         
12         //方式一:迭代器
13         Iterator it=array.iterator();
14         while(it.hasNext()) {
15             Student s=(Student)it.next();
16             System.out.println(s.getName()+"---"+s.getAge());
17         }
18         
19         //方式二:for循环
20         for(int x=0;x<array.size();x++) {
21             Student s=(Student)array.get(x);
22             System.out.println(s.getName()+"---"+s.getAge());
23         }
24     }

  Vector举例:

 1 public static void main(String[] args) {
 2         Vector v = new Vector();
 3         //添加元素
 4         v.addElement("hello");
 5         v.addElement("world");
 6         v.addElement("java");
 7         //遍历一
 8         for(int x=0;x<v.size();x++) {
 9             String s=(String)v.elementAt(x);
10             System.out.println(s);
11         }
12         System.out.println("----------------");
13         //遍历二
14         Enumeration en=v.elements();
15         while(en.hasMoreElements()) {
16             String s=(String)en.nextElement();
17             System.out.println(s);
18         }
19     }

  LinkedList举例:

 1 public static void main(String[] args) {
 2         LinkedList link=new LinkedList();
 3         //添加元素
 4         link.add("hello");
 5         link.add("world");
 6         link.add("java");
 7         //分别在表头和表尾追加元素
 8         link.addFirst("javaee");
 9         link.addLast("android");
10         //获取表头元素和表尾元素
11         System.out.println(link.getFirst());
12         System.out.println(link.getLast());
13         //删除表头元素和表尾元素并返回表头元素和表尾元素
14         System.out.println(link.removeFirst());
15         System.out.println(link.removeLast());
16     }

 

  3.Collections接口的常用功能和方法

    1.添加功能

      boolean add(Object obj):添加一个元素

      boolean add(Collection c):添加一个集合的所有元素

    2.删除功能

      void clear():移除所有元素

      boolean remove(Object obj):移除一个元素

      boolean remove(Collection c):移除一个集合的所有元素

    3.判断功能

      boolean contains(Object obj):判断集合中是否包含指定的元素

      boolean containsAll(Collection):判断集合中是否包含指定的集合元素

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

    4.获取功能

      Iterator<E> iterator()

    5.长度功能

      int size():元素的个数

    6.交集功能

      boolean retainAll(Collection c):两个集合都有的元素

    7.集合转换为数组

      Object[] toArray():集合直接调用toArray()方法,将其转换为数组,再使用遍历即可得到每一个元素。

 

  3.Set的特点和分类

    Set集合是无序的,元元素不允许重复。

  

    Set接口下主要包含两个类:HashSet类和TreeSet类。

  (1)HashSet底层数据结构采用哈希表实现,元素无序且唯一,线程不安全,效率高,可以存储null元素。

    元素的唯一性是靠所存储元素类型是否重写hashCode()和equals()方法来保证的,如果没有重写这两个方法,则无法保证元素的唯一性。

    (注:哈希表是一个元素为链表的数组,综合了数组和链表的好处。)

    步骤:首先比较哈希值

       如果哈希值相同,则调用equals()方法,返回true则元素重复不添加,返回false则不重复,元素添加。

       如果哈希值不同,则直接添加到集合中。

  举例:自定义对象Student类中的hashCode()和equals()方法必须重写,否则无法保证元素的唯一性。

  当然,除了重写,我们还可以使用自动生成,这种方法更加快速,是我们推荐的。

 1 //HashSet集合存储自定义对象,如何保证其唯一性
 2 public class HashSetDemo2 {
 3     public static void main(String[] args) {
 4         HashSet<Student> hs=new HashSet<Student>();
 5         
 6         Student s1=new Student("benon",28);
 7         Student s2=new Student("yy",22);
 8         Student s3=new Student("shaobao",6);
 9         Student s4=new Student("tianbao",7);
10         Student s5=new Student("benon",28);
11         Student s6=new Student("benon",27);
12         
13         hs.add(s1);
14         hs.add(s2);
15         hs.add(s3);
16         hs.add(s4);
17         hs.add(s5);
18         hs.add(s6);
19         
20         for(Student s:hs) {
21             System.out.println(s.getName()+"---"+s.getAge());
22         }
23     }
24 }

自定义对象类:(并重写equals方法和hashCode方法)

 1 public class Student {
 2     @Override
 3     public boolean equals(Object obj) {
 4         if(this==obj) {
 5             return true;
 6         }
 7         if(!(obj instanceof Student)) {
 8             return false;
 9         }
10         Student s=(Student)obj;
11         return this.name.equals(s.name)&&this.age==age;
12     }
13 
14     @Override
15     public int hashCode() {
16         return 0;
17     }
18 
19     private String name;
20     private int age;
21     
22     public Student() {
23         super();
24     }
25     
26     public String getName() {
27         return name;
28     }
29     public void setName(String name) {
30         this.name = name;
31     }
32     public int getAge() {
33         return age;
34     }
35     public void setAge(int age) {
36         this.age = age;
37     }
38 
39     public Student(String name, int age) {
40         super();
41         this.name = name;
42         this.age = age;
43     }
44     
45 }

  (2)LinkedHashSet底层数据结构由哈希表和链表组成:

    哈希表保证元素的唯一性

    链表保证元素有序(存储和取出是一致的)

  (3)TreeSet底层数据结构采用二叉树来实现,元素唯一且有序。

    根据构造方法不同,分为自然排序(无参构造)和比较器排序(有参构造)。

    底层是二叉树结构(红黑树是一种自平衡的二叉树)

    元素的存储步骤:

      第一个元素存储的时候直接作为根节点

      第二个元素从根节点开始比较,比其大则为其右节点,比其小则为其左节点,相等则不管它

    元素的遍历步骤:

      按照左中右的原则依次取出即可。

  因为TreeSet是有序的,所以自定义集合时不能像HashSet那样直接使用增强for进行遍历排序,因此要用到以下两种情况:

  自然排序:(使用无参构造方法时)

    如果一个自定义类的元素要想能够进行自然排序,就必须实现自然排序接口:implements Comparable<T>

    其次要在自定义类中重写compareTo(T t)方法,而且要知道排序条件(主要排序和次要排序),这样才能确定返回值。

   举例:需求要按年龄排序

 1 public static void main(String[] args) {
 2         TreeSet<Student> ts=new TreeSet<Student>();
 3         
 4         Student s1=new Student("benon",28);
 5         Student s2=new Student("yy",22);
 6         Student s3=new Student("shaobao",6);
 7         Student s4=new Student("tianbao",7);
 8         Student s5=new Student("jiabao",5);
 9         Student s6=new Student("yanyan",22);
10         
11         ts.add(s1);
12         ts.add(s2);
13         ts.add(s3);
14         ts.add(s4);
15         ts.add(s5);
16         ts.add(s6);
17         
18         for(Student s:ts) {
19             System.out.println(s.getName()+"---"+s.getAge());
20         }
21     }

  自定义类:实现接口,再重写compareTo()方法

 1 public class Student implements Comparable<Student>{
 2     @Override
 3     public int compareTo(Student s) {
 4         //这里返回什么应该根据排序规则来做
 5         int num=this.age-s.age;
 6         int num2=num==0?this.name.compareTo(s.name):num;
 7         return num2;
 8     }
 9 
10     private String name;
11     private int age;
12     
13     public Student() {
14         super();
15     }
16     
17     public String getName() {
18         return name;
19     }
20     public void setName(String name) {
21         this.name = name;
22     }
23     public int getAge() {
24         return age;
25     }
26     public void setAge(int age) {
27         this.age = age;
28     }
29 
30     public Student(String name, int age) {
31         super();
32         this.name = name;
33         this.age = age;
34     }
35     
36 }

  比较器排序:(使用有参构造方法时)

    我们首先想到的是新建一个外部类,但有点过于繁琐。

    前面有学过,如果一个方法的参数是接口时,那么真正要的是接口实现类的对象,而匿名内部类就可以实现这个东西。

    因为与自然排序相比,免去了自定义类中的实现接口和重写方法,只需在主类中定义一个匿名内部类即可。

    还是上面的代码示例,换成比较器排序:

   注意:

    1.自定义类中进行比较可以直接使用this,而不在自定义类中进行比较,只能有两个参数s1和s2;

    2.自定义类Student中的字段name和age是私有的,故无法直接调用,只能使用getName()和getAge()方法。

 1 public class TreeSetDemo {
 2     public static void main(String[] args) {
 3         TreeSet<Student> ts=new TreeSet<Student>(new Comparator<Student>() {
 4 
 5             @Override
 6             public int compare(Student s1, Student s2) {
 7                 int num=s1.getName().length()-s2.getName().length();
 8                 int num2=num==0?s1.getAge()-s2.getAge():num;
 9                 return num2;
10             }
11             
12         });
13         
14         Student s1=new Student("benon",28);
15         Student s2=new Student("yy",22);
16         Student s3=new Student("shaobao",6);
17         Student s4=new Student("tianbao",7);
18         Student s5=new Student("jiabao",5);
19         Student s6=new Student("yy",23);
20         
21         ts.add(s1);
22         ts.add(s2);
23         ts.add(s3);
24         ts.add(s4);
25         ts.add(s5);
26         ts.add(s6);
27         
28         for(Student s:ts) {
29             System.out.println(s.getName()+"---"+s.getAge());
30         }
31     }
32 }

 

   4.Collection中的类如何选择?

  

 

5.Map体系的特点和使用

  Map是java集合的另一个根接口,map也像一个罐子,不过在map中存储的时候都是以键值对的方式存储的(key-value方式)。

  存储的时候,key键是不能重复的,相当于索引,而value值是可以重复的。查询value值时通过key进行查询。

  

   下图中是Map体系中一些常用的实现类:

  

   Map集合的功能:

    1.添加(替换)

      V put (K key,V value):

        如果key不存在,则存储该键值对,返回null;

        如果key已存在,则替换该键对应的值,返回被替换的值。

    2.删除

      void clear():移除所有键值对

      V remove(Object key):根据键删除键值对,返回值

    3.判断

      boolean containKey(Object key):是否包含指定键

      boolean containsValue(Objet value):是否包含指定值

      boolean isEmpty():集合是否为空

    4.获取

      Set<Map.Entry<K,V>> entrySet():获取键值对对象集合

      V get(Object key):根据键获取值

      Set<K> keySet():获取所有键的集合(键是唯一的,所以是Set类型)

      Collection<V> values():获取所有值的集合(值是可重复的,所以是Collection类型)

    5.长度

      int size():返回集合中的键值对的对数

 

  •   hashMap示例:
 1 public class HashMapDemo3 {
 2     public static void main(String[] args) {
 3         //新建集合
 4         HashMap<String, Student> hm=new HashMap<String, Student>();
 5         //创建自定义对象
 6         Student s1=new Student("benon",28);
 7         Student s2=new Student("yy",22);
 8         Student s3=new Student("shaobao",6);        
 9         //添加元素
10         hm.put("706532", s1);
11         hm.put("504284", s2);
12         hm.put("704", s3);
13         //遍历
14         Set<String> set=hm.keySet();
15         for(String key:set) {
16             Student value=hm.get(key);//根据键获取值
17             System.out.println(key+"---"+value.getName()+"---"+value.getAge());
18         }
19     }
20 }
  • LinkedHashMap:是Map接口的哈希表和链接列表的表现形式,具有可预知的迭代顺序

  由哈希表保证键的唯一性;

  由链表保证键的有序性;

  • TreeMap:是基于红黑树的Map接口的实现,会保证键的排序和唯一性。  

    (注意:是重新排序,不是按之前的默认顺序)

  同前面的TreeSet一样,根据构造方法不同,分为自然排序(无参构造)和比较器排序(有参构造)。

  • Hashtable:此类实现一个哈希表,该哈希表将键映射到相应的值,且是同步的。(任何非null对象都可以作为键和值)

  与HashMap的区别:HashMap是非同步的,且允许使用null值和null键,其它两者无区别。

 

四.Collections类

  这是一个针对集合操作的工具类,都是静态方法。

  Collection和Collections的区别:

    Collection:是单列集合的顶层接口,有子接口List和Set。

    Collections:是针对集合操作的工具类,有针对集合进行排序和二分查找的方法。

 

posted @ 2020-05-05 15:07  benon  阅读(232)  评论(0编辑  收藏  举报