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()); } } }
执行结果:
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); } } }
执行结果:
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.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()); } } }
执行结果: