集合实际上就是一个容器,可以容纳其它类型的数据。集合不能存储基本数据类型,也不能存储Java对象,集合当中存储的都是Java对象的内存地址。在Java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。
Java集合主要有3中重要的类型:
- List:是一个有序集合,可以放重复的数据
- Set:是一个无序集合,不允许放重复的数据
- Map:是一个无序集合,包含一个键值对,键对象不允许重复,值对象可以重复
在java中集合分为两大类:
一类是单个方式存储元素:
单个方式存储元素,这一类集合中超级父接口:java.util.Collection;
一类是以键值对儿的方式存储元素
以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;
集合这块最主要掌握的内容
- 每个集合对象的创建(new)
- 向集合中添加元素
- 从集合中取出某个元素
- 遍历集合
- 主要的集合类:ArrayList、LinkedList、HashSet (HashMap的key,存储在HashMap集合key的元素需要同时重写hashCode + equals)、TreeSet、HashMap、Properties、TreeMap
Conllection是List和Set的父接口,在Collection中定义了一些主要方法:
| boolean | add() 确保此collection包含指定的元素 |
|---|---|
| boolean | addAll() 将指定collection中的所有元素都添加到此collection中 |
| void | clear() 移除此collection中的所有元素 |
| boolean | contains() 如果此collection包含指定元素,则返回true |
| boolean | containAll() 如果此collection包含指定collection中的所有元素,则返回true |
| boolean | equals() 比较此collection与指定对象是否相等 |
| int | hashCode() 返回此collection的哈希码值 |
| boolean | isEmpty() 返回此collection是否为空 |
| Iterator | iterator() 返回在此collection的元素上进行迭代的迭代器 |
| boolean | remove() 从此collection中移除指定元素的单个实例 |
| boolean | removeAll() 从此collection中移除指定元素的所有实例 |
| boolean | remainAll() 仅保留collection中那些也包含在指定collection的元素 |
| int | size() 返回此collection中的元素数 |
| Object[] | toArray() 返回包含此collection中所有元素的数组 |
| T[] | toArray() 返回包含此collection中的所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同 |
关于Iterator接口说明,Iterator称为迭代接口,通过此接口可以遍历集合中的数据,此接口主要方法为:
| boolean | hasNext() 如果仍有元素可以迭代,则返回True |
|---|---|
| E | next() 返回迭代的下一个元素 |
List接口主要有两个实现ArrayList和LinkedList,都是有序的,为可变数组。
- ArrayList:查询数据比较快,添加和删除数据比较慢(基于可变数据)
- LinkedList:查询数据比较慢,添加和删除数据比较快(基于链表数据)
- Vector:效率慢,不建议使用,已经被ArrayList取代
- Stack:是继承Vector实现的一个栈,目前已经被LinkedList取代
List是Collection接口的子接口。所以List接口中有一些特有的方法。
void add(int index, Object element)
Object set(int index, Object element)
Object get(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
Object remove(int index)
ArrayList集合初始化容量10,扩容为原容量1.5倍。底层是数组。
public class ArrayListTest {
public static void main(String[] args) {
// 面向接口编程,使用Collection更加灵活
Collection c=new ArrayList();
List listt=new ArrayList();
// 自动装箱
listt.add(1);
// jdk1.5之前,必须如下使用
listt.add(new Integer(2));
// 可以加入重复数据
listt.add(1);
// 采用get方法依次获取元素
for (int i = 0; i < listt.size(); i++) {
System.out.println((Integer)(listt.get(i)));
}
// 调用remove删除集合中的元素
listt.remove(0);
// 采用Iterator遍历数据(while)
Iterator iter=listt.iterator();
while (iter.hasNext()){
System.out.println((Integer)iter.next());
}
// 采用Iterator遍历数据(for)
for(Iterator iter1=listt.iterator();iter1.hasNext();){
System.out.println((Integer)iter1.next());
}
// 判断集合中是否包含:3
System.out.println(listt.contains(3));
// 判断集合是否为空
System.out.println(listt.isEmpty());
// 转换成对象数据
Object[] arr=listt.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println((Integer)arr[i]);
}
// 运行时自动创建相应类型的数组
Integer[] iArr=new Integer[listt.size()];
listt.toArray(iArr);
for (int i = 0; i < iArr.length; i++) {
System.out.println(iArr[i]);
}
}
}
LinkedList
用法同ArrayList,将上述代码中List listt=new ArrayList();替换为List listt=new LinkedList();即可。
Set HashSetHashSet中的数据是无序,不可重复的。
public class HashSetTest {
public static void main(String[] args) {
Set set=new HashSet();
set.add("c");
set.add("r");
set.add("y");
// 输出是无序的
for(Iterator iter=set.iterator();iter.hasNext();){
System.out.println(iter.next());
}
// 加入重复数据
set.add("c");
for(Iterator iter=set.iterator();iter.hasNext();){
System.out.println(iter.next());
}
String s1="abc";
String s2="abc";
System.out.println("s1 equals s2,"+s1.equals(s2));
// equals相等,hashcode一定是相等的
System.out.println("s1.hashCode"+s1.hashCode());
System.out.println("s2.hashCode"+s2.hashCode());
String s3="cry";
System.out.println("s1 equals s3,"+s1.equals(s3));
System.out.println("s3.hashCode"+s3.hashCode());
}
}
equals和hashCode
public class HashSetTest {
public static void main(String[] args) {
Person p1=new Person();
p1.name="张三";
p1.age=21;
Person p2=new Person();
p2.name="李四";
p2.age=24;
Person p3=new Person();
p3.name="张三";
p3.age=23;
Set set=new HashSet();
set.add(p1);
set.add(p2);
set.add(p3);
for(Iterator iter=set.iterator();iter.hasNext();){
Person p=(Person)iter.next();
System.out.println("name="+p.name+",age="+p.age);
}
System.out.println("p1.hashCode="+p1.hashCode());
System.out.println("p2.hashCode="+p2.hashCode());
System.out.println("p3.hashCode="+p2.hashCode());
}
}
class Person{
String name;
int age;
}
输出结果:
name=李四,age=24 name=张三,age=21 name=张三,age=23 p1.hashCode=1163157884 p2.hashCode=1956725890 p3.hashCode=356573597
加入了重复的数据,因为hashCode不同,所以存储格式也不同。
只有重写equals和hashCode后,程序会在hashCode相同时,调用equals进行比较,如果equals相等将不把此元素加入到Set,如果equals比较不相等则会重新根据hashCode换算位置仍然将该元素加入进去。
class Person{
String name;
int age;
// 重写equals
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
特别强调:向HashSet或HashMap中加入数据时必须同时覆盖equals和hashCode方法!
Java要求:
- 两个对象equals相等,那么它的hashcode相等
- 两个对象equals不相等,那么它的hashcode并不要求它不相等,但一般建议不相等
- hashcode相等不代表两个对象相等(需要采用equals比较)
TreeSet可以对Set集合进行排序,默认自然升序,也可以做客户化的排序
- 对自然数进行排序
public class TreeSetTest {
public static void main(String[] args) {
Set set=new TreeSet();
set.add(3);
set.add(2);
set.add(4);
//不能存放重复数据
set.add(3);
// set.add("abc"); TreeSet只对一种类型排序,加入abc后将无法排序,
for(Iterator iter = set.iterator();iter.hasNext();){
System.out.println((Integer)iter.next());
}
}
}
- 对Person进行排序
public class TreeSetTest {
public static void main(String[] args) {
Person p1=new Person();
p1.name="张三";
p1.age=21;
Person p2=new Person();
p2.name="李四";
p2.age=24;
Person p3=new Person();
p3.name="张三";
p3.age=23;
Set set=new TreeSet();
set.add(p1);
set.add(p2);
set.add(p3);
for (Iterator iter=set.iterator(); iter.hasNext();) {
Person p = (Person)iter.next();
System.out.println("name=" + p.name + ", age=" + p.age);
}
}
}
class Person implements Comparable{
String name;
int age;
}
出现错误,提示必须完成Comparable接口。基本类型的包装类以及String类可以排序是因为它们都实现了Comparable接口。
Exception in thread "main" java.lang.ClassCastException: InterfaceTest.Person cannot be cast to java.lang.Comparable at java.util.TreeMap.compare(TreeMap.java:1294) at java.util.TreeMap.put(TreeMap.java:538) at java.util.TreeSet.add(TreeSet.java:255) at InterfaceTest.TreeSetTest.main(TreeSetTest.java:23)实现Comparable接口完成排序
public class TreeSetTest {
public static void main(String[] args) {
Person p1=new Person();
p1.name="张三";
p1.age=21;
Person p2=new Person();
p2.name="李四";
p2.age=24;
Person p3=new Person();
p3.name="张三";
p3.age=23;
Set set=new TreeSet();
set.add(p1);
set.add(p2);
set.add(p3);
for (Iterator iter=set.iterator(); iter.hasNext();) {
Person p = (Person)iter.next();
System.out.println("name=" + p.name + ", age=" + p.age);
}
}
}
class Person implements Comparable{
String name;
int age;
// 如果重写了equals,最好保证equals和compareto比较规则保持一致
public int compareTo(Object o){
if(o instanceof Person){
Person p=(Person)o;
// 降序
return (p.age-this.age);
}
throw new IllegalArgumentException("非法参数,O="+o);
}
}
实现Comparator接口完成排序
public class TreeSetTest {
public static void main(String[] args) {
Person p1=new Person();
p1.name="张三";
p1.age=21;
Person p2=new Person();
p2.name="李四";
p2.age=24;
Person p3=new Person();
p3.name="张三";
p3.age=23;
Comparator personComparator=new PersonComparator();
Set set=new TreeSet(personComparator);
set.add(p1);
set.add(p2);
set.add(p3);
for (Iterator iter=set.iterator(); iter.hasNext();) {
Person p = (Person)iter.next();
System.out.println("name=" + p.name + ", age=" + p.age);
}
}
}
class Person{
String name;
int age;
}
// 实现Person比较器
class PersonComparator implements Comparator{
@Override
public int compare(Object o1, Object o2) {
if(!(o1 instanceof Person)){
throw new IllegalArgumentException("非法参数,o1="+o1);
}
if(!(o2 instanceof Person)){
throw new IllegalArgumentException("非法参数,o2="+o2);
}
Person p1=(Person)o1;
Person p2=(Person)o2;
return p1.age-p2.age;
}
}
采用匿名类完成Comparator的实现
public class TreeSetTest {
public static void main(String[] args) {
Person p1=new Person();
p1.name="张三";
p1.age=21;
Person p2=new Person();
p2.name="李四";
p2.age=24;
Person p3=new Person();
p3.name="张三";
p3.age=23;
Set set=new TreeSet(new Comparator(){
public int compare(Object o1, Object o2) {
if(!(o1 instanceof Person)){
throw new IllegalArgumentException("非法参数,o1="+o1);
}
if(!(o2 instanceof Person)){
throw new IllegalArgumentException("非法参数,o2="+o2);
}
Person p1=(Person)o1;
Person p2=(Person)o2;
return p1.age-p2.age;
}
});
set.add(p1);
set.add(p2);
set.add(p3);
for (Iterator iter=set.iterator(); iter.hasNext();) {
Person p = (Person)iter.next();
System.out.println("name=" + p.name + ", age=" + p.age);
}
}
}
class Person{
String name;
int age;
}
Comparable与Comparator
Comparable是默认的比较接口,Comparator可以分离比较规则,更加灵活。
一个类实现了Comparable接口则表明该类的对象之间是可以相互比较的,这个类对象组成的集合可以直接使用sort方法排序。
Comparator可以看作是一种算法的实现,将算法与数据分离,Comparator还可以在以下两种环境使用:
- 类没有考虑到比较问题而没有实现Comparable,可以用过Comparator来实现排序并且不用改变对象本身
- 可以使用多种排序标准,如升序、降序等。
Map中可以放置键值对,每个元素包含键对象和值对象,Map实现较常用的为HashMap,HashMap对键对象的存取和HashSet一样,仍然采用的是哈希算法,所以如果使用自定义类作为Map的键对象,必须重写equals和hashCode方法。
遍历Map集合的两种方式都要精通。
第一种:获取所有key,遍历每个key,通过key获取value.
第二种:获取Set
调用entry.getKey() entry.getValue()
public class HashMapTest {
public static void main(String[] args) {
Map map=new HashMap();
map.put("101","张三");
map.put("102","李四");
map.put("103","王五");
// 采用entrySet遍历Map
Set entrySet=map.entrySet();
for(Iterator iter=entrySet.iterator();((Iterator) iter).hasNext();){
Map.Entry entry=(Map.Entry)iter.next();
System.out.println(entry.getKey()+","+entry.getValue());
}
// 取得map中指定的key
Object v=map.get("101");
System.out.println("101-----"+v);
// 如果存在相同的条目,会采用此条目替换
// 但map中始终保持的是不可重复的数据
// 主要依靠key值判断是否重复,和value无关
map.put("103","cry");
// 采用keySet和get获取map中所有的数据
for(Iterator iter=map.keySet().iterator();iter.hasNext();){
String k=(String)iter.next();
System.out.println(k+","+map.get(k));
}
}
}
HashMap,采用自定义类作为key
public class HashMapTest02 {
public static void main(String[] args) {
IdCard idCard1=new IdCard();
idCard1.cardNo=123456789L;
Player player1=new Player();
player1.name="张三";
IdCard idCard2=new IdCard();
idCard2.cardNo=123456789L;
Player player2=new Player();
player2.name="李四";
IdCard idCard3=new IdCard();
idCard3.cardNo=123456789L;
Player player3=new Player();
player3.name="张三";
Map map=new HashMap();
map.put(idCard1,player1);
map.put(idCard2,player2);
map.put(idCard3,player3);
for(Iterator iter=map.entrySet().iterator();((Iterator) iter).hasNext();){
Map.Entry entry=(Map.Entry)iter.next();
IdCard idCard=(IdCard)entry.getKey();
Player player=(Player)entry.getValue();
System.out.println(idCard.cardNo+","+player.name);
}
}
}
class IdCard{
long cardNo;
}
class Player{
String name;
}
以上加入了重复的数据:
123456789,李四 123456789,张三 123456789,张三
因为 HashMap 的底层实现采用的是 hash 表,所以 Map 的 key 必须重写hashcode 和 equals 方法。
HashMap,重写IdCard的equals和hashCode方法class IdCard{
long cardNo;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IdCard idCard = (IdCard) o;
return cardNo == idCard.cardNo;
}
@Override
public int hashCode() {
return Objects.hash(cardNo);
}
}
class Player{
String name;
}
此处对IdCard的equals和hashCode进行了重写,原程序并没有加入重复的数据。
123456789,张三TreeMap
TreeMap可以对Map的key进行排序,如果map中的key采用的是自定义类则需要实现Comparable或Comparator接口完成排序。
public class TreeMapTest {
public static void main(String[] args) {
Map map=new TreeMap();
map.put("103","王五");
map.put("101","张三");
map.put("102","李四");
for(Iterator iter=map.entrySet().iterator();((Iterator) iter).hasNext();){
Map.Entry entry=(Map.Entry)iter.next();
System.out.println(entry.getKey()+","+entry.getValue());
}
}
}
Collections工具类
Collections位于java.util包中,提供了一系列实用的方法,如:对集合排序,对集合的内容查找等。
public class CollectionsTest {
public static void main(String[] args) {
List listt=new ArrayList();
listt.add(4);
listt.add(1);
listt.add(3);
listt.add(2);
for(Iterator iter=listt.iterator();((Iterator) iter).hasNext();){
System.out.print(iter.next());
}
System.out.println("");
Collections.sort(listt);
for(Iterator iter=listt.iterator();iter.hasNext();){
System.out.print(iter.next());
}
System.out.println("");
Set set=new HashSet();
set.add(9);
set.add(6);
set.add(8);
set.add(7);
// 不能直接对set排序
// Collections.sort(set);
List setList=new ArrayList(set);
Collections.sort(setList);
for(Iterator iter=setList.iterator();iter.hasNext();){
System.out.print(iter.next());
}
System.out.println("");
int index=Collections.binarySearch(setList,9);
System.out.println("index="+index);
Collections.reverse(setList);
for(Iterator iter=setList.iterator();iter.hasNext();){
System.out.print(iter.next());
}
}
}
泛型
泛型能更早的发现错误,如类型转换错误,通常在运行期才会发现,如果使用泛型则能够在编译期发现错误。通常错误发现的越早越容易调试,从而减少成本。
为什么使用泛型public class GenericTest {
public static void main(String[] args) {
List listt=new ArrayList();
listt.add(1);
listt.add(2);
listt.add(3);
for(Iterator iter=listt.iterator();((Iterator) iter).hasNext();){
String s=(String)iter.next();
System.out.println(s);
}
}
}
出现java.lang.ClassCastException异常,这种转型错误最好能在编译期发现,那么就必须使用泛型。
案例一public class GenericTest {
public static void main(String[] args) {
List listt=new ArrayList();
listt.add(1);
listt.add(2);
listt.add(3);
// 使用了泛型,abc无法放进集合中,会在编译期报错
// listt.add("abc");
for(Iterator iter=listt.iterator();iter.hasNext();){
// 使用了泛型,编译器会报错
// String s=(String)iter.next();
// 使用了泛型,在编译器不用强制转换
// Integer s=(Integer)iter.next();
// 可以直接获取相应元素,使用泛型返回的是真正的类型
Integer s=iter.next();
System.out.println(s);
}
}
}
采用泛型改善自定义比较器
public class GenericTest {
public static void main(String[] args) {
Person p1=new Person();
p1.name="张三";
p1.age=21;
Person p2=new Person();
p2.name="李四";
p2.age=24;
Person p3=new Person();
p3.name="张三";
p3.age=23;
Set set=new TreeSet();
set.add(p1);
set.add(p2);
set.add(p3);
for (Iterator iter=set.iterator(); iter.hasNext();) {
Person p = (Person)iter.next();
System.out.println("name=" + p.name + ", age=" + p.age);
}
}
}
class Person implements Comparable{
String name;
int age;
@Override
// 使用了泛型,instanceof就不用再写了
public int compareTo(Person o) {
return (this.age-o.age);
}
}
采用泛型改造Map
public class HashMapTest02 {
public static void main(String[] args) {
IdCard idCard1=new IdCard();
idCard1.cardNo=123456789L;
Player player1=new Player();
player1.name="张三";
IdCard idCard2=new IdCard();
idCard2.cardNo=123456789L;
Player player2=new Player();
player2.name="李四";
IdCard idCard3=new IdCard();
idCard3.cardNo=123456789L;
Player player3=new Player();
player3.name="张三";
Map map=new HashMap();
map.put(idCard1,player1);
map.put(idCard2,player2);
map.put(idCard3,player3);
for(Iterator> iter=map.entrySet().iterator();((Iterator) iter).hasNext();){
// Map.Entry entry=(Map.Entry)iter.next();
// IdCard idCard=(IdCard)entry.getKey();
// Player player=(Player)entry.getValue();
Map.Entry entry=iter.next();
IdCard idCard=entry.getKey();
Player player=entry.getValue();
System.out.println(idCard.cardNo+","+player.name);
}
}
}
class IdCard{
long cardNo;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IdCard idCard = (IdCard) o;
return cardNo == idCard.cardNo;
}
@Override
public int hashCode() {
return Objects.hash(cardNo);
}
}
class Player{
String name;
}
自定义泛型
public class GenericTest{ private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } public static void main(String[] args) { GenericTest g=new GenericTest (); g.setObj("abc"); String s=g.getObj(); } }
如上
- JDK5.0新特性
foreach循环
对数组怎么遍历?
for(int i : arr){
System.out.println(i);
}
对集合怎么遍历?
for(String s : list){
System.out.println(s);
}
- JDK8.0新特性:钻石表达式
Listlist = new ArrayList<>();
类型自动推断!
遗留类对比表| 遗留类 | 缺点 | 取代类 |
|---|---|---|
| Vector | 方法是同步的,影响性能 | ArrayList和LinkedList |
| Hashtable | 方法是同步的,影响性能 | HashMap |
| Stack | 继承了Vector,影响性能 | LinkedList |
| Enumeration | 只能与历史集合使用 | Iterator |



