5、集合类
集合类的由来:
对象用于封装特有数据,对象多了需要存储;如果对象的个数不确定,就使用集合容器进行存储。
集合容器因为内部的数据结构不同,有多种具体容器。不断的向上抽取,就形成了集合框架。
集合的特点:
1.、用于存储对象的容器。
2、集合的长度是可变的
3、集合中不可以存储基本数据类型值
集合框架的构成及分类:
集合和数组的区别:
1、长度区别:数组的长度固定;集合长度可变
2、内容区别:数组存储的是同一种类型的元素;而集合可以存储不同类型的元素
3、元素的数据类型区别:数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用类型
5.1 Collection接口
Collection:是集合的顶层接口,它的子体系有重复的,有唯一的,有有序的,有无序的。
Collection接口的常用方法:
5.1.1 添加功能
boolean add(Object obj):添加一个元素
boolean addAll(Collection c):添加一个集合的元素
5.1.2 删除功能
boolean remove(Object o):移除一个元素
boolean removeAll(Collection c):移除一个集合的元素(是一个还是所有)
void clear():移除所有元素
5.1.3 判断功能
boolean contains(Object o):判断集合中是否包含指定的元素
boolean containsAll(Collection c):判断集合中是否包含指定的集合元素(是一个还是所有)
boolean isEmpty():判断集合是否为空
5.1.4 获取功能
int size():元素的个数
Iterator iterator():
取出元素的方式:迭代器。该对象必须依赖于具体容器,因为每一个容器的数据结构都不同,所以该迭代器对象是在容器中进行内部实现的,也就是iterator方法在每个容器中的实现方式是不同的。
对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,也就是iterator方法。
Iterator接口就是对所有的Collection容器进行元素取出的公共接口。
5.1.5 其他功能
boolean retainAll(Collection coll);取交集
Object toArray();将集合转成数组
示例1:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo {
public static void main(String[] args) {
Collection coll = new ArrayList();
show(coll);
System.out.println("---------------------------------");
Collection c1 = new ArrayList();
Collection c2 = new ArrayList();
show(c1, c2);
}
public static void show(Collection coll) {
// 1、添加元素,add
coll.add("abc1");
coll.add("abc2");
coll.add("abc3");
System.out.println("coll:" + coll);
// 2、删除元素,remove
coll.remove("abc2");// 会改变集合的长度
System.out.println("coll:" + coll);
// 清空集合
// coll.clear();
System.out.println(coll.contains("abc1"));
}
public static void show(Collection c1, Collection c2) {
// 给c1添加元素
c1.add("abc1");
c1.add("abc2");
c1.add("abc3");
c1.add("abc4");
// 给c2添加元素
c2.add("abc2");
c2.add("abc6");
c2.add("abc7");
System.out.println("c1:" + c1);
System.out.println("c2:" + c2);
// 演示addAll
// 将c2中的元素添加到c1中
System.out.println("addAll:" + c1.addAll(c2));
System.out.println("c1:" + c1);
// 演示removeAll
// 从c1集合中删除与c2集合相同的元素
System.out.println("removeAll:" + c1.removeAll(c2));
System.out.println("c1:" + c1);
// 演示containsAll
System.out.println("containsAll:" + c1.containsAll(c2));
// 演示retainAll
// A对B做交集,最终的结果保存在A中,B不变
//返回值表示的是A是否发生改变
System.out.println("retainAll:"+c1.retainAll(c2));
System.out.println("c1、c2交集:" + c1);
}
}
运行结果:
示例2:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorDemo {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add("abc1");
coll.add("abc2");
coll.add("abc3");
coll.add("abc4");
System.out.println(coll);
// 使用了Collection中的iterator()方法。调用集合中的迭代器方法,是为了获取集合中的迭代器对象。
Iterator it1 = coll.iterator();
while (it1.hasNext()) {
System.out.println(it1.next());
}
System.out.println("------");
// for循环结束,Iterator变量内存释放,更高效
for (Iterator it2 = coll.iterator(); it2.hasNext();) {
System.out.println(it2.next());
}
}
}
运行结果:
5.2 List及其子类
Collection
|--List:有序(存入和取出的顺序一致),元素都有索引(角标),允许重复元素。
|--Set:元素不能重复,无序。
5.2.1 List
List
|--ArrayList
|--Vector
|--LinkedList
1、List:特有的常见方法。
有一个共性特点就是都可以操作角标。
1)、添加功能:
void add(int index,Object element):在指定位置添加元素
2)、删除功能:
Object remove(int index):根据索引删除元素,返回被删除的元素
3)、修改功能:
Object set(int index,Object element):根据索引修改元素,返回被修改的元素
4)、获取功能:
Object get(int index):获取指定位置的元素
int indexOf(Object o):返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
int lastIndexOf(Object o):返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
List subList(int fromIndex,int toIndex)返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。
5)、列表迭代器
ListIterator listIterator():List集合特有的迭代器
示例1:
import java.util.ArrayList;
import java.util.List;
public class ListDemo{
public static void main(String[] args){
List list = new ArrayList();
show(list);
}
public static void show(List list){
//添加元素
list.add( "abc1" );
list.add( "abc2" );
list.add( "abc3" );
System.out.println(list); // [abc1, abc2, abc3]
//插入元素
list.add(1, "abc2" );// [abc1 abc2 abc2 abc3]
//删除元素
System.out.println( "remove:" + list.remove(2)); // remobe:abc2 [abc1 abc2 abc3 ]
//修改元素
System.out.println( "set:" + list.set(1,"abc8" )); // set:abc2 [abc1 abc8 abc3]
//获取元素:
System.out.println( "get:" + list.get(0)); // get:abc1
//获取子列表
System.out.println( "sublist:" + list.subList(1,2));// sublit:abc8
System.out.println(list); //[abc1 abc8 abc3]
}
}
运行结果:
示例2:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class ListDemo{
public static void main(String[] args){
List list = new ArrayList();
show(list);
}
public static void show(List list){
list.add( "abc1");
list.add( "abc2");
list.add( "abc3");
list.add( "abc4");
//使用迭代器遍历集合
Iterator it = list.iterator();
while(it.hasNext()){
String s = (String)it.next();
System.out.println(s);
}
System.out.println("---------------");
//list特有的取出元素的方式之一
for(int x = 0; x < list.size(); x++){
String s = (String)list.get(x);
System.out.println(s);
}
}
}
运行结果:
示例3:List特有的列表迭代器ListIterator
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/*
* 列表迭代器:
* ListIterator listIterator():List集合特有的迭代器
* 该迭代器 继承了Iterator迭代器,所以可以直接使用hasNext()和next()方法
* 特有功能:
* Object previous():获取上一个元素
* boolean hasPrevious:判断是否有元素
*
* 注意:ListIterator可以实现逆向遍历,但是必须先正向遍历,所以一般无意义,不使用。
*/
public class ListDemo {
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("java");
//ListIterator listIterator()
ListIterator lit = list.listIterator();
while(lit.hasNext()){
String s = (String)lit.next();
System.out.println(s);
}
System.out.println("-------------");
//逆向遍历
while(lit.hasPrevious()){
String s = (String)lit.previous();
System.out.println(s);
}
}
}
运行结果:
练习:有一个集合[hello world java],想判断里面有没有"world"这个元素,如果有,就添加一个"javaee"元素,请写代码实现。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListDemo2 {
public static void main(String[] args) {
// 创建List集合对象
List list = new ArrayList();
// 添加元素
list.add("hello");
list.add("world");
list.add("java");
// 迭代器遍历
for (Iterator it = list.iterator(); it.hasNext();) {
String s = (String) it.next();
if (s.equals("world")) {
list.add("javaee");
}
}
System.out.println("list:" + list);
}
}
运行结果:
出错分析:迭代器是依赖于集合而存在的,在判断成功后,集合的中新添加了元素,而迭代器却不知道,所以就报错了,这个错叫并发修改异常(ConcurrentModificationException)。其实这个问题描述的是:迭代器遍历元素的时候,通过集合是不能修改元素的。
解决办法1:使用List特有的迭代器迭代元素,用迭代器修改元素。这样添加完元素后,“javaee”会在“world”后面。
解决办法2:集合遍历元素,集合修改元素(普通for循环)。这样添加完元素后,“javaee”会在集合的最后面。
方式1:
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListDemo2 {
public static void main(String[] args) {
// 创建List集合对象
List list = new ArrayList();
// 添加元素
list.add("hello");
list.add("world");
list.add("java");
//方式1:迭代器迭代元素,迭代器修改元素
ListIterator lit = list.listIterator();
while(lit.hasNext()){
String s = (String)lit.next();
if(s.equals("world")){
lit.add("javaee");
}
}
System.out.println("list:" + list);
}
}
运行结果:
方式2:
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListDemo2 {
public static void main(String[] args) {
// 创建List集合对象
List list = new ArrayList();
// 添加元素
list.add("hello");
list.add("world");
list.add("java");
//方式2:集合遍历元素,集合修改元素
for(int x = 0;x <list.size();x++){
String s = (String)list.get(x);
if(s.equals("world")){
list.add("javaee");
}
}
System.out.println("list:" + list);
}
}
运行结果:
5.2.2 List的子类
List:
|--ArrayList:底层数据结构是数组,查询快,增删慢。线程不安全,效率高
|--Vector:底层数据结构是数组,查询快,增删慢。线程安全,效率低
|--LinkedList: 底层数据结构是链表,查询慢,增删快。线程不安全,效率高
ArrayList相对于父类List来说,没有什么常用的特殊功能。
1、Vector特有功能
添加功能:
public void addElement(Object obj):相当于add(Object obj)
获取功能:
public Object elementAt(int index):相当于get(int index)
public Enumeration elements():相当于Iterator iterator()
boolean hasMoreElements():相当于hasNext()
Object nextElement():相当于next()
public class VectorDemo {
public static void main(String[] args) {
//创建集合对象
Vector v = new Vector();
//添加功能
v.addElement("hello");
v.addElement("world");
v.addElement("java");
//普通for循环遍历
for(int x = 0;x<v.size();x++){
String s = (String)v.elementAt(x);
System.out.println(s);
}
System.out.println("--------");
//
Enumeration e = v.elements();
while(e.hasMoreElements()){
String s = (String)e.nextElement();
System.out.println(s);
}
}
}
运行结果:
2、LinkedList特有功能
添加功能:
public void addFirst(Object e):
public void addLast(Object e):
删除功能:
public Object removeFirst():获取并移除,如果链表为空,抛出NoSuchElementException。
public Object removeLast():
获取功能:
public Object getFirst()://获取但不移除,如果链表为空,抛出NoSuchElementException。
public Obejct getLast():
import java.util.Iterator;
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList link = new LinkedList();
link.addFirst("abc1");
link.addFirst("abc2");
link.addFirst("abc3");
link.addFirst("abc4");
Iterator it = link.iterator();
while (it.hasNext()) {
System.out.println("next:" + it.next());
}
System.out.println(link);
System.out.println("getFirst:" + link.getFirst()); // 获取第一个,但是不删除。
System.out.println("getLast:" + link.getLast());
System.out.println("removeFirst:" + link.removeFirst()); // 获取第一个,但是删除
System.out.println("removeLast:" + link.removeLast());
// 删除所有元素的方法
while (!link.isEmpty()) {
System.out.println(link.removeFirst());
}
}
}
运行结果:
List子类的练习:
练习1:ArrayList去除集合中字符串的重复值(字符串的内容相同)
import java.util.ArrayList;
import java.util.Iterator;
/*
* ArrayList去除集合中字符串的重复值(字符串的内容相同)
* 分析:
* A:创建集合对象
* B:添加多个字符串元素
* C:创建新集合
* D:遍历旧集合,获取得到每一个元素
* E:拿这个元素到新集合去找,看有没有
* 有:不搭理他
* 没有:就添加到新集合中
* F:遍历新集合
*/
public class ArrayListDemo {
public static void main(String[] args) {
//创建集合对象
ArrayList array = new ArrayList();
//添加多个字符串元素
array.add("hello");
array.add("world");
array.add("java");
array.add("java");
array.add("java");
array.add("world");
array.add("hello");
array.add("hello");
array.add("world");
for(int x = 0;x<array.size();x++){
String s = (String)array.get(x);
System.out.print(s+" ");
}
System.out.println();
//创建新集合
ArrayList newArray = new ArrayList();
//遍历就集合,获取每一个元素
Iterator it = array.iterator();
while(it.hasNext()){
String s = (String)it.next();
if(!newArray.contains(s)){
newArray.add(s);
}
}
//遍历新集合
for(int x = 0;x<newArray.size();x++){
String s = (String)newArray.get(x);
System.out.print(s+" ");
}
}
}
运行结果:
如果不创建新的集合,就在之前的集合中操作,该怎么做呢?
import java.util.ArrayList;
public class ArrayListDemo2 {
public static void main(String[] args) {
// 创建集合对象
ArrayList array = new ArrayList();
// 添加多个字符串元素
array.add("hello");
array.add("world");
array.add("java");
array.add("java");
array.add("java");
array.add("world");
array.add("hello");
array.add("hello");
array.add("world");
//通过数组里选择排序的思想
//遍历集合
for(int x = 0;x<array.size()-1;x++){
for(int y = x;y<array.size()-1;y++){
if(array.get(x).equals(array.get(y+1))){
array.remove(y+1);
y--;//只要删除后,一定要做一次y--,否则会出现去除不完整的结果。
}
}
}
for(int x = 0;x <array.size();x++){
String s = (String)array.get(x);
System.out.println(s);
}
}
}
运行结果:
练习2:去除集合中自定义对象的重复值(对象的成员变量值都相同)
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
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 class ArrayListDemo3 {
public static void main(String[] args) {
// 创建集合对象
ArrayList array = new ArrayList();
// 创建学生对象
Student s1 = new Student("林青霞", 27);
Student s2 = new Student("林志玲", 40);
Student s3 = new Student("芙蓉姐姐", 30);
Student s4 = new Student("貂蝉", 27);
Student s5 = new Student("林青霞", 27);
Student s6 = new Student("林青霞", 30);
// 添加元素
array.add(s1);
array.add(s2);
array.add(s3);
array.add(s4);
array.add(s5);
array.add(s6);
// 创建新集合
ArrayList array2 = new ArrayList();
// 遍历旧集合
for (int x = 0; x < array.size(); x++) {
Student s = (Student) array.get(x);
if (!array2.contains(s)) {
array2.add(s);
}
}
// 遍历新集合
for (Iterator it = array2.iterator(); it.hasNext();) {
Student s = (Student) it.next();
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
运行结果:
为什么按照去除重复字符串的方法来操作自定义对象,运行后并没有去除自定义对象的重复值呢?
遇到这种情况,我们要分析下哪里可能出问题。简单分析后,发现问题出在了判断上,通过查看contains()方法的源码,发现contains()方法的底层依赖的是equals()方法。而学生类中并没有复写equals()方法,所以调用的是父类Object()中的equals()方法,比较的是地址值,所以会出现这样的结果。解决办法就是在学生类中复写下equals()方法。
练习3:请用LinkedList模拟栈或者队列数据结构的集合,并测试。
模拟栈:
import java.util.Iterator;
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
//A:LinkedList的特有添加功能:addFirst()
//B:栈的特点:先进后出
//创建集合对象
LinkedList link = new LinkedList();
//添加元素
link.addFirst("hello");
link.addFirst("world");
link.addFirst("java");
//遍历
Iterator it = link.iterator();
while(it.hasNext()){
String s = (String)it.next();
System.out.println(s);
}
}
}
运行结果:
我们发现运行后满足了栈数据结果的特点:先进后出。但是这种做法是不对的。因为题目的意思应该是:定义一个集合类(容器),给使用者提供一个集合对象完成这两种结构中的一种,在这个集合类内部可以使用LinkedList。正确做法:
import java.util.LinkedList;
/*
* 栈的特点是先进后出。元素弹栈后便从栈中消失。
*/
public class MyStack {
private LinkedList link;
public MyStack(){
link = new LinkedList();
}
//栈的添加功能
public void myAdd(Object obj){
link.addFirst(obj);
}
//栈的获取功能
public Object myGet(){
return link.removeFirst();//因为元素弹栈后便从栈中消失,所以使用LinkedList的removeFirst()方法,而不是getFirst()方法
}
public boolean isEmpty(){
return link.isEmpty();
}
}
public class MyStackDemo {
public static void main(String[] args) {
//创建MyStack的对象
MyStack ms = new MyStack();
//调用添加功能
ms.myAdd("abc1");
ms.myAdd("abc2");
ms.myAdd("abc3");
while(!ms.isEmpty()){
String s = (String)ms.myGet();
System.out.println(s);
}
}
}
运行结果:
** JDK1.5新特性
1、泛型
泛型是一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。参数化类型,把类型当作参数一样的传递。格式:<数据类型>。此处的数据类型只能是引用类型。
1)泛型类
泛型类:把泛型定义在类上
public class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
public class ObejctToolDemo {
public static void main(String[] args) {
ObjectTool<String> ot = new ObjectTool<String>();
//ot.setObj(new Integer(21)); //这个时候便编译期间就过不去
ot.setObj(new String("林青霞"));
String s = ot.getObj();
System.out.println("name:"+s);
ObjectTool<Integer> ot2 = new ObjectTool<Integer>();
ot2.setObj(new Integer("20"));
Integer i = ot2.getObj();
System.out.println("age:"+i);
}
}
运行结果:
2)泛型方法
泛型方法:把泛型定义在方法上
public class ObjectTool{
public<T> void show(T t){
System.out.println(t);
}
}
public class ObjectToolDemo {
public static void main(String[] args) {
/* ObjectTool ot = new ObjectTool();
ot.show("hello");
ot.show(20);
ObjectTool<String> ot = new ObjectTool<String>();
ot.show("hello");
ObjectTool<Integer> ot2 = new ObjectTool<Integer>();
ot2.show(20);
ObjectTool<Boolean> ot3 = new ObjectTool<Boolean>();
ot3.show(true);*/
//定义泛型方法后
ObjectTool ot = new ObjectTool();
ot.show("hello");
ot.show(10);
ot.show(true);
}
}
运行结果:
3)泛型接口
泛型接口:把泛型定义在接口上
public interface Inter<T> {
public abstract void show(T t);
}
public class InterImpl<T> implements Inter<T>{
@Override
public void show(T t) {
System.out.println(t);
}
}
public class InterDemo {
public static void main(String[] args) {
//第一种情况测试
//Inter<String> i = new InterImpl();
//i.show("show");
//第二种情况测试
Inter<String> i = new InterImpl<String>();
i.show("hello");
Inter<Integer> ii = new InterImpl<Integer>();
ii.show(100);
}
}
运行结果:
4)泛型之通配符
?:任意类型,如果没有明确,那么就是Object以及任意的Java类了
? extends E:向下限定,E及其子类
? super E:向上限定,E极其父类
class Animal{}
class Dog extends Animal{
}
public class GenericDemo {
public static void main(String[] args) {
//泛型如果明确的写的时候,前后必须一致。
Collection<Object> c1 = new ArrayList<Object>();
Collection<Animal> c2 = new ArrayList<Animal>();
//Collection<Animal> c3 = new ArrayList<Dog>();
//?表示任意的类型都是可以的
Collection<?> c4 = new ArrayList<Object>();
Collection<?> c5 = new ArrayList<Animal>();
Collection<?> c6 = new ArrayList<Dog>();
//? extends E :向下限定,E及其子类
//Collection<? extends Animal> c7 = new ArrayList<Object>();
Collection<? extends Animal> c8 = new ArrayList<Animal>();
Collection<? extends Animal> c9 = new ArrayList<Dog>();
//? super E:向上限定,E极其父类
Collection<? super Animal> c10 = new ArrayList<Object>();
Collection<? super Animal> c11 = new ArrayList<Animal>();
//Collection<? super Animal> c12 = new ArrayList<Dog>();
}
}
2、增强for
增强for是for循环的一种。
格式:
for(元素数据类型 变量 : 数组或者Collection集合){
使用变量即可,该变量就是元素
}
public class ForDemo {
public static void main(String[] args) {
// 定义一个int数组
int[] arr = { 1, 2, 3, 4, 5 };
for (int x = 0; x < arr.length; x++) {
System.out.print(arr[x] + " ");
}
System.out.println("\n--------");// \n: 换行
for (int x : arr) {
System.out.print(x + " ");
}
System.out.println("\r--------");// \r: 回车
// 定义一个字符串数组
String[] strArray = { "林青霞", "风清扬", "令狐冲", "阳顶天" };
// 增强for
for (String s : strArray) {
System.out.println(s);
}
System.out.println("--------");
// 定义一个集合
ArrayList<String> array = new ArrayList<String>();
array.add("hello");
array.add("world");
array.add("java");
// 增强for
for (String s : array) {
System.out.println(s);
}
}
}
运行结果:
注意:1)增强for其实是用来替代迭代器的。
2)增强for的目标不能为null。解决办法:先判断是否为null。
public class ForDemo {
public static void main(String[] args) {
List<String> list = null;
// NullPointerException
// 这个s是我们从list里面获取出来的,在获取前,它肯定还要做一个判断
// 说白了,这就是迭代器的功能
if (list != null) {
for (String s : list) {
System.out.println(s);
}
}
}
}
3、静态导入
要使用静态成员(方法和变量)我们必须给出提供这个静态成员的类。使用静态导入可以使被导入类的静态变量和静态方法在当前类直接可见,使用这些静态成员无需再给出他们的类名。
格式:格式:import static 包名….类名.方法名;
import static java.lang.Math.abs;
import static java.lang.Math.pow;
import static java.lang.Math.max;
public class StaticImportDemo {
public static void main(String[] args) {
System.out.println(abs(-100));
//System.out.println(java.lang.Math.abs(-100));
System.out.println(pow(2, 3));
System.out.println(max(20, 30));
}
}
运行结果:
4、可变参数
适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。
格式:
修饰符 返回值类型 方法名(数据类型… 变量名){
}
/*
* 需求:写一个求和的功能,到底是几个数据求和呢,我不太清楚,但是我知道在调用的时候我肯定就知道了
*/
public class ArgsDemo {
public static void main(String[] args) {
// 2个数求和
int a = 10;
int b = 20;
int result = sum(a, b);
System.out.println("rusult:" + result);
// 3个数求和
int c = 30;
result = sum(a, b, c);
System.out.println("result:" + result);
}
public static int sum(int... a) {
int s =0;
for(int x:a){
s+=x;
}
return s;
}
}
运行结果:
注意:可变参数必须位于最后一项。
5、List集合练习
练习1:集合的嵌套遍历
/*
* 集合的嵌套遍历
* 需求:
* 我们班有学生,每一个学生是不是一个对象。所以我们可以使用一个集合表示我们班级的学生。ArrayList<Student>
* 但是呢,我们旁边是不是还有班级,每个班级是不是也是一个ArrayList<Student>。
* 而我现在有多个ArrayList<Student>。也要用集合存储,怎么办呢?
* 就是这个样子的:ArrayList<ArrayList<Student>>
*/
public class ArrayListDemo {
public static void main(String[] args) {
// 创建大集合
ArrayList<ArrayList<Student>> bigArrayList = new ArrayList<ArrayList<Student>>();
// 创建第一个学生班级
ArrayList<Student> firstArrayList = new ArrayList<Student>();
Student s1 = new Student("唐僧", 30);
Student s2 = new Student("白龙马", 22);
Student s3 = new Student("孙悟空", 28);
// 把一班的学生添加进班级
firstArrayList.add(s1);
firstArrayList.add(s2);
firstArrayList.add(s3);
// 把第一个学生班级存储到学生系统中
bigArrayList.add(firstArrayList);
// 创建第二个学生班级
ArrayList<Student> secondArrayList = new ArrayList<Student>();
Student s4 = new Student("宋江", 37);
Student s5 = new Student("吴用", 39);
Student s6 = new Student("林冲", 35);
// 把二班的学生添加进班级
secondArrayList.add(s4);
secondArrayList.add(s5);
secondArrayList.add(s6);
// 把第二个学生班级存储到学生系统中
bigArrayList.add(secondArrayList);
// 创建第三个学生班级
ArrayList<Student> thirdArrayList = new ArrayList<Student>();
Student s7 = new Student("诸葛亮", 28);
Student s8 = new Student("关羽", 30);
Student s9 = new Student("张飞", 29);
// 把三班的学生添加进班级
thirdArrayList.add(s7);
thirdArrayList.add(s8);
thirdArrayList.add(s9);
// 把第三个学生班级存储到学生系统中
bigArrayList.add(thirdArrayList);
/*
* //迭代器迭代元素 for(Iterator<ArrayList<Student>> it =
* bigArrayList.iterator();it.hasNext();){ ArrayList<Student> array =
* it.next(); for(Iterator<Student> it2 =
* array.iterator();it2.hasNext();){ Student s= it2.next();
* System.out.println(s.getName()+"---"+s.getAge()); } }
*/
// 增强for迭代元素
for (ArrayList<Student> array : bigArrayList) {
for (Student s : array) {
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
}
运行结果:
练习2:获取10个1-20之间的随机数,要求不能重复。
import java.util.ArrayList;
import java.util.Random;
/*
* 获取10个1-20之间的随机数,要求不能重复
*
* 用数组实现,但是数组的长度是固定的,长度不好确定。
* 所以我们使用集合实现。
*
* 分析:
* A:创建产生随机数的对象
* B:创建一个存储随机数的集合。
* C:定义一个统计变量。从0开始。
* D:判断统计变量是否小于10
* 是:先产生一个随机数,判断该随机数在集合中是否存在。
* 如果不存在:就添加,统计变量++。
* 如果存在:就不搭理它。
* 否:不搭理它
* E:遍历集合
*/
public class RandomDemo {
public static void main(String[] args) {
// 创建产生随机数的对象
Random r = new Random();
// 创建一个存储随机数的集合
ArrayList<Integer> array = new ArrayList<Integer>();
// 定义一个统计变量
int count = 0;
//判断统计变量是否小于10
while(count<10){
int number = r.nextInt(20)+1;
//Integer i = Integer.valueOf(number);
if(!(array.contains(number))){
array.add(number);
count++;
}
}
for(Integer i:array){
System.out.println(i);
}
}
}
运行结果:
练习3:键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
/*
* 键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
*
* 分析:
* A:创建键盘录入数据对象
* B:键盘录入多个数据,我们不知道多少个,所以用集合存储
* C:以0结束,这个简单,只要键盘录入的数据是0,我就不继续录入数据了
* D:把集合转成数组
* E:对数组排序
* F:获取该数组中的最大索引的值
*/
public class ArrayListDemo {
public static void main(String[] args) {
//键盘录入数据
Scanner sc = new Scanner(System.in);
ArrayList<Integer> array = new ArrayList<Integer>();
while(true){
System.out.println("请输入数据:");
int number = sc.nextInt();
if(0==number){
break;
}
else{
array.add(number);
}
}
//把集合转成数组
//public <T> T[] toArray(T[] a)
Integer[] i = new Integer[array.size()];
//Integer[] ii = array.toArray(i);
array.toArray(i);
//System.out.println(i);
//System.out.println(ii);
//对数组进行排序
//public static void sort(Object[] a)
Arrays.sort(i);
System.out.println("数组是:"+arrayToString(i)+",最大值是:"+i[i.length-1]);
}
public static String arrayToString(Integer[] i){
StringBuilder sb = new StringBuilder();
sb.append("[");
for(int x =0;x<i.length;x++){
if(x!=(i.length-1)){
sb.append(i[x]+" ");
}
else{
sb.append(i[x]).append("]");
}
}
return sb.toString();
}
}
运行结果:
5.3 Set及其子类
5.3.1 Set
Set元素不可以重复,是无序。Set接口中的方法和Collection一致。
5.3.2 Set的子类
Set
|--HashSet:底层数据结构是哈希表(元素为链表的数组),线程不安全,效率高。
|--TreeSet:可以对Set集合中的元素进行排序,线程不安全,效率高。
1、HashSet
import java.util.HashSet;
/*
* HashSet:存储字符串并遍历
* 问题:为什么存储字符串的时候,字符串内容相同的只存储了一个呢?
* 通过查看add方法的源码,我们知道这个方法底层依赖 两个方法:hashCode()和equals()。
* 步骤:
* 首先比较哈希值
* 如果相同,继续走,比较地址值或者走equals()
* 如果不同,就直接添加到集合中
* 按照方法的步骤来说:
* 先看hashCode()值是否相同
* 相同:继续走equals()方法
* 返回true: 说明元素重复,就不添加
* 返回false:说明元素不重复,就添加到集合
* 不同:就直接把元素添加到集合
* 如果类没有重写这两个方法,默认使用的Object()。一般来说不同相同。
* 而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉。只留下一个。
*/
public class HashSetDemo {
public static void main(String[] args) {
//创建几何对象
HashSet<String> hs = new HashSet<String>();
//创建并添加元素
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
//遍历集合
for(String s : hs){
System.out.println(s);
}
}
}
运行结果:
为什么HashSet能保证元素的唯一性呢?查看HashSet的add()方法,知道了该方法底层依赖 两个方法:hashCode()和equals()。执行过程:
首先比较两个元素的哈希值(实际上调用的是HashCode()方法),如果不同,就直接添加到集合中;如果相同,就比较 地址值||内容。(前面调用的是“==”用来比较地址值是否相同,后面调用equals()方法来比较内容是否相同)。所以要想保证元素的唯一性,必须在元素所属类中复写hashCode()和equals()方法。String类复写了这两个方法,所以保证了元素的唯一性。
练习:需求:存储自定义对象,并保证元素的唯一性(如果两个对象的成员变量值都相同,则为同一个元素)。
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
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 int hashCode() {
return this.name.hashCode()+this.age*15;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Student)) {
return false;
}
Student s = (Student) obj;
return this.name.equals(s.name) && this.age == s.age;
}
}
public class HashSetDemo2 {
public static void main(String[] args) {
//创建集合
HashSet<Student> hs = new HashSet<Student>();
//创建学生对象
Student s1= new Student("林青霞",30);
Student s2 = new Student("柳岩",28);
Student s3= new Student("王祖贤",30);
Student s4= new Student("王祖贤",30);
Student s5= new Student("范冰冰",29);
//添加元素
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
hs.add(s5);
//增强for 集合遍历
for(Student s :hs){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
运行结果:
LInkedHashSet
底层数据结构由哈希表(保证元素的唯一性)和链表(保证元素有序)组成。
import java.util.LinkedHashSet;
public class LinkedHashSetDemo {
public static void main(String[] args) {
//创建集合对象
LinkedHashSet<String> hs = new LinkedHashSet<String>();
//创建并添加元素
hs.add("hello");
hs.add("world");
hs.add("java");
//增强for遍历
for(String s:hs){
System.out.println(s);
}
}
}
运行结果:
2、TreeSet
通过public TreeSet(Comparator<? super E> comparator)构造方法来创建对象
TreeSet底层数据结构是红黑树(是一个自平衡的二叉树)。能够对元素按照某种规则进行排序。排序有两种方式:自然排序和比较器排序。
1)自然排序
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合元素
TreeSet<Integer> ts = new TreeSet<Integer>();
// 创建元素并添加
// 20,18,23,22,17,24,19,18,24
ts.add(20);
ts.add(18);
ts.add(23);
ts.add(22);
ts.add(17);
ts.add(24);
ts.add(24);
ts.add(19);
ts.add(18);
ts.add(24);
for(Integer i : ts){
System.out.println(i);
}
}
}
运行结果:
为什么TreeSet能够保证元素的唯一性呢?通过查看TreeSet的add()方法(其实是TreeMap的put()方法),我们知道其实是通过判断一个比较方法(自然排序调用的是Comparable接口的compareTo()方法,比较器排序调用的是Comparator接口的compare()方法)的返回值(负数、0、正数),如果是0,则代表两个元素相同,也就不添加。所以必须要复写compareTo()方法或者compare()方法。Integer类实现了接口Comparable,并且复写了compareTo()方法,所以保证了元素的唯一性。
练习1:TreeSet存储自定义对象并保证唯一性,排序按照年龄排序。
import java.util.TreeSet;
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
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 int compareTo(Student s) {
// 这里返回什么,其实应该根据我的排序规则来做
// 主要条件:按照年龄排序
int num = this.age - s.age;
// 次要条件:
// 年龄相同的时候,还得去看姓名是否也相同
// 如果年龄和姓名都相同,才是同一个元素
int num2 = (num == 0) ? this.name.compareTo(s.name) : num;
return num2;
}
}
public class TreeSetDemo2 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts = new TreeSet<Student>();
//创建元素
Student s1 = new Student("林青霞",27);
Student s2 = new Student("张国荣",30);
Student s3 = new Student("吴奇隆",40);
Student s4 = new Student("刘诗诗",29);
Student s5 = new Student("张国荣",30);
Student s6 = new Student("刘诗诗",35);
Student s7 = new Student("令狐冲",35);
//添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
//遍历
for(Student s:ts){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
运行结果:
练习2:TreeSet存储自定义对象并保证唯一性,排序按照姓名的长度排序。
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
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 int compareTo(Student s) {
// 主要条件:姓名的长度
int num = this.name.length() - s.name.length();
// 次要条件:姓名的长度相同,不代表姓名的内容也相同
int num2 = (num == 0) ? this.name.compareTo(s.name) : num;
// 姓名的长度和内容相同,不代表年龄也相同
int num3 = (num2 == 0) ? this.age - s.age : num2;
return num3;
}
}
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
TreeSet<Student> ts = new TreeSet<Student>();
// 创建元素
Student s1 = new Student("abc1", 27);//
Student s2 = new Student("abc11", 30);
Student s3 = new Student("abc111", 40);
Student s4 = new Student("abc1", 29);
Student s5 = new Student("abc2", 30);
Student s6 = new Student("abc22", 35);
Student s7 = new Student("令狐冲", 35);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
运行结果:
2)比较器排序
通过public TreeSet(Comparator<? super E> comparator)构造方法来创建对象
import java.util.Comparator;
import java.util.TreeSet;
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
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 class MyComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
// int num = this.name.length() - s.name.length();
// this -- s1
// s -- s2
// 姓名长度
int num = s1.getName().length() - s2.getName().length();
// 姓名内容
int num2 = (num == 0) ? s1.getName().compareTo(s2.getName()) : num;
// 年龄
int num3 = (num2 == 0) ? s1.getAge() - s2.getAge() : num2;
return num3;
}
}
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
// public TreeSet(Comparator<? super E> comparator)
TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());
// 创建元素
Student s1 = new Student("abc1", 27);
Student s2 = new Student("abc11", 30);
Student s3 = new Student("abc111", 40);
Student s4 = new Student("abc1", 29);
Student s5 = new Student("abc2", 30);
Student s6 = new Student("abc22", 35);
Student s7 = new Student("令狐冲", 35);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
运行结果:
如果我们仅仅使用一次TreeSet的对象,可以使用匿名内部类来优化上面代码:
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
// 如果一个方法的参数是接口,那么真正要的接口的实现类的对象
// 而匿名内部类可以实现
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getName().length() - s2.getName().length();
int num2 = (num == 0) ? s1.getName().compareTo(s2.getName())
: num;
int num3 = (num2 == 0) ? s1.getAge() - s2.getAge() : num2;
return num3;
}
});
// 创建元素
Student s1 = new Student("abc1", 27);
Student s2 = new Student("abc11", 30);
Student s3 = new Student("abc111", 40);
Student s4 = new Student("abc1", 29);
Student s5 = new Student("abc2", 30);
Student s6 = new Student("abc22", 35);
Student s7 = new Student("令狐冲", 35);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
练习:键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台。
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
public class Student {
private String name;
private int chinese;
private int math;
private int english;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int getEnglish() {
return english;
}
public void setEnglish(int english) {
this.english = english;
}
public Student(String name, int chinese, int math, int english) {
super();
this.name = name;
this.chinese = chinese;
this.math = math;
this.english = english;
}
public Student() {
super();
}
public int getSum(){
return this.chinese+this.math+this.english;
}
}
public class TreeSetDemo {
public static void main(String[] args) {
// 创建一个TreeSet集合
// 创建Comparator接口的一个匿名内部类
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 总分高-->低
int num = s2.getSum() - s1.getSum();
// 总分相同的不一定语文相同
int num2 = (num == 0) ? s2.getChinese() - s1.getChinese() : num;
// 总分相同的不一定数学相同
int num3 = (num2 == 0) ? s2.getMath() - s1.getMath() : num2;
// 总分相同的不一定英语相同
int num4 = (num3 == 0) ? s2.getEnglish() - s1.getEnglish()
: num3;
// 姓名也不一定相同
int num5 = (num4 == 0) ? s1.getName().compareTo(s2.getName())
: num4;
return num4;
}
});
System.out.println("学生信息录入开始");
// 键盘录入5个学生信息
for (int x = 1; x <= 5; x++) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入第" + x + "个学生的姓名:");
String name = sc.nextLine();
System.out.println("请输入第" + x + "个学生的语文成绩:");
String chineseStringString = sc.nextLine();
System.out.println("请输入第" + x + "个学生的数学成绩:");
String mathString = sc.nextLine();
System.out.println("请输入第" + x + "个学生的英语成绩:");
String englishString = sc.nextLine();
// 把数据封装到学生对象中
Student s = new Student(name,
Integer.parseInt(chineseStringString),
Integer.parseInt(mathString),
Integer.parseInt(englishString));
// 把学生对象添加到集合
ts.add(s);
}
System.out.println("学生信息录入完毕");
System.out.println("学生总分成绩从高到低排序如下:");
System.out.println("姓名\t语文成绩\t数学成绩\t英语成绩");
// 遍历集合
for (Student s : ts) {
System.out.println(s.getName() + "\t" + s.getChinese() + "\t"
+ s.getMath() + "\t" + s.getEnglish());
}
}
}
运行结果: