Java 基础 —— 集合框架概述

一、集合框架图

在这里插入图片描述
Java 集合框架主要包括两种类型的容器:一种是集合(Collection),存储一个元素集合;另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型:List、Set 和 Queue。再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。

集合框架内容:

集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:

1、接口

代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象

2、实现(类)

集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。

3、算法

实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

除了集合,该框架也定义了几个 Map 接口和类。Map 里存储的是键/值对。尽管 Map 不是集合,但是它们完全整合在集合中。

二、集合框架体系

集合框架体系如图所示:
在这里插入图片描述
Java 集合框架提供了一套性能优良,使用方便的接口和类,java集合框架位于java.util包中, 所以当使用集合框架的时候需要进行导包。

1、集合接口

集合框架定义了一些接口。
在这里插入图片描述
Set 和 List 的区别

(1)Set 接口实例存储的是无序的,不重复的数据List 接口实例存储的是有序的,可以重复的元素
(2)Set 检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 (实现类有 HashSet、TreeSet)。
(3)List 和数组类似,可以动态增长,根据实际存储的数据的长度自动增长 List 的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 (实现类有ArrayList、LinkedList,Vector) 。

2、集合实现类(集合类)

Java 提供了一套实现了 Collection 接口的标准集合类。其中一些是具体类,这些类可以直接拿来使用,而另外一些是抽象类,提供了接口的部分实现。
在这里插入图片描述
java.util 包中定义的类:
在这里插入图片描述

3、集合算法

集合框架定义了几种算法,可用于集合和映射。这些算法被定义为集合类的静态方法。

4、迭代器 Iterator

迭代器是一种模式、详细可见其设计模式,可以使得序列类型的数据结构的遍历行为与被遍历的对象分离,即我们无需关心该序列的底层结构是什么样子的。只要拿到这个对象,使用迭代器就可以遍历这个对象的内部
ListIterator 继承了 Iterator,以允许双向遍历列表和修改元素
Iterable:实现这个接口的集合对象支持迭代,是可以迭代的,实现了这个可以配合 foreach 使用。List、Set 和 Map 的实现类均实现了 Iterable 接口,所以其实现类均可以迭代。
Iterator:迭代器,提供迭代机制的对象,具体如何迭代是这个 Iterator 接口规范的。

(1)Iterator 源码

public interface Iterator<E> {
    
    
    // 检测集合中是否还有元素
    boolean hasNext();
	// 返回迭代器的下一个元素,并且更新迭代器的状态    
    E next();
    // 将迭代器返回的元素删除
    /*删除最近一次已近迭代出出去的那个元素。
     只有当next执行完后,才能调用remove函数。
     比如你要删除第一个元素,不能直接调用 remove()   而要先next一下( );
     在没有先调用next 就调用remove方法是会抛出异常的。
     这个和MySQL中的ResultSet很类似
    */
    default void remove() {
    
    
        throw new UnsupportedOperationException("remove");
    }
   
    default void forEachRemaining(Consumer<? super E> action) {
    
    
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}

(2)Iterator 对象的获得

List、Set 和 Map 的实现类都提供了 iterator() 方法来获得自身容器的迭代器。

例如:

List<String> list=new ArrayList<String>();
Iterator<String> listiter=list.iterator();

Set<String> set=new HashSet<String>();
Iterator<String> setiter=set.iterator();

Map<String, String> map=new HashMap<String, String>();
// 获得 Entry 的迭代器
Iterator<Map.Entry<String, String>> mapiter=map.entrySet().iterator();

(3)ListIterator 双向迭代
在这里插入图片描述

public interface ListIterator<E> extends Iterator<E> {
    
    
    // 判断是否还有下一个元素,有则返回true  (正向遍历)
    boolean hasNext();
    // 返回列表中的下一个元素,并且前进指针位置
    E next();
    // 判断是否还有上一个元素,有则返回true  (反向遍历)
    boolean hasPrevious();
    // 返回列表中的上一个元素,并向后(上)移动指针位置
    E previous();
    
    int nextIndex();
   
    int previousIndex();
    
    void remove();
    // 用指定的元素替换由 next()或 previous()返回的最后一个元素
    void set(E e);
    // 将指定的元素插入列表的末尾
    void add(E e);
}

5、集合遍历

一般遍历数组都是采用 for 循环或者增强 for,这两个方法也可以用在集合框架,但是还有一种方法是采用迭代器遍历集合框架,它是一个对象,实现了 Iterator 接口或 ListIterator 接口。

(1)遍历 ArrayList

import java.util.*;

public class Test{
    
    
 public static void main(String[] args) {
    
    
     List<String> list=new ArrayList<String>();
     list.add("Hello");
     list.add("World");
     list.add("HAHAHAHA");
     
     //第一种遍历方法使用 For-Each 遍历 List
     for (String str : list) {
    
    
        System.out.println(str);
     }

     //第二种遍历,把链表变为数组相关的内容进行遍历
     String[] strArray=new String[list.size()];
     list.toArray(strArray);
     for(int i=0;i<strArray.length;i++) {
    
    
        System.out.println(strArray[i]);
     }

    //第三种遍历 使用迭代器进行相关遍历
     Iterator<String> ite=list.iterator();
     while(ite.hasNext()) {
    
    
         System.out.println(ite.next());
     }
 }
}

三种方法都是用来遍历 ArrayList 集合,第三种方法是采用迭代器的方法,该方法可以不用担心在遍历的过程中会超出集合的长度

(2)遍历 Set

	// 1、迭代遍历:  
	Set<String> set = new HashSet<String>();  
	Iterator<String> it = set.iterator();  
	while (it.hasNext()) {
    
      
  		String str = it.next();  
  		System.out.println(str);  
	  }  
  
	// 2.for循环遍历:  
	for (String str : set) {
    
      
    	System.out.println(str);  
	  }  

	// 3、set.forEach(java8 才支持)
	Set<String> set = new HashSet<String>();
	set.forEach(item -> {
    
    
        System.out.println(item);
       });

	// 4、set.stream().forEach (java8 才支持)
	Set<String> set = new HashSet<String>();
	set.stream().forEach(item -> {
    
    
        System.out.println(item);
       });

(3)遍历 Map

import java.util.*;

public class Test{
    
    
     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");

      //第一种:普遍使用,二次取值
      System.out.println("通过 Map.keySet 遍历 key和value:");
      for (String key : map.keySet()) {
    
    
       System.out.println("key= "+ key + " and value= " + map.get(key));
      }

      //第二种:迭代器
      System.out.println("通过 Map.entrySet 使用 iterator 遍历 key 和 value:");
      Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
      while (it.hasNext()) {
    
    
       Map.Entry<String, String> entry = it.next();
       System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
      }

      //第三种:推荐,尤其是容量大时
      System.out.println("通过 Map.entrySet 遍历 key 和 value");
      for (Map.Entry<String, String> entry : map.entrySet()) {
    
    
       System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
      }

      //第四种
      System.out.println("通过 Map.values() 遍历所有的 value,但不能遍历 key");
      for (String v : map.values()) {
    
    
       System.out.println("value= " + v);
      }
     }
}

遍历方式的比较:

(1) for 循环适合访问顺序结构,可以根据下标快速获取指定元素。而 Iterator 适合访问链式结构,因为迭代器是通过 next() 和 Pre() 来定位的,可以访问没有顺序的集合。
(2)使用 Iterator 的好处在于可以使用相同方式去遍历集合中元素,而不用考虑集合类的内部实现(只要它实现了 java.lang.Iterable 接口)。如果使用 Iterator 来遍历集合中元素,一旦不再使用 List 转而使用 Set 来组织数据,那遍历元素的代码不用做任何修改,如果使用 for 来遍历,那所有遍历此集合的算法都得做相应调整,因为List有序,Set无序,结构不同,他们的访问算法也不一样(遍历和集合本身分离)。

5、比较器

使一个类具备比较的能力只需要其实现具有比较能力的接口即可

(1)实现 Comparable 接口

Comparable 接口:这个接口要在自定义的类上实现,比如定义了一个 Person 类,实现 Comparable 接口,除了该类应有的属性、构造器、方法外,我们需要实现该接口中的抽象方法 compareTo() 并且需要指定其泛型也就是要比较的引用数据的类型),意思就是说要比较的是泛型也就是<>中间的类型。通过重写 compareTo() 方法来重写自己需要的比较规则,程序执行时底层会自动调用该方法

import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

class Stu implements Comparable<Stu>{
    
    
	private int id;
	private String name;
	private int age;
	
	public Stu() {
    
    
		super();
	}
	public Stu(int id, String name, int age) {
    
    
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}
	public int getId() {
    
    
		return id;
	}
	public void setId(int id) {
    
    
		this.id = id;
	}
	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;
	}
	@Override
	public String toString() {
    
    
		return "Stu [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
	//重写接口的方法
	@Override
	public int compareTo(Stu o) {
    
    
		// 如果年龄相同就按名字排序
		if(o.age==this.age){
    
      
            return this.name.compareTo(o.name);    // 注意:这里调用的 compareTo 方法是 String 类中重写的 compareTo 方法,并不是我们重写的方法。
        }else{
    
      
        	// 年龄不同,按年龄排序
            return this.age-o.age;
        }
		// 拿新的对象与几何中的已有对象分别比较
		return this.age-o.age; // 按年龄升序
		// 进来一个新的元素放到集合中,分别从左到右比较集合中所有元素,当返回值 >=0 时,新的元素位置不变,当返回值 <0 时,位置交换,然后依次比较所有元素
	}	
}

//建立一个集合  Stu 类型的  有属性 id,name, age
//将集合中的元素按照 age 进行排序
public class TestComparable{
    
    
	public static void main(String[] args) {
    
    
	
		List<Stu> list = new ArrayList<Stu> ();
		list.add(new Stu(1,"lhj",25));
		list.add(new Stu(2,"zj",23));
		list.add(new Stu(3,"hj",15));
		Collections.sort(list);
		System.out.println(list);
	}	
}

(2)实现 Comparator 接口

Comparator 接口:这个接口需要我们自定义一个类来实现它,我们可以称这个类为比较器,并且需要指定比较类型类型(用泛型来制指定,在实现接口的时候指定),我们需要实现该接口中的抽象方法 compare() ,只要实现了该比较方法就已经实现了比较器的功能,当然也可以加别的属性和方法。
在创建集合对象的时候将比较器传入,从而使集合对象具有比较功能。

//比较器二:实现 Comparator 接口,在 compare() 方法中编写比较规则
class MyComparator1 implements Comparator<Person>{
    
    

    public int compare(Person p1,Person p2){
    
    
    	 // 年龄相同用名字排序
        if(p1.age==p2.age){
    
     
            return p2.name.compareTo(p1.name);
        }else{
    
       
        	// 年龄不同用年龄排序
            return p1.age-p2.age;
        }
    }
}

//  要传入写好的比较器
 Set<Person> set=new TreeSet<>(new MyComparator1());    //传入比较器

上述比较器的优缺点:

(1)实现 Comparable 接口的比较器,因为要在要比较的类上实现,所以比较规则只能重写一次,比较规则比较单一,适用于比较规则不变的时候使用此方法
(2)实现 Comparator 接口的比较器,因为是新建一个类实现 Comparator 接口,而我们可以新建很多类来实现 Comparator 接口,所以可以写很多种不同的比较规则,适用于比较规则多变的情况,根据需要传入相应的比较器即可,比较灵活多变

Guess you like

Origin blog.csdn.net/IT__learning/article/details/121338921