java 集合框架
集合框架
一、体系概述
Collection定义了集合框架的共性功能。集合可以看作是一种容器,用来存储对象信息。所有集合类都位于java.util包下。
数组与集合的区别如下:
- 数组长度不可变而且无法保存具有映射关系的数据;集合类用于保存数量不确定的数据,以及保存具有映射关系的数据。
- 数组元素既可以是基本类型的值,也可以是对象;集合只能保存对象。
Java集合类主要由两个根接口Collection和Map派生出来的,Collection派生出了两个子接口:List、Set,因此Java集合大致也可分成List、Set、Map三种接口体系。
List代表了有序可重复集合,可直接根据元素的索引来访问;Set代表无序不可重复集合,只能根据元素本身来访问;Map代表的是存储key-value对的集合,可根据元素的key来访问value。
二、Collection常用方法
引用自 Java API
import java.util.*;
class TT {
public static void main(String[] args) {
//创建一个集合容器。使用Collection接口的子类。ArrayList
ArrayList al = new ArrayList();
//添加元素。
al.add("java01");//add(Object obj);
al.add("java02");
al.add("java03");
al.add("java04");
//打印原集合。
sop("原集合:" + al);
//删除元素。
al.remove("java02");
sop(al);
//al.clear();//清空集合。
//判断元素。
sop("java03是否存在:" + al.contains("java03"));
sop("集合是否为空?" + al.isEmpty());
//获取个数。集合长度。
sop("size:" + al.size());
//打印改变后的集合。
sop(al);
}
public static void sop(Object obj) {
System.out.println(obj);
}
}
运行结果:
原集合:[java01, java02, java03, java04]
[java01, java03, java04]
java03是否存在:true
集合是否为空?false
size:3
[java01, java03, java04]
迭代器 Iterator
迭代器是取出方式,会直接访问集合中的元素。所以将迭代器通过内部类的形式来进行描述。通过容器的iterator()方法获取该内部类的对象。
import java.util.*;
class CollectionDemo {
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
Iterator it = al.iterator();//获取迭代器,用于取出集合中的元素。
while (it.hasNext()) {
sop(it.next());
}
/*用for循环也行
for (Iterator it = al.iterator(); it.hasNext(); ) {
sop(it.next());
}*/
}
public static void sop (Object obj){
System.out.println(obj);
}
}
运行结果:
java01
java02
java03
java04
三、List集合
|--List:元素是有序的,元素可以重复。因为该集合体系有索引。
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。
|--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。
- 特有方法:凡是可以操作角标的方法都是该体系特有的方法。
- 特有的迭代器:ListIterator(列表迭代器),Iterator的子接口。
//演示列表迭代器。
import java.util.*;
public class Test {
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
System.out.println(al);
ListIterator li = al.listIterator();
while(li.hasNext())
{
Object obj = li.next();
if(obj.equals("java02")) {
li.set("java006");//把“java02”元素设置为“java006”
}
}
System.out.println(al);
}
}
运行结果:
[java01, java02, java03]
[java01, java006, java03]
ArrayList
它允许任何符合规则的元素插入甚至包括null。每一个ArrayList都有一个初始容量(10),该容量代表了数组的大小。随着容器中的元素不断增加,容器的大小也会随着增加。
常见方法的例子
import java.util.*;
public class TT {
public static void main(String[] args) {
ArrayList al = new ArrayList();
//添加元素
al.add("java01");
al.add("java02");
al.add("java03");
sop("原集合是:" + al);
//在指定位置添加元素。
al.add(1, "java09");
//删除指定位置的元素。
//al.remove(2);
//修改元素。
//al.set(2,"java007");
//通过角标获取元素。
sop("get(1):" + al.get(1));
sop(al);
//通过indexOf获取对象的位置。
sop("index=" + al.indexOf("java02"));
//返回列表中指定的 。
List sub = al.subList(1, 3);
sop("sub=" + sub);
}
public static void sop(Object obj) {
System.out.println(obj);
}
}
LinkedList
LinkedList的实现机制与ArrayList的实现机制完全不同,ArrayLiat内部以数组的形式保存集合的元素,所以随机访问集合元素有较好的性能;LinkedList内部以链表的形式保存集合中的元素,所以随机访问集合中的元素性能较差,但在插入删除元素时有较好的性能。
特有方法
- addFirst();
- addLast();
- getFirst();
- getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
- removeFirst();
- removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException
在JDK1.6出现了替代方法。 - offerFirst();
- offerLast();
- peekFirst();
- peekLast();
获取元素,但不删除元素。如果集合中没有元素,会返回null。 - pollFirst();
- pollLast();
获取元素,但是元素被删除。如果集合中没有元素,会返回null。
import java.util.*;
class LinkedListDemo {
public static void main(String[] args) {
LinkedList link = new LinkedList();
link.addLast("java01");
link.addLast("java02");
sop("link: " + link);
sop(link.getFirst());
sop(link.getLast());
sop(link.removeLast());
sop("link: " + link);
LinkedList link1 = new LinkedList();
link1.addLast("java01");
link1.addLast("java02");
link1.addLast("java03");
sop("link1: " + link1);
sop(link.offerFirst("java01"));
sop(link1.peekLast());
sop("link1: " + link1);
}
public static void sop(Object obj) {
System.out.println(obj);
}
}
运行结果:
link: [java01, java02]
java01
java02
java02
link: [java01]
link1: [java01, java02, java03]
true
java03
link1: [java01, java02, java03]
Process finished with exit code 0
Vector
与ArrayList相似,但是Vector是同步的。所以说Vector是线程安全的动态数组。它的操作与ArrayList几乎一样。
Set
Set:无序,不可以重复元素。
- TreeSet:可以对Set集合中的元素进行排序。底层数据结构是二叉树。
保证元素唯一性的依据:compareTo方法return 0.
HashSet
注意:对于判断元素是否存在以及删除等操作,依赖的方法是元素的hashcode和equals方法。
特点
- 不能保证元素的顺序。
- 线程不同步的。
- 集合元素值可以是null。
- 数据结构是哈希表。
存储原理:
当向集合存储一个元素时,HashSet会调用该对象的hashCode()方法得到其hashCode值,然后根据hashCode值决定该对象的存储位置。
查找原理
基于存储原理,在查找元素时,HashSet先计算元素的HashCode值(也就是调用对象的hashCode方法的返回值),然后直接到hashCode值对应的位置去取出元素。
判断两个元素相等的标准
- 两个对象的hashCode()方法返回值相等。
- 两个对象通过equals()方法比较返回true;
因此,如果1和2有一个不满足条件,则认为这两个对象不相等,可以添加成功。如果两个对象的hashCode()方法返回值相等,但是两个对象通过equals()方法比较返回false,HashSet会以链式结构将两个对象保存在同一位置,这将导致性能下降,因此在编码时应避免出现这种情况。
保证元素唯一性的原理:判断元素的hashCode值是否相同。如果相同,继续判断元素的equals方法,是否为true。
例子
/*
往hashSet集合中存入自定义对象
若姓名和年龄相同为同一个人,重复元素。
*/
import java.util.*;
class HashSetTest {
public static void sop(Object obj) {
System.out.println(obj);
}
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new Person("a1", 11));
hs.add(new Person("a2", 12));
hs.add(new Person("a1", 11));
hs.add(new Person("a3", 13));
Iterator it = hs.iterator();
while (it.hasNext()) {
Person p = (Person) it.next();
sop(p.getName() + "::" + p.getAge());
}
}
}
class Person {
private String name;
private int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
//复写hasCode方法
public int hashCode() {
return name.hashCode() + age * 37;
}
//复写eauals方法
public boolean equals(Object obj) {
if (!(obj instanceof Person))//判断是不是Person类
return false;
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
运行结果:
a3::13
a1::11
a2::12
TreeSet
TreeSet的排序方式
- 让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。这种方式也称为元素的自然顺序,或者叫做默认顺序。
- 当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。
import java.util.*;
/*
当元素自身不具备比较性,或者具备的比较性不是所需要的。
这时需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
当两种排序都存在时,以比较器为主。
定义一个类,实现Comparator接口,覆盖compare方法。
*/
class Student implements Comparable//该接口强制让学生具备比较性。
{
private String name;
private int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
public int compareTo(Object obj) {
if (!(obj instanceof Student))
throw new RuntimeException("不是学生对象");
Student s = (Student) obj;
if (this.age > s.age)
return 1;
if (this.age == s.age) {
return this.name.compareTo(s.name);
}
return -1;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
class TreeSetDemo2 {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add(new Student("lisi02", 22));
ts.add(new Student("lisi02", 21));
ts.add(new Student("lisi007", 20));
ts.add(new Student("lisi09", 19));
ts.add(new Student("lisi06", 18));
ts.add(new Student("lisi06", 18));
ts.add(new Student("lisi007", 29));
Iterator it = ts.iterator();
while (it.hasNext()) {
Student stu = (Student) it.next();
System.out.println(stu.getName() + "..." + stu.getAge());
}
}
}
class MyCompare implements Comparator {
public int compare(Object o1, Object o2) {
Student s1 = (Student) o1;
Student s2 = (Student) o2;
int num = s1.getName().compareTo(s2.getName());
if (num == 0) {
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
}
return num;
}
}
泛型
泛型格式:通过<>来定义要操作的引用数据类型。
? 通配符。也可以理解为占位符。
泛型的限定;
? extends E: 可以接收E类型或者E的子类型。上限。
? super E: 可以接收E类型或者E的父类型。下限。
- 静态方法不可以访问类上定义的泛型。
- 如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
- 泛型类定义的泛型,在整个类中有效。如果被方法使用,
- 那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
为了让不同方法可以操作不同类型,而且类型还不确定。那么可以将泛型定义在方法上。
import java.util.*;
class GenericDemo2 {
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<String>(new LenComparator());
ts.add("abcd");
ts.add("cc");
ts.add("cba");
ts.add("aaa");
ts.add("z");
ts.add("hahaha");
Iterator<String> it = ts.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
class LenComparator implements Comparator<String> {
public int compare(String o1, String o2) {
int num = new Integer(o2.length()).compareTo(new Integer(o1.length()));
if (num == 0)
return o2.compareTo(o1);
return num;
}
}
Map集合
该集合存储键值对。一对一对往里存。而且要保证键的唯一性。
Map
|--Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。jdk1.0.效率低。
|--HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。
|--TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。
import java.util.*;
class MapDemo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
//添加元素,添加元素,如果出现添加时,相同的键。那么后添加的值会覆盖原有键对应值。
//并put方法会返回被覆盖的值。
System.out.println("put:" + map.put("01", "zhangsan1"));
System.out.println("put:" + map.put("01", "wnagwu"));
map.put("02", "zhangsan2");
map.put("03", "zhangsan3");
System.out.println("containsKey:" + map.containsKey("022"));
//System.out.println("remove:"+map.remove("02"));
System.out.println("get:" + map.get("023"));
map.put("04", null);
System.out.println("get:" + map.get("04"));
//可以通过get方法的返回值来判断一个键是否存在。通过返回null来判断。
//获取map集合中所有的值。
Collection<String> coll = map.values();
System.out.println(coll);
System.out.println(map);
}
}
运行结果:
put:null
put:zhangsan1
containsKey:false
get:null
get:null
[wnagwu, zhangsan2, zhangsan3, null]
Set keySet
将map中所有的键存入到Set集合。因为set具备迭代器。然后用迭代方式取出所有的键。再根据get方,获取每一个键对应的值。
Set<Map.Entry<k,v>> entrySet
将map集合中的映射关系存入到了set集合中,而这个关系的数据类型就是:Map.Entry。
Entry其实就是Map中的一个static内部接口。
例子
/*
每一个学生都有对应的归属地。
学生Student,地址String。
学生属性:姓名,年龄。
注意:姓名和年龄相同的视为同一个学生。
保证学生的唯一性。
1,描述学生。
2,定义map容器。将学生作为键,地址作为值。存入。
3,获取map集合中的元素。
*/
import java.util.*;
class Student implements Comparable<Student> {
private String name;
private int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
public int compareTo(Student s) {
int num = new Integer(this.age).compareTo(new Integer(s.age));
if (num == 0) {
return this.name.compareTo(s.name);
}
return num;
}
public int hashCode() {
return name.hashCode() + age * 34;
}
public boolean equals(Object obj) {
if (!(obj instanceof Student)) {
throw new ClassCastException("类型不匹配");
}
Student s = (Student) obj;
return this.name.equals(s.name) && this.age == s.age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String toString() {
return name + ":" + age;
}
}
class MapTest {
public static void main(String[] args) {
HashMap<Student, String> hm = new HashMap<Student, String>();
hm.put(new Student("lisi1", 21), "beijing");
hm.put(new Student("lisi1", 21), "tianjin");
hm.put(new Student("lisi2", 22), "shanghai");
hm.put(new Student("lisi3", 23), "nanjing");
hm.put(new Student("lisi4", 24), "wuhan");
//第一种取出方式 keySet
Set<Student> keySet = hm.keySet();
Iterator<Student> it = keySet.iterator();
while (it.hasNext()) {
Student stu = it.next();
String addr = hm.get(stu);
System.out.println(stu + ".." + addr);
}
//第二种取出方式 entrySet
Set<Map.Entry<Student, String>> entrySet = hm.entrySet();
Iterator<Map.Entry<Student, String>> iter = entrySet.iterator();
while (iter.hasNext()) {
Map.Entry<Student, String> me = iter.next();
Student stu = me.getKey();
String addr = me.getValue();
System.out.println(stu + "........." + addr);
}
}
}
运行结果:
lisi4:24..wuhan
lisi2:22..shanghai
lisi1:21..tianjin
lisi3:23..nanjing
lisi4:24.........wuhan
lisi2:22.........shanghai
lisi1:21.........tianjin
lisi3:23.........nanjing