-------android培训、java培训、期待与您交流! ----------
一、集合Java是面向对象的语言,对象用来封装数据,当操作的对象多了也需要存储起来,数组和集合都是容器,都可
以存储对象;但数组长度是固定的,集合是可变长度的,当对象的个数不确定的时候,就需要用到集合。
1:集合的特点
A:用于存储对象的容器
B:集合的长度是可变的
C:集合中不可以存储基本数据类型值。
2:集合和数组的区别
A:长度区别
数组固定;集合可变
B:内容区别
数组可以是基本类型,也可以是引用类型
集合只能是引用类型
C:元素内容
数组只能存储同一种类型
集合可以存储不同类型
3:集合的继承关系体系结构
由于需求不同,Java就提供了不同的集合类。但是它们都是要提供存储和遍历功能的,
把它们的共性不断的向上提取,最终就形成了集合的继承体系结构图。
数据结构:就是容器中存储数据的方式。
二、Collection
1:Collection:Collection是集合的顶层接口,它的子体系有重复的,有唯一的,有有序的,有无序的。
|—List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。
|—Set:无序(存入和取出顺序有可能不一致),不可以存储重复元素。必须保证元素唯一性。
2:功能
(1)添加功能
boolean add(Object obj):添加一个元素
boolean addAll(Collection c):添加一个集合的元素
(2)删除功能
void clear():移除所有元素
boolean remove(Object o):移除一个元素注意:删除成功,集合的长度会改变。
boolean removeAll(Collection c):移除一个集合的元素,只要有一个元素被移除就返回true。
(3)判断功能
boolean contains(Object o):判断集合中是否包含指定的元素
boolean containsAll(Collection c):判断集合中是否包含指定的集合元素,只有包含所有的元素,才叫包含
boolean isEmpty():判断集合是否为空
(4)获取功能
Iterator<E> iterator() 迭代器,集合的专用遍历方式。
(5)长度功能
int size():元素的个数
(6)交集功能
boolean retainAll(Collection c):两个集合的交集假设有两个集合A,B。A对B做交集,
最终的结果保存在A中,B不变。返回值表示的是A是否发生过改变。
(7)把集合转换为数组
Object toArray()
3:Iterator接口
迭代器,主要遍历Collection集合中的元素。
每一个集合都有自己的数据结构,都有特定的取出自己内部元素的方式。
为了便于操作所有的容器,取出元素。将容器内部的取出方式按照一个统一的规则向外提供,
这个规则就是Iterator接口。
方法:
A:boolean hasNext():若被迭代的集合元素还没有被遍历,返回true.
B:Object next():返回集合的下一个元素.
C:void remove():删除集合上一次next()方法返回的元素。(若集合中有多个相同的元素,都可以删掉)
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorDemo {
public static void main(String[] args) {
// 创建集合对象
Collection c = new ArrayList();
// 添加元素
c.add("hello");
c.add("world");
c.add("java");
Iterator it = c.iterator(); // 实际返回的肯定是子类对象,这里是多态
while (it.hasNext()) {//判断,防止出现NoSuchElementException异常。
String s = (String) it.next();//迭代器的next方法返回值类型是Object,要类型转换
System.out.println(s);
}
}
}
三、List
特有方法都有索引,这是该集合最大的特点。List是Collection的子接口,具备了Collection的所有方法,还有自己特有的共性方法。
1:List
|--ArrayList:底层的数据结构是数组,线程不同步,ArrayList替代了Vector,查询元素的速度非常快。
|--LinkedList:底层的数据结构是链表,线程不同步,增删元素的速度非常快。
|--Vector:底层的数据结构就是数组,线程同步的,Vector无论查询和增删都巨慢,但是线程安全。
2:特有功能
A:添加功能
void add(int index,Object element):在指定位置添加元素
B:获取功能
Object get(int index):获取指定位置的元素
C:列表迭代器
ListIterator listIterator():List集合特有的迭代器
D:删除功能
Object remove(int index):根据索引删除元素,返回被删除的元素
E:修改功能
Object set(int index,Object element):根据索引修改元素,返回被修饰的元素
3:ListIterator
提供了专门操作List的方法。在Iterator上额外增加的方法:ListIterator()是List集合特有的迭代器。该方法返回ListIterator对象,ListIterator继承了Iterator接口,
支持双向输出:
A:boolean hasPrevious():返回该迭代器关联集合是否还有上一个元素;
B:Object previous():返回该迭代器的上一个元素;
注意:ListIterator可以实现逆向遍历,但是必须先正向遍历,才能逆向遍历,所以一般无意义,不使用。
4:常见数据结构
A:栈 先进后出
B:队列 先进先出
C:数组 查询快,增删慢
D:链表 查询慢,增删快
5:List的子类特点
ArrayList
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高。
Vector
底层数据结构是数组,查询快,增删慢。
线程安全,效率低。
LinkedList
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高。
6:LinkedList
A:添加功能
public void addFirst(Object e)
public void addLast(Object e)
B:获取功能
public Object getFirst():获取但不移除
public Obejct getLast()
C:删除功能
public Object removeFirst():获取并移除
public Object removeLast()
四、JDK5的新特性
JDK5的新特性:自动拆装箱,泛型,增强for,静态导入,可变参数,枚举
1:泛型
是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。(1)格式:
<数据类型>
注意:该数据类型只能是引用类型。(2)好处:
A:把运行时期的问题提前到了编译期间(3)泛型高级通配符
B:避免了强制类型转换
C:优化了程序设计,让程序更安全
?:任意类型,如果没有明确,那么就是Object以及任意的Java类了
? extends E:向下限定,E及其子类
? super E:向上限定,E极其父类
(4)声明多个泛型类型
若一个类中多个字段需要不同的泛型声明,则在声明类的时候指定多个泛型类型即可;
格式:
public interface Dao<PK, T> { PK add(T t); void remove(PK id); void update(PK id, T t); T get(PK id); }
(5)泛型类、接口和方法
A:声明带泛型的类:
class类名<泛型类型1,泛型类型2……>{
泛型类型 变量名;
泛型类型 方法名(){}
返回值类型方法名(泛型类型变量名){}
}
使用带泛型的类:
类名<具体类>对象名 = new类名<具体类>();
B:声明泛型接口,声明方式和声明泛型类是一样的。
public interface Inter<T>{ public abstract void show(T t); }
泛型接口子类有两种方式:
a:直接在子类后申明泛型;还不知道是什么类型时。
public class InterImpl<T> implements Inter<T> { @Override public void show(T t) { System.out.println(t); } }
b:在子类实现的接口中给出具体的泛型类型;已经知道该是什么类型时。
public class InterImpl implements Inter<String> { @Override public void show(String t) { System.out.println(t); } }
C:声明泛型方法,方法中可定义泛型参数,形参的参数类型就是实参的类型。
格式:
<泛型类型>返回值类型 方法名([泛型类型参数]...)
Arrays工具类的一个方法
public static <T> List<T> asList(T... a)把数组转成集合。
注意:这个集合的长度不能改变。
好处:如果数组中存储的引用数据类型,直接作为集合的元素可以直接用集合方法操作。
如果数组中存储的是基本数据类型,asList会将数组实体作为集合元素存在。
public static<T extends Number> List<T>show(T... ...t){ return null; } public static void main(String[] args){ //show(new Object[]{});不可以,因为方法参数类型的限定 show(new Number[]{}); show(new Integer[]{}); }
(6)泛型的嵌套遍历
import java.util.ArrayList;
public class ArraysListDemo {
public static void main(String[] args) {
// 创建大集合
ArrayList<ArrayList<Integer>> bigArrayList = new ArrayList<ArrayList<Integer>>();
// 创建第一个集合
ArrayList<Integer> firstArrayList = new ArrayList<Integer>();
// 创建对象
Integer a1 = new Integer(11);
Integer a2 = new Integer(22);
Integer a3 = new Integer(33);
// 对象添加进第一个集合
firstArrayList.add(a1);
firstArrayList.add(a2);
firstArrayList.add(a3);
// 把第一个集合添加到大集合中
bigArrayList.add(firstArrayList);
// 创建第二个集合
ArrayList<Integer> secondArrayList = new ArrayList<Integer>();
// 添加
Integer b1 = new Integer(44);
Integer b2 = new Integer(55);
Integer b3 = new Integer(66);
// 对象添加到第二个集合
secondArrayList.add(b1);
secondArrayList.add(b2);
secondArrayList.add(b3);
// 把第二个集合添加到大集合中、
bigArrayList.add(secondArrayList);
// 遍历集合
for (ArrayList<Integer> array : bigArrayList) { //大集合遍历
for (Integer i : array) { //小集合遍历
System.out.println(i);
}
}
}
}
2:增强for
是for循环的一种,简化了迭代器
(1)格式:
for(元素的数据类型 变量名 : 数组或者Collection集合的对象) {
使用该变量即可,该变量其实就是数组或者集合中的元素。
}
(2)好处:
简化了数组和集合的遍历
(3)弊端
增强for循环的目标不能为null。建议在使用前,先判断是否为null。
(4)普通for和增强for
Map map = new HashMap(); map.put("a", "aaa"); Set entrys = map.entrySet(); // 1.获得所有的键值对Entry对象 iter = entrys.iterator(); // 2.迭代出所有的entry while(iter.hasNext()) { Map.Entry entry = (Entry) iter.next(); String key = (String) entry.getKey(); // 分别获得key和value String value = (String) entry.getValue(); System.out.println(key + "=" + value); }
增强for循环迭代:原则上map集合是无法使用增强for循环来迭代的,因为增强for循环只能针对实现了Iterable接口的集合进行迭代;Iterable是jdk5中新定义的接口,就一个方法iterator方法,只有实现了Iterable接口的类,才能保证一定有iterator方法,java有这样的限定是因为增强for循环内部还是用迭代器实现的,而实际上,我们可以通过某种方式来使用增强for循环。
for(Object obj : map.entrySet()) { Map.Entry entry = (Entry) obj; // obj 依次表示Entry System.out.println(entry.getKey() + "=" + entry.getValue()); }
(5)注意事项
a:在迭代集合的过程中,不能对集合进行增删操作(会报并发访问异常);可以用迭代器的方法进行操作
(子类listIterator:有增删的方法)。
b:在使用增强for循环时,不能对元素进行赋值.
int[] arr = {1,2,3}; for(int num : arr) { num = 0; //不能改变数组的值 }
3:静态导入
可以导入到方法级别的导入
(1)格式:
import static 包名....类名.方法名;
import static java.lang.System.out;//导入java.lang包下的System类的静态方法out; public class HelloWorld{ public static void main(String[] args){ out.print("Hello World!");//既是在这里不用再写成System.out.println("Hello World!")了,因为已经导入了这个静态方法out。 } }
(2)注意事项:
A:方法必须是静态的。
B:如果多个类下有同名的方法,就不好区分了,还得加上前缀。所以一般我们并不使用静态导入。
4、可变参数
如果我们在写方法的时候,参数个数不明确,就应该定义可变参数。
格式:
修饰符 返回值类型 方法名(数据类型... 变量) {}
注意:
A:该变量其实是一个数组名
B:如果一个方法有多个参数,并且有可变参数,可变参数必须在最后
public class ArraysDemo {
public static void main(String[] args){
int a = 10;
int b = 20;
int c = 30;
int result = sum(a,b);
System.out.println("result1:" + result);
result = sum(a,b,c);
System.out.println("result2:" + result);
result = sum(a,b,c,40);
System.out.println("result3:" + result);
}
public static int sum(int... a) {
int s = 0;
for(int x : a){
s += x;
}
return s;
}
}
5:枚举
使用enum声明,默认直接继承了java.lang.Enum类,而不是Object类;
枚举类的对象是固定的,实例个数有限,不可以再new( ),枚举对象后可以跟()。
枚举元素必须位于枚举类体中的最开始部分,枚举元素后要有分号与其他成员分隔。
枚举类的构造方法的权限修饰符默认是private;一旦枚举对象后面加上{},
那么该对象实际是枚举匿名内部类对象;
所有枚举类都提供一个静态的values()方法(返回该枚举类所有对象组成的数组),便于遍历所有枚举对象;
所有枚举类都提供一个静态的valueOf(Stringname)方法, 返回枚举类中对象名等于 name的对象。
public class Test {
public enum Direction {
FRONT("前"){
@Override
public void show() {
System.out.println("你选择了前");
}
},
BEHIND("后"){
@Override
public void show() {
System.out.println("你选择了后");
}
},
LEFT("左"){
@Override
public void show() {
System.out.println("你选择了左");
}
},
RIGHT("右"){
@Override
public void show() {
System.out.println("你选择了右");
}
};
private String name;
private Direction(String name){
this.name = name;
}
public String getName(){
return name;
}
public abstract void show();
}
public static void main(String[] args) {
Direction d1 = Direction.FRONT;
Direction d2 = Direction.BEHIND;
Direction d3 = Direction.LEFT;
Direction d4 = Direction.RIGHT;
System.out.println(d1.compareTo(d4));//-3。d1的角标-d4的角标。
System.out.println(d2.toString());//BEHIND。
System.out.println(d3.ordinal());//2。d3的角标
System.out.println(d4.name);//右。
Direction dd1 = Enum.valueOf(Direction.class, "LEFT");
System.out.println(dd1.getName());//左。对象名等于 name的对象
d1.show();//你选择了前。调用show()方法。
Direction[] dirs = Direction.values();//values()方法遍历所有对象
for (Direction dd2 : dirs) {
System.out.println(dd2);
System.out.println(dd2.getName());
}
}
}
五、Set
1:特点
元素不可以重复,是无序。
Set接口中的方法和Collection一致。
2:Set
|--HashSet:内部数据结构是哈希表,是不同步的。
|--TreeSet:可以对Set集合中的元素进行排序,是不同步的。
3:HashSet集合
HashSet类是Set接口最常用的实现类,采用hash算法存储数据,具有良好的存储和查找功能。
HashSet集合元素值允许是null,但是最多只能有一个
(1)底层数据结构是哈希表(是一个元素为链表的数组)
(2)哈希表底层依赖两个方法:hashCode()和equals()
执行顺序:
首先比较哈希值是否相同
相同:继续执行equals()方法
返回true:元素重复了,不添加
返回false:直接把元素添加到集合
不同:就直接把元素添加到集合
(3)由hashCode()和equals()保证元素的唯一性的,应用中应重写这两个方法。
如果类没有重写这两个方法,默认使用的Object()。一般来说不同的也会默认相同。
而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉。只留下一个。
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);//无重复无序。输入和取出不一致。 } }
(4)LinkedHashSet
底层数据结构由哈希表和链表组成。
哈希表保证元素的唯一性。
链表保证元素有序。(存储和取出是一致)
public static void main(String[] args) { // 创建集合对象 LinkedHashSet<String> hs = new LinkedHashSet<String>(); // 创建并添加元素 hs.add("hello"); hs.add("world"); hs.add("java"); hs.add("world"); hs.add("java"); // 遍历 for (String s : hs) { System.out.println(s);//hello world java //有序无重复 } }
4:TreeSet集合
TreeSet是SortedSet接口唯一的实现,底层数据结构是红黑树(是一个自平衡的二叉树)
TreeSet集合保证元素排序和唯一性的原理
唯一性:是根据比较的返回是否是0来决定。
排序方式
a:自然排序(元素具备比较性)
让元素所属的类实现Comparable接口,覆盖compareTo方法
b:比较器排序(集合具备比较性)
让集合构造方法接收Comparator的实现类对象