Java基础(五十八)-类集框架

Java类集框架

1:Java类集框架简介

从JDK1.2开始Java引入了类集开发框架。 所谓的类集框架是一套动态对象数组的实现方案。

在实际的开发之中没有任何一项的开发可以离开数组,但是传统的数组实现起来非常繁琐,而且长度是致命伤,正式因为长度的问题,所以传统的数组是不可能大范围使用的,但是我们的开发又离不开数组,所以最初就只能依靠数据结构来实现动态的数组处理,而其中最为重要的两个结构: 链表,树(面对这些数据结构有如下问题:)

在这里插入图片描述

在这里插入图片描述

整个类集框架里面有如下的几个核心接口:

Collection,List,Set,Map,Iterator,Enumeration,Queue,ListIterator 。

2:Collection接口

java.util.Collection是单值集合操作的最大的父接口,在该接口之中定义有所有的单值数据的处理操作,这个接口当中定义有如下的操作方法。

在这里插入图片描述

在进行集合操作的时候有两个方法最为常用:【增加】add(),【输出】iterator();

在JDK1.5版本以前,Collection只是一个独立的接口,但是从JDK1.5之后提供有了Iterable父接口,并且在JDK1.8之后针对于Iterable接口也得到了一些扩充。在JDK1.2到JDK1.4 的时代要进行结合的使用,往往会直接操作Collection接口。但是从JDK1.5开始都是选择Collection的两个子接口:

允许重复的List子接口

不允许重复的Set子接口

在这里插入图片描述

3:list接口

List接口是Collection子接口,其最大的特点是允许保存有重复元素数据,该接口的定义如下:

在这里插入图片描述

List自己依然是一个接口,对于接口的使用规则一定要使用子类来完成定义,在List子接口中有三个常用子类:ArrsyList,LinkList,Vector

在这里插入图片描述

范例:观察List中的静态方法

import java.util.List;

public class Test {
	public static void main(String[] args) throws Exception {
		List<String> all = List.of("Hello", "World", "你好", "MLDN", "饿了么?");
		System.out.println(all); 
	}
}

在这里插入图片描述

这些操作并不是List传统用法,是之后新版本之后添加的新功能。

4:ArrayList子类

在这里插入图片描述在这里插入图片描述

案例:使用ArrayList实例化List父接口

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


public class Test {
	public static void main(String[] args) throws Exception {
		List<String> all = new ArrayList<String>() ;	// 为List父接口进行实例化
		all.add("Hello") ;
		all.add("Hello") ;	// 重复数据
		all.add("World") ;
		all.add("MLDN") ;
		System.out.println(all);
	}
}

在这里插入图片描述

上述程序总结List存储的特征;

1:保存的顺序就是其存储顺序;
2:List集合里面允许存在有重复数据;

在这里插入图片描述

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

public class Test {
	public static void main(String[] args) throws Exception {
		List<String> all = new ArrayList<String>() ;	// 为List父接口进行实例化
		all.add("Hello") ;
		all.add("Hello") ;	// 重复数据
		all.add("World") ;
		all.add("MLDN") ;
		all.forEach((str)->{
			System.out.print(str + "、");
		});
	}
}//注意:此种输出并不是在正常开发情况下考虑的操作形式

在这里插入图片描述

在这里插入图片描述

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

public class Test {
	public static void main(String[] args) throws Exception {
		List<String> all = new ArrayList<String>() ;	// 为List父接口进行实例化
		System.out.println("集合是否为空?" + all.isEmpty() + "、集合元素个数:" + all.size());
		all.add("Hello") ;
		all.add("Hello") ;	// 重复数据
		all.add("World") ;
		all.add("MLDN") ;
		all.remove("Hello") ; // 删除元素
		System.out.println("集合是否为空?" + all.isEmpty() + "、集合元素个数:" + all.size());
		all.forEach((str)->{
			System.out.print(str + "、");
		});
	}
}

在这里插入图片描述

ArrayList里面操作支持与之前编写的链表形式是非常相似的,但是它并不是使用链表实现的,通过类名称实际上就已经可以清楚的发现了,ArrayList应该封装的是一个数组。

在这里插入图片描述

通过构造方法可以发现,在ArrayList里面所包含的数据实际上就是一个对象数组,如果现在进行数据追加的时候发现ArrayList集合里面保存的对象数组的长度不够的时候会进行新的数据的开辟,同时将原始数组内容拷贝到新数组之中。

在这里插入图片描述在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

当ArrayList之中保存的容量不足的时候会采用成倍的方式增长,原始长度为10,那么下次的增长就是20.以此类推,在使用ArrayList子类的时候一定要估算出你的数据量会是多少,如果超过了10个,那么使用有参构造方法进行创建,以避免垃圾数组的空间产生。

5:ArrayList保存自定义类

在这里插入图片描述

import java.util.ArrayList;
import java.util.List;
class Person {
	private String name ;
	private int age ;
	public Person(String name,int age) {
		this.name = name ;
		this.age = age ;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true ; 
		}
		if (obj == null) {
			return false ;
		}
		if (!(obj instanceof Person)) {
			return false ;
		}
		Person per = (Person) obj ;
		return this.name.equals(per.name) && this.age == per.age ;
	}
	// setter、getter、构造略
	public String toString() {
		return "姓名:" + this.name + "、年龄:" + this.age ;
	}
}
public class Test {
	public static void main(String[] args) throws Exception {
		List<Person> all = new ArrayList<Person>();
		all.add(new Person("张三",30)) ;
		all.add(new Person("李四",16)) ;
		all.add(new Person("小强",78)) ;
		System.out.println(all.contains(new Person("小强",78)));
		all.remove(new Person("小强",78)) ;
		all.forEach(System.out::println); 	// 方法引用代替了消费型的接口
	}
}

在这里插入图片描述

在使用List保存自定义类对象的时候如果需要使用到contains(),remove()方法进行查询与删除处理的时候一定要保证类之中已经成功覆写了equals()方法。

6:LinkedList子类

在List接口里面还有另外一个比较常用的子类:LinkedList:基于链表的实现。

在这里插入图片描述
在这里插入图片描述

import java.util.LinkedList;
import java.util.List;

public class Test {
	public static void main(String[] args) throws Exception {
		List<String> all = new LinkedList<String>(); // 为List父接口进行实例化
		all.add("Hello");
		all.add("Hello"); // 重复数据
		all.add("World");
		all.add("MLDN"); 
		all.forEach(System.out::println);
	}
}

在这里插入图片描述

在这里插入图片描述
通过分析发现:LinkedList封装的就是一个链表实现。

在这里插入图片描述

7:Vector子类

在这里插入图片描述
在这里插入图片描述

import java.util.List;
import java.util.Vector;
public class Test {
	public static void main(String[] args) throws Exception {
		List<String> all = new Vector<String>(); // 为List父接口进行实例化
		all.add("Hell");
		all.add("Helo"); // 重复数据
		all.add("Wold");
		all.add("MLN"); 
		all.forEach(System.out::println);
	}


}

在这里插入图片描述

在这里插入图片描述

Vector类如果使用的是无参构造方法,则一定会默认开辟一个10长度的数组,而后其余的实现操作与ArrayList是相同的,通过源代码的分析可以发现Vector类之中的操作方法采用的都是synchronized同步处理的,而ArrayList并没有同步处理吗,所以Vector类之中的方法在多线程访问的时候属于线程安全的,但是性能不如ArrayList高。

8:Set集合接口

Set接口最大的特点就是不允许保存重复元素,是Collection 的子接口。

在这里插入图片描述

在这里插入图片描述

import java.util.Set;

public class Test {
	public static void main(String[] args) throws Exception {
		// 进行Set集合数据的保存,并且设置有重复的内容
		Set<String> all = Set.of("Hello", "World", "MLDN", "Hello", "World");
		all.forEach(System.out::println);	// 直接输出
	}

}

在这里插入图片描述

当使用of()这个方法的时候如果发现集合之中存在有重复元素会直接抛出异常。这与传统的Set集合不保存重复元素的特点一致,致不过自己抛出了异常而已。

Set集合的常规使用形式一定是依靠子类进行实例化的,所以Set接口之中有两个常用子类:HashSet,TreeSet。

9:HashSet子类

HashSet是Set接口里面使用最多的一个子类,其最大的特点就是保存时无序的,而HashSet子类继承关系如下

在这里插入图片描述

import java.util.HashSet;
import java.util.Set;
public class Test {
	public static void main(String[] args) throws Exception {
		Set<String> all = new HashSet<String>();
		all.add("MLDN");
		all.add("NiHao") ;
		all.add("Hello");
		all.add("Hello"); // 重复元素
		all.add("World");
		all.forEach(System.out::println); // 直接输出
	}
}

在这里插入图片描述

通过上述的结果就可以发现HashSet子类的操作特点:不允许保存重复元素(Set接口定义的),另外一点HashSet之中保存的数据是无序的。

10:TreeSet子类

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

import java.util.Set;
import java.util.TreeSet;
public class Test {
	public static void main(String[] args) throws Exception {
		Set<String> all = new TreeSet<String>();
		all.add("MLDN"); 
		all.add("NiHao") ;
		all.add("Hello");
		all.add("Hello"); // 重复元素
		all.add("World");
		all.forEach(System.out::println); // 直接输出
	}
}

在这里插入图片描述

当利用TreeSet保存的数据的时候所有的数据都是按照数据升序进行自动排序处理。

在这里插入图片描述

在这里插入图片描述

import java.util.Set;
import java.util.TreeSet;
class Person implements Comparable <Person> { // 比较器
	private String name ;
	private int age ;
	public Person(String name,int age) {
		this.name = name ;
		this.age = age ;
	}
	public String toString() {
		return "姓名:" + this.name + "、年龄:" + this.age ;
	}
	@Override
	public int compareTo(Person per) {
		if (this.age < per.age) {
			return -1 ;
		} else if (this.age > per.age) {
			return 1 ;
		} else {
			return this.name.compareTo(per.name) ; 
		}
	}
}

public class Test {
	public static void main(String[] args) throws Exception {
		Set<Person> all = new TreeSet<Person>(); // 为List父接口进行实例化
		all.add(new Person("张三",19)) ;
		all.add(new Person("李四",19)) ;	// 年龄相同,但是姓名不同
		all.add(new Person("王五",20)) ;	// 数据重复
		all.add(new Person("王五",20)) ;	// 数据重复
		all.add(new Person("小强",78)) ;
		all.forEach(System.out::println); 
	}

}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

import java.util.HashSet;
import java.util.Set;
class Person { // 比较器
	private String name ;
	private int age ;
	public Person(String name,int age) {
		this.name = name ;
		this.age = age ;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.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;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	public String toString() {
		return "姓名:" + this.name + "、年龄:" + this.age ;
	}
}

public class Test {
	public static void main(String[] args) throws Exception {
		Set<Person> all = new HashSet<Person>(); // 为List父接口进行实例化
		all.add(new Person("张三",19)) ;
		all.add(new Person("李四",19)) ;	// 年龄相同,但是姓名不同
		all.add(new Person("王五",20)) ;	// 数据重复
		all.add(new Person("王五",20)) ;	// 数据重复
		all.add(new Person("小强",78)) ;
		all.forEach(System.out::println); 
	}

}

在这里插入图片描述

在Java程序之中真正的重复元素的判断处理利用就是hashCode()与equals()两个方法共同作用完成的,而只有在排序要求的情况下(TreeSet)才会利用Comparrable接口来实现。

11:集合输出-Iterator(核心)

集合输出实际上从JDK1.8开始就在Iterable接口之中提供有一个forEach()方法,但是这种方法的输出并不是传统意义上的集合输出形式,并且也很难在实际的开发之中出现,对于操作而言,一共有四种输出形式:Iterator迭代输出(95%),ListIterator双向迭代输出(0.1%),Enumeration枚举输出(4.9%),foreach输出(与Iterator相当)。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

案例:使用Iterator输出

import java.util.Iterator;
import java.util.Set;

public class Test {
	public static void main(String[] args) throws Exception {
		Set<String> all = Set.of("Hello", "World", "MLDN");
		Iterator<String> iter = all.iterator() ; // 实例化Iterator接口对象
		while (iter.hasNext()) {
			String str = iter.next() ;
			System.out.println(str);
		}
	}

}

在这里插入图片描述

但是对于Iterator接口中的remove()方法的使用需要特别注意一下(如果不是必须不要使用)。实际上在Collection接口里面定义有数据的删除操作的方法,但是如果在进行迭代输出的过程里面如果你使用了Collection中的remove()方法会导致迭代失败。

案例:采用Collection集合中的remove()方法删除

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Test {
	public static void main(String[] args) throws Exception {
		Set<String> all = new HashSet<String>();
		all.add("Hello") ;
		all.add("World") ;
		all.add("MLDN") ;
		Iterator<String> iter = all.iterator() ; // 实例化Iterator接口对象
		while (iter.hasNext()) {
			String str = iter.next() ;
			if ("World".equals(str)) {
				all.remove("World") ; // Collection集合方法
			} else {
				System.out.println(str);
			}
		}
	}

}

在这里插入图片描述

上述的代码中无法进行数据的删除操作,那么此时只能够利用Iterator接口中的remove()方法删除。

范例:使用Iterator接口删除方法

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Test {
	public static void main(String[] args) throws Exception {
		Set<String> all = new HashSet<String>();
		all.add("Hello") ;
		all.add("World") ;
		all.add("MLDN") ;
		Iterator<String> iter = all.iterator() ; // 实例化Iterator接口对象
		while (iter.hasNext()) {
			String str = iter.next() ;
			if ("World".equals(str)) {
				iter.remove() ; // 删除当前的数据
			} else {
				System.out.println(str);
			}
		}
		System.out.println("*** " + all);
	} 

}

在这里插入图片描述

此时程序执行之后没有出现任何错误,并且可以成功的删除原始集合中的数据。

在这里插入图片描述

12:ListIterator输出

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

案例:实现双向迭代

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

public class Test {
	public static void main(String[] args) throws Exception {
		List<String> all = new ArrayList<String>();
		all.add("Hello") ;
		all.add("World") ;
		all.add("MLDN") ;
		ListIterator<String> iter = all.listIterator() ;
		System.out.print("由前向后输出:");
		while(iter.hasNext()) {
			System.out.print(iter.next() + "、");
		} 
		System.out.print("\n由后向前输出:");
		while (iter.hasPrevious()) {
			System.out.print(iter.previous() + "、");
		}
	} 

}

在这里插入图片描述

如果要想实现由后向前遍历,那么首先要实现的是由前向后向后实现遍历处理。

13:Enumeration输出

在这里插入图片描述

在这里插入图片描述

案例:使用Enumeration实现输出

import java.util.Enumeration;
import java.util.Vector;

public class Test {
	public static void main(String[] args) throws Exception {
		Vector<String> all = new Vector<String>();
		all.add("Hello") ;
		all.add("World") ;
		all.add("MLDN") ;
		Enumeration<String> enu = all.elements() ;
		while (enu.hasMoreElements()) {
			String str = enu.nextElement() ;
			System.out.print(str + "、");
		}
	} 

}

在这里插入图片描述

由于该接口出现的时间太长了,所以在一些比较早的开发之中,也有部分的方法支支持Enumeration输出操作,但是随着类方法的不断完善,大部分的类都直接利用Iterator实现了。

14:foreach输出

除了使用迭代接口实现输出的之外,从JDK1.5之后加强型for循环可以开始实现集合的输出了。这种输出的形式与数组操作形式类似。

范例:使用foreach输出

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

public class Test {
	public static void main(String[] args) throws Exception {
		List<String> all = new ArrayList<String>();
		all.add("Hello");
		all.add("World");
		all.add("MLDN");
		for (String str : all) {
			System.out.print(str + "、"); 
		}
	}

}

在这里插入图片描述

这种输出最初出现的时候很多人并不建议使用,因为便准的集合操作还是应该以Iterator为主。

15:Map集合接口

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

范例:观察Map集合的特点

import java.util.Map;

public class Test {
	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = Map.of("one", 1, "two", 2, "one", 101, null, 0);
		System.out.println(map);
	}

}

在这里插入图片描述

在这里插入图片描述

在正常的开发之中需要通过Map集合的子类来进行接口对象的实例化,而常用的子类:HashMap,Hashtable,TreeMap,LinkedHashMap。

16:HashMap子类

HashMap是Map接口之中最常见的一个子类,该类的主要特点是无序储存,定义形式如下:

在这里插入图片描述
观察Map集合的使用

import java.util.HashMap;
import java.util.Map;

public class Test {
	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = new HashMap<String,Integer>() ;
		map.put("one", 1) ;
		map.put("two", 2) ;
		map.put("one", 101) ;	// key重复
		map.put(null, 0) ;		// key为null
		map.put("zero", null) ; // value为null
		System.out.println(map.get("one"));	// key存在
		System.out.println(map.get(null));	// key存在
		System.out.println(map.get("ten"));	// key不存在
	}

}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

import java.util.HashMap;
import java.util.Map;

public class Test {
	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = new HashMap<String,Integer>() ;
		System.out.println(map.put("one", 1));	// key不重复,返回null
		System.out.println(map.put("one", 101));	// key重复,返回旧数据
	}

}

在这里插入图片描述

在设置了相同的key的内容的时候put()方法会返回原始的数据内容。

HashMap的源码如下:HashMap之中肯定需要存储大量的数据,那么对于数据的存储。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

17:LinkedHashMap

在这里插入图片描述

既然是链表保存,所以一般在使用LinkedHashMap类的时候往往数据量都不需要特别大,因为会造成时间复杂度攀升,通过继承结构可以发现LinkedHashMap是HashMap的子类,继承关系如下:

在这里插入图片描述

import java.util.Map;
import java.util.LinkedHashMap;


public class Test {
	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = new LinkedHashMap<String,Integer>() ;
		map.put("one", 1) ;
		map.put("two", 2) ;
		map.put("one", 101) ;	// key重复
		map.put(null, 0) ;		// key为null
		map.put("zero", null) ; // value为null
		System.out.println(map);	// key不存在
	}	
}

在这里插入图片描述

通过此时的程序执行可以发现当使用LinkedHashMap进行存储之后所有数据的保存顺序为添加顺序。

18:Hashtable

在这里插入图片描述

在这里插入图片描述

观察Hashtable子类的使用

import java.util.Hashtable;
import java.util.Map;

public class JavaAPIDemo {

	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = new Hashtable<String,Integer>() ; 
		map.put("one", 1) ;
		map.put("two", 2) ;
		map.put("one", 101) ;	// key重复
		System.out.println(map);	// key不存在
	}
	
}

在这里插入图片描述

通过观察发现Hashtable里面进行数据存储的时候设置的key或value都不允许为null,否则会出现NullPointException异常。

在这里插入图片描述

19:TreeMap子类

在这里插入图片描述

案例:使用TreeMap实现排序

import java.util.Map;
import java.util.TreeMap;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = new TreeMap<String, Integer>();
		map.put("C", 3);
		map.put("B", 2);
		map.put("A", 1);
		System.out.println(map);
	}

}

在这里插入图片描述

20:Map.Entry接口

清楚了整个Map集合的基本形式,但是依然需要有一个核心的问题:Map集合里面是如何进行数据存储的?对于List而言(LinkedList子类)依靠的是链表的形式实现的数据存储,那么在进行数据存储的时候一定要将数据保存在一个Node节点之中,虽然在HashMap里面也可以见到Node类型的定义,通过源代码定义卡可以发现,HashMap类中的Node内部类本身实现了Map.Entry接口。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

import java.util.Map;

public class JavaAPIDemo {

	public static void main(String[] args) throws Exception {
		Map.Entry<String, Integer> entry = Map.entry("one", 1) ;
		System.out.println("获取Key:" + entry.getKey());
		System.out.println("获取Value:" + entry.getValue());
		System.out.println(entry.getClass().getName()); // 观察使用的子类
	}

}

在这里插入图片描述

21:使用Iterator输出Map集合

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class JavaAPIDemo {

	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("one", 1);
		map.put("two", 2);
		Set<Map.Entry<String,Integer>> set = map.entrySet() ;	// 将Map集合变为Set集合
		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());
		}
	}

}

在这里插入图片描述

在这里插入图片描述


import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class JavaAPIDemo {

	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("one", 1);
		map.put("two", 2);
		Set<Map.Entry<String,Integer>> set = map.entrySet() ;	// 将Map集合变为Set集合
		for (Map.Entry<String, Integer> entry : set) {
			System.out.println(entry.getKey() + " = " + entry.getValue());
		}
	}

}

在这里插入图片描述

22:关于KEY的定义

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

案例:使用自定义类作为Key类型

import java.util.HashMap;
import java.util.Map;

class Person {
	private String name;
	private int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.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;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
}

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值