概述
类集实际上就属于动态的对象数组,最原始的数组本身有一个最大的缺陷:数组的长度是固定的。主要有以下四个内容:
- List接口
- Set接口
- Map接口
- 集合输出
在java.util包提供有两个最为核心的操作接口:Collection集合接口(包括List和Set接口)、Map接口,Collection的操作形式与链表的形式相似,每一次进行数据操作的时候只能够对单个对象进行处理。Collection不能区分存储类型:Collection的子接口:List(允许重复)、Set(不允许重复)
- List接口的基本使用
List接口是Collection的子接口,其最大的特点在于有一个get()方法,可以根据索引取得内容(数据结构中的有序概念)。
但List本身还属于一个接口,而如果要想取得接口的实例化对象,就必须要有子类,在List接口下有三个常用子类:ArrayList、Vector、LinkedList
1.1 ArrayList子类
ArrayList是一个针对于List接口的数组操作实现。首先利用ArrayList做一些List的基本操作
范例:观察List基本处理
import java.util.*;;
public class TestDemo1 {
public static void main(String[] args) throws Exception{
List<String> all = new ArrayList<String>();
System.out.println(all.size() + "、" + all.isEmpty());
all.add("hello");
all.add("hello");
all.add("world");
System.out.println(all.size() + "、" + all.isEmpty());
all.remove("hello");
System.out.println(all.contains("world"));
System.out.println(all.contains("abc"));
System.out.println(all);
for (int i = 0; i < all.size(); i++) {
System.out.println(all.get(i));
}
}
}
1.2 Vector子类
代码同上,将ArrayList改为Vector即可
Vector和ArrayList的区别:
No. 区别 ArrayList Vector
1 历史时间 JDK 1.2 JDK 1.0
2 处理形式 异步处理,性能更高 同步处理,性能降低
3 数据安全 非线程安全 线程安全
4 输出形式 Iterator、ListIterator、foreach Iterator、ListIterator、foreach、Enumeration
1.3 LinkedList子类
代码同上,将ArrayList改为LinkedList即可
ArrayList和LinkedList区别:
ArrayList里面存放的是一个数组,如果在实例化此类对象的时候,默认传入了数组的大小,则里面保存的数组会开辟一个定长的数组。如果后面再进行数据保存的时候发现数组的长度不够了。那么会进行数组的动态扩充。所以在实际开发之中,如果要使用ArrayList最好的做法就是设置好初始化的大小
LinkedList是纯粹的链表实现,与之前编写的链表程序的实现完全一样。
总结: ArrayList封装的是一个数组,LinkedList封装的是一个链表实现。ArrayList时间复杂度是1,LinkedList时间复杂度为n
2. Set接口的基本使用
2.1 HashSet子类
2.2 TreeSet子类
import java.util.*;
public class TestDemo1 {
public static void main(String[] args) throws Exception{
Set<String> all = new HashSet<String>();
all.add("hello");
all.add("hello");//重复元素
all.add("world");
all.add("abcd");
all.add("hash");
System.out.println(all);
}
}
2.3 重复元素的判断
HashSet判断重复元素的依靠的是Object类中的两个方法:
import java.util.*;
public class TestDemo1 {
public static void main(String[] args) throws Exception{
Set<String> all = new TreeSet<String>();
all.add("A");
all.add("B");
all.add("A");
all.add("D");
all.add("C");
System.out.println(all);
}
}
对象比对分两步:第一步要通过一个对象的唯一编码找到一个对象的信息,当编码匹配之后再调用equals()方法进行内容的比较
范例:使用HashSet来观察对象:
import java.util.*;
class Person implements Comparable<Person>{
private String name;
private Integer age;
public Person(String name,Integer age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
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 == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Person o) {
if (this.age > o.age) {
return 1;
}else if (this.age < o.age) {
return -1;
}else {
return this.name.compareTo(o.name);
}
}
}
public class TestDemo1 {
public static void main(String[] args) throws Exception{
Set<Person> all = new HashSet<Person>();
all.add(new Person("张三", 20));
all.add(new Person("张三", 20));//数据重复
all.add(new Person("李四", 20));//年龄重复
all.add(new Person("王五", 19));
System.out.println(all.contains(new Person("李四", 20)));
System.out.println(all);
}
}
保存自定义类对象一定用List接口,保存系统类的信息一定使用Set接口(过滤重复)
- 集合输出
在之前集合输出的时候都利用了toString(),或者是利用了List接口中get()方法,但是这些都不是集合的标准输出模式
如果从标准上来讲,集合的输出一共有四种手段:Iterator、ListIterator、Enumeration、foreach
3.1 迭代输出:Iterator
• 判断是否有下一个元素:public boolean hasNext()
• 取得当前元素:public E next()
• 删除元素:public default void remove()
• 取得Iteratior接口对象:public Iterator<E> iterator()
范例:标准的Iteratior使用
public class TestDemo1 {
public static void main(String[] args) throws Exception{
List<String> all = new ArrayList<String>();
all.add("hello");
all.add("hello");
all.add("world");
Iterator<String> iter = all.iterator(); //实例化Iterator接口
while (iter.hasNext()) {
String string = (String) iter.next();
System.out.println(string);
}
}
}
3.2 双向迭代输出:ListIterator
如Iterator输出接口有一个特点:只能够由前向后进行内容的迭代。而如果想要进行双向的迭代输出就使用ListIterator
有两个方法:
• 判断是否有上一个元素:public boolean hasPrevious()
• 取得当前元素:public E previous()
1
2
3.3 枚举输出:Enumeration
Enumeration有如下方法:
• 是否有下一个元素:public boolean hasMoreElements()
• 取得元素:public E nextElement()
1
2
范例:使用Enumeration来输出
public class TestDemo1 {
public static void main(String[] args) throws Exception{
Vector<String> all = new Vector<String>();
all.add("a");
all.add("b");
all.add("c");
ListIterator<String> iter = all.listIterator(); //实例化Iterator接口
Enumeration<String> enu = all.elements();
while (enu.hasMoreElements()) {
String string = (String) enu.nextElement();
System.out.println(string);
}
}
}
一些操作类库上依然只支持Enumeration,而不支持Iteratior,所以必须会使用到Enumeration
3.4 foreach
范例:使用foreach来输出
public class TestDemo1 {
public static void main(String[] args) throws Exception{
List<String> all = new ArrayList<String>();
all.add("a");
all.add("b");
all.add("c");
for (String string : all) {
System.out.println(string);
}
}
}
- Map接口的基本使用
Collection集合的特点是每次进行单个对象的保存,如果现在要进行一对对象的保存(偶对象),只能使用Map集合来完成。Map集合一次性保存两个,两个对象的关系:key = value 的接口。最大特点是可以通过key找到value。
Map集合本身是一个接口,要使用Map必须通过子类进行对象实例化,而子类有如下几个:HashMap、HashTable、TreeMap(用作排序)、ConcurrentHashMap
4.1 HashMap子类
范例:Map的基本操作
public class TestDemo1 {
public static void main(String[] args) throws Exception{
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(3, "d");
map.put(1, "a");
map.put(1, "b");//key重复了
map.put(2, "c");
System.out.println(map);
System.out.println(map.get(1));//根据key取得值
System.out.println(map.get(99));
}
}
HashMap的原理:
在数据量小的时候,Hash Map是按照链表的模式存储的,当数据量变大之后为了进行快速的查找,那么会将这个链表变为红黑树(均衡二叉树),用hash码作为数据的定位,来进行保存。
4.2 HashTable 子类
HashTable 和 HashMap 的区别*:
No. 区别 HashMap HashTable
1 历史时间 JDK 1.2 JDK 1.0
2 处理形式 异步处理,性能更高 同步处理,性能降低
3 数据安全 非线程安全 线程安全
4 null操作 允许存放null 不允许为空
4.3 ConcurrentHashMap
操作代码同上,改成ConcurrentHashMap即可。
ConcurrentHashMap = HashTable的线程安全 + HashMap的高性能,在使用ConcurrentHashMap 处理的时候既可以保证多个线程更新数据的同步、又可以保证很高效的查询速度。
ConcurrentHashMap的操作原理:
数据分桶:如果说现在采用一定的算法,将保存的大量数据平均分在不同的桶(数据区域中),在进行数据查找的时候就可以避免全部的数据扫描。采用分桶之后每一个数据必须有一个明确的分桶的标记(通过使用hashcode())
4.4 使用Iterator输出Map集合
如果存储数据是为了输出,那么优先考虑的一定是Collection,使用Map的主要操作是为了设置内容,而后使用get()进行查找。
明确一点:Map接口没有iterator方法。
Collection与Map数据保存的区别:
Map内部有嵌套类 Map.Entry<K,V>,把数据封装成Entry
接口 Map.Entry<K,V>有如下方法需要关注:
• public K getKey()
• public V getValue()
1
2
在Map接口有一个重要方法:将Map集合转为Set集合: public Set<Map.Entry<K,V>> entrySet()
范例:通过iterator输出Map集合(必须熟练使用)
public class TestDemo1 {
public static void main(String[] args) throws Exception{
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(3, "d");
map.put(1, "a");
map.put(1, "b");//key重复了
map.put(2, "c");
Set<Map.Entry<Integer, String>> set = map.entrySet();//1.将Map集合变为Set集合
Iterator<Map.Entry<Integer, String>> iter = set.iterator();//2.实例化iterator接口
while (iter.hasNext()) {
Map.Entry<Integer, String> entry = (Map.Entry<Integer,String>) iter.next();//3.取出Map.Entry
System.out.println("key: " + entry.getKey() + " value: " + entry.getValue()); //4.取得key和value
}
}
}
4.5 Map中key的说明
可以使用自定义key类型,不过要覆写hashcode和equeals
范例: 覆写hashcode和equeals
class Person{
private String name;
private Integer age;
public Person(String name,Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
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 == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
public class TestDemo1 {
public static void main(String[] args) throws Exception{
Map<Person, String> map = new HashMap<Person, String>();
map.put(new Person("张三", 20),new String("zs"));
System.out.println(map.get(new Person("张三", 20)));
}
}
4.6 TreeMap子类
范例:无实例化对象
public class TestDemo1 {
public static void main(String[] args) throws Exception{
Map<Integer, String> map = new TreeMap<Integer, String>();
map.put(19, "a");
map.put(2253, "b");
map.put(14, "c");
map.put(865, "d");
System.out.println(map);
}
}
范例:实例化对象(无hashcode和equals)
class Person implements Comparable<Person>{
private String name;
private Integer age;
public Person(String name,Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Person o) {
// TODO Auto-generated method stub
return this.name.compareTo(o.name);
}
}
public class TestDemo1 {
public static void main(String[] args) throws Exception{
Map<Person, String> map = new TreeMap<Person, String>();
map.put(new Person("张三", 20),new String("zs"));
System.out.println(map.get(new Person("张三", 20)));
}
}
结论:有comparable的地方过滤重复数据就依靠compareTo()方法,这类操作一般不常见