zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Java集合框架学习(ArrayList、Set、Collection、Vector、Enumeration、Map、Iterator)

JAVAsetMap集合学习框架 vector ArrayList
2023-09-11 14:22:11 时间

在这里插入图片描述

一、前言引入

集合框架就是java是实现的数据结构,或者简单点说,所谓的类集指的是对象数组的引用。之前保存多个对象,使用的是数组,但数组的长度是固定的。后来使用了链表实现了一个动态的对象数组。

  • 链表的开发麻烦;
  • 链表的操作性能不好;
  • 链表使用了Object类进行保存,所有的对象必须向上转型以及强制性向下转型的操作;

综上,得出结论:提供了一个与链表类似的工具类——Vector(向量),后来发现这个类并不能很好的进行项目的开发,所以从Java2(JDK1.2之后)提供了一个专门实现数据结构的开发框架——类集框架,并且在JDK1.5之后泛型技术的引入,又解决了类集框架中都使用Object所带来的安全隐患。

随后在JDk1.8之后,又针对类集的大数据操作环境下推出了数据流的分析操作功能。
在这里插入图片描述
在整个类集之中有以下几个核心接口:

  • Collection、List、Set;

  • Map;

  • Iterator、Enumeration。

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

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

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

二、具体内容

2.1 collection接口
Collection是整个类集之中单值保存的最大父接口。即:每一次可以向集合里面保存一个对象。

   public interface Collection<E>extends Iterable<E>
Public boolean add(E e)向集合中加入元素
Public boolean addAll(Collection<? extends E> e)追加一个集合
Public void clear()清空集合,根元素为null
Public boolean contains(Object o)判断是否包含此元素(区分大小写)
Public boolean isEmpty()判断是否不包含元素
Public boolean remove()删除单个实例
Public int size()集合中的元素个数
Public Object[] toArray()将集合变为对象数组保存
Public Iterator iterator()为Iterator接口实例化

由于开发的严格性一般不会使用collection接口。而会去使用它的两个子接口:List(允许重复)、Set(不允许重复)。

2.2 List接口

在List字接口中重点掌握以下方法:

方法释义
Public E get(int index)取得索引编号的内容
Public E set(int index,E element)修改指定索引编号的内容
Public ListIterator listIterator()为ListIterator接口实例化
  • 而List本身属于接口,所以如果要想使用此接口进行操作,必须存在有子类,可以使用ArrayList子类是实现操作(还有一个Vector子类,多数选择ArrayList)。

2.2.1 子类:ArrayList
ArrayList类是List接口最为常用的一个子类。
范例:基本操作

public class TestDemo{
	public static void main(String args[]){
		List<String> all = new ArrayList<String>();
		System.out.println("长度:"+all.size()+all.isEmpty());
		all.add("wo");
		all.add("de");
		all.add("weilai");
		all.add("bushimeng");
		System.out.println("长度:"+all.size()+"是否为空:"+all.isEmpty());
//		collection接口定义了size()方法可以取得集合长度
//		List子接口扩充了一个get()方法,可以索引取得数据
		for (int i = 0; i < all.size(); i++) {
			String str = all.get(i);//取得索引数据
			System.out.println(str);
		}
	}
}

在这里插入图片描述

演示发现,List集合之中保存的数据按照保存的顺序存放,而且允许数据重复,List子接口扩充有get()方法。Collection接口中无get()方法。

范例:集合中保存对象

class Book {
	private String title;
	private int price;
	public Book(String title,int price) {
		this.title = title;
		this.price = price;
	}
	@override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		Book book = (Book) obj;
		if (this.title.equals(book.title)&&this.price==book.price) {
			return true;
		}
		return false;
	}
	@override
	public String toString(){
		return "书名:" + this.title + ",价格" + this.price + "\n";
	}
}
public class TestDemo{
	public static void main(String args[]) {
		List<Book> all = new ArrayList<Book>();
		all.add(new Book("java",23));
		all.add(new Book("java",23));
		all.add(new Book("java",23));
        // 默认的操作方法是按照地址比较的,所以我们要覆写equals()方法
		all.remove(new Book("java",23));
		System.out.println(all);
	}
}

2.2.2 旧的子类:vector

public class TestDemo{
	public static void main(String args[]){
		List<Book> all = new Vector<Book>();
		all.add(new Book("java",23));
		all.add(new Book("java",23));
		all.add(new Book("java",23));
		all.remove(new Book("java", 23));
		System.out.println(all);
	}
}

面试题:请解释Vector和ArrayList的区别 在这里插入图片描述
List接口学习总结

  • List中的数据保存顺序就是数据的添加顺序,对数据的添加有精准的控制;
  • List集合中可以保存重复的元素;
  • List接口比Collection接口扩充了一个get()方法;
  • List接口选择子类就使用ArrayList。

2.3 Set接口

在collection接口下又有一个比较常用的子接口set接口,set子接口并没有对collection接口进行大量的扩充,而是简单的继承了Collection接口,当然也没有get()方法.
Set接口有两个常用的子类:HashSet、TreeSet。

范例:观察HashSet子类的特点

public class TestDemo{
    	public static void main(String args[]){
    		Set<String> all = new HashSet<String>();
    		all.add("m");
    		all.add("a");
    		all.add("b");
    		all.add("c");
    		all.add("d");
    		all.add("d");
    		System.out.println(all);
    
    	}
}

}在这里插入图片描述

  • 通过代码演示可以发现,Set集合下没有重复的元素(这一点是Set接口的特征),同时发现里面保存的数据是无序排列的,即:HashSet子类的特征属于无序排列。

范例:使用TreeSet子类

public class TestDemo{
	public static void main(String args[]){
		Set<String> all = new TreeSet<String>();
		all.add("m");
		all.add("a");
		all.add("b");
		all.add("c");
		all.add("d");
		all.add("d");
		System.out.println(all);
	}
}

在这里插入图片描述

  • 此时使用了TreeSet子类,没有重复数据,以及保存的内容自动排序。

2.4 关于数据排序的说明

集合就是一个动态的对象数组,那么如果要想为一组对象进行排序,在java里面必须要使用比较器,应该使用Comparable完成比较。在比较方法里面需要将这个类的所有属性都一起参与比较

class Book implements Comparable<Book> {
	private String title;
	private double price;
	public Book(String title,double price) {
		this.title = title;
		this.price = price;
	}
	public String toString() {
		return "书名:" + this.title + ",价格:" + this.price + "\n";
	}
	@Override
	public int compareTo(Book o) {
		if (this.price > o.price) {
			return 1;
		} else if (this.price < o.price) {
			return -1;
		}
		return this.title.compareTo(o.title);
	}
}
public class TestDemo {
	public static void main(String args[]) {
		Set<Book> all = new TreeSet<Book>();
		all.add(new Book("java开发",79.8));
		all.add(new Book("java开发",79.8)); // 全部重复
		all.add(new Book("JSP开发",79.8)); // 价格重复
		all.add(new Book("Android开发",89.8)); // 都不重复
		System.out.println(all);
	}
}
  • 通过检测发现TreeSet主要是依靠Comparable接口中的ComparaTo()方法判断是否重复,如果返回是0,那么就是重复数据,不会被保存。

2.5 关于重复元素的说明

Comparable 接口只负责 TreeSet 子类进行重复元素的判断,它并不是真正的用于能够进行元素的验证的操作。现在只能够依靠Object类中的所提供的方法:

方法释义
public int hashCode()取得哈希码
public boolean equals(Object obj)对象比较

范例:(重写hashCode和equals方法)

class Book implements Comparable<Book> {
	private String title;
	private double price;
	public Book(String title,double price) {
		this.title = title;
		this.price = price;
	}
	public String toString(){
		return "书名:" + this.title + ",价格:" + this.price + "\n";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		long temp;
		temp = Double.doubleToLongBits(price);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		result = prime * result + ((title == null) ? 0 : title.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Book other = (Book) obj;
		if (Double.doubleToLongBits(price) != Double.doubleToLongBits(other.price))
			return false;
		if (title == null) {
			if (other.title != null)
				return false;
		} else if (!title.equals(other.title))
			return false;
		return true;
	}
	@Override
	public int compareTo(Book o) {
		if(this.price > o.price){
			return 1;
		}else if(this.price < o.price){
			return -1;
		}
		return this.title.compareTo(o.title);
	}
}
public class TestDemo {
	public static void main(String args[]) {
		Set<Book> all = new HashSet<Book>();
		all.add(new Book("java开发",79.8));
		all.add(new Book("java开发",79.8));//全部重复
		all.add(new Book("JSP开发",79.8));//价格重复
		all.add(new Book("Android开发",89.8));//都不重复
		System.out.println(all);
	}
}

以后在非排序的情况下,只要是判断重复元素依靠的都是hashCode()与equals()方法。

2.6 集合输出
2.6.1 迭代输出:Iterator

一般集合的输出操作都会使用Iterator接口,观察Iterator接口的定义。

public interface Iterator<E> {
	public boolean hasNext();
	public E next();
}

Iterator本身是一个接口,如果想取得本接口实例化对象只能依靠Collection,Collection中定义一个操作方法: public Iterator iterator();

范例: 使用Iterator输出集合

public class Test{
	public static void main(String args[]){
		
		Set<String> all = new HashSet<String>();
		all.add("呵呵");
		all.add("和合伙");
		Iterator<String> iter=all.iterator();
		while(iter.hasNext()){
			String str = iter.next();
			System.out.println(str);
		}
	}
}

2.6.2 双向迭代:ListIterator

Iterator具备由前向后的输出,但是有的人想要实现由后向前可以使用Iterator的子接口ListIterator接口。

主要掌握两个方法:

方法释义
public boolean hasPrevious()判断是否有前一个元素
public E previous()取得前一个元素

ListIterator是专门为List子接口定义的输出接口,方法:public ListIterator listIterator。

范例:完成双向迭代

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class Test{
	public static void main(String args[]){
		
		List<String> all = new ArrayList<String>();
		all.add("a");
		all.add("b");
		all.add("c");
		ListIterator<String> iter=all.listIterator();
		while(iter.hasNext()){
			String str=iter.next();
			System.out.print(str+"、");
		}
		System.out.println("\n由后向前:");
		while(iter.hasPrevious()){
			String str = iter.previous();
			System.out.print(str+"、");
		}
	}
}
  • 如果想要实现由后向前的输出操作,一定要首先发生由前向后的输出。

2.6.3 foreach输出

public class Test{
	public static void main(String args[]){
		
		List<String> all = new ArrayList<String>();
		all.add("a");
		all.add("b");
		all.add("c");
		for(String str : all){
			System.out.print(str);
		}	
	}
}

2.6.4 Enumeration输出

Enumeration是与Vector类一起在JDK1.0的时候推出的输出接口,早期的Vector用Enumeration接口完成,此接口的定义如下:

方法释义
public boolean hasMoreElements()判断是否有下一个元素
public E nextElement()取得当前元素
public Enumeration elements()取得Enumeration 操作对象

范例:Enumeration使用

public class Test{
	public static void main(String args[]){
		
		Vector<String> all = new Vector<String>();
		all.add("a");
		all.add("b");
		all.add("c");
		Enumeration<String> enu=all.elements();
		while(enu.hasMoreElements()){
			String str = enu.nextElement();
			System.out.println(str);
		}
	}
}

2.7 Map 接口

Collection每一次都只会保存一个对象,而Map只要是负责保存一对对象的信息。
如果要现在要保存一对关联数据(key=value)的时候,那么使用Collection就不能满足要求,可以使用Map接口实现此类数据的保存,并且Map接口提供有根据key查找value的功能。

Map中定义的常用方法:

方法释义
public V put(K key,V value)普通 向集合中保存数据
public V get(Object key)普通 指定键所映射的值
public Set<Map.Entry<K,V>>普通 将Map集合转化为set集合
public Set keySet()取出全部的可以key
在Map接口下有两个常用的子类:HashMap、Hashtable
代码示例:
public class Test{
	public static void main(String args[]){
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 1);
		map.put("b", 2);
		map.put("c", 3);
		map.put("c", 17);
		System.out.println(map);
	}
}

通过以上的程序发现:

  • 使用HashMap定义的Map集合是无序存放的;

  • 如果出现重复的key会进行覆盖,新的value覆盖旧的

在HashMap接口里面提供有get()方法,这个方法的主要功能是根据key查找所需要的value。

public class Test{
	public static void main(String args[]){
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 1);
		map.put("b", 2);
		map.put("c", 3);
		map.put("c", 17);
		map.put(null, 0);  key 可以为空
		System.out.println(map.get("c"));
		System.out.println(map.get(null));//查询没有的key返回null,查询null返//回0
	}
}

在这里插入图片描述

  • 通过以上的验证可以发现,Map存放数据的目的是为了信息的查找,但是Collection存放数据是为了输出。

范例:取得Map的key

public class Test{
	public static void main(String args[]) {
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 1);
		map.put("b", 2);
		map.put("c", 3);
		map.put("c", 17);
		map.put(null, 0);
		Set<String> set = map.keySet();
        Iterator<String> ster = set.iterator();
        while (ster.hasNext()) {
        	System.out.println(ster.next());
        }
	}
}

范例:使用Hashtable

public class Test{
	public static void main(String args[]){
		Map<String, Integer> map = new Hashtable<String, Integer>();
		map.put("a", 1);
		map.put("b", 2);
		map.put("c", 3);
		map.put("c", 17);
		Set<String> set = map.keySet();
        Iterator<String> ster=set.iterator();
        while(ster.hasNext()){
        	System.out.println(ster.next());
        }
	}
}
  • Hashtable 里面对于key和value的数据都不允许使用null。
    在这里插入图片描述

2.8 关于Iterator输出的问题

之前一直强调集合的输出一定要使用Iterator完成,但是在Map接口里并没有定义可以返回Iterator接口对象的方法,分析Map和Collection集合保存数据的特点。

每当使用put()方法向Map集合里面保存一对数据的时候,实际上所有的数据都会被自动的封装为Map.Entry接口对象,那么来观察Map.Entry接口定义:

在这个接口里面定义两个操作:

  • 取得key:public K getKey()
  • 取得value:public V getValue()、

在这里插入图片描述
在Map集合中定义了一个将Map集合转换为Set集合的方法:

public  Set<Map.Entry<K,V>>  entrySet()
public static interface Map.Entry<K,V>

Map集合利用Iterator接口输出的步骤:

  • 利用Map接口的entryset()方法将Map集合变为Set集合,里面的泛型是Map.Entry;
  • 利用Set集合中的iterator()方法将Set集合进行Iterator输出;
  • 每一次Iterator循环输出的都是Map.Entry接口对象,利用此对象进行key与value的取出。

范例:利用Iterator实现Map接口的输出

public class Test3 {
	public static void main(String[] args) {
		Map<String, Integer> map = new Hashtable<String, Integer>();
		map.put("a", 1);
		map.put("b", 2);
		map.put("c", 3);
		map.put("c", 17);
		// 将Map集合变为Set集合,目的是为了使用iterator()方法
		Set<Map.Entry<String, Integer>> set = map.entrySet();
		Iterator<Map.Entry<String, Integer>> iter = set.iterator(); // 当然了也可以写成一句
		while (iter.hasNext()) {
			Map.Entry<String, Integer> me = iter.next();
			System.out.println(me.getKey() + "=" + me.getValue());
		}
	}
}

遍历 Map使用的三种方式

public class Test3{
     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");
      
      //第一种:普遍使用,二次取值,通过 key  找 value 
      System.out.println("通过Map.keySet遍历key和value:");
      for (String key : map.keySet()) {
       System.out.println("key= "+ key + " and value= " + map.get(key));
      }
      
      //第二种,通过键值对 对象 找key和value
      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());
      }
    
     }
}

在这里插入图片描述