集合


本文主要记录了集合使用中的一些知识点,关于集合框架的API等,请参考书籍。

概述

在这里插入图片描述
在这里插入图片描述

List集合

1、List集合特点:存储的元素有顺序,可重复。
2、List接口有两个实现类:ArrayList和LinkedList。除此之外,还有一个Vector。

3、ArrayList底层采用数组实现,查询快,增删慢。
解释:查询快是因为可以根据索引查询元素。但是Java中数组的长度是固定的,当存入的元素超过数组长度时,ArrayList将会创建一个更大的数组(当前容量*1.5+1)来存储元素,所以增删操作会导致创建新的数组,效率低。

2018-04-20补充:ArrayList创建时如果不在构造方法形参中传入数组大小,默认大小为10。
比如List list = new ArrayList(20);创建一个初始大小为20的ArrayList。
4、LinkedList底层采用双向循环链表实现,增删快,查询慢。

5、Vector的用法和ArrayList完全相同,区别是Vector使用同步,线程安全但效率低;而ArrayList使用异步,线程不安全但效率高。
此外,两者底层都使用数组实现,Vector满则扩充为原来的2倍。

6、遍历List集合与Set集合的两种方法:
(1)方式一:Iterator接口
(2)方式二:foreach循环

Set集合

1、Set集合特点:存储的元素无序,不可重复。
2、Set接口主要有两个实现类:HashSet和TreeSet。HashSet根据对象的哈希值来确定元素在集合中的存储位置,所以取出时也可以根据哈希码快速找到,并且取出时是无序的,也不一定是增加时的顺序;而TreeSet是以二叉树的方式来存储元素,存储时自动排序,取出时就是按照顺序取出的。
3、HashSet添加元素的过程分析:
在这里插入图片描述

所以,为了保证HashSet正常工作,应该重写要存入对象的equals方法和hashCode方法。Java规范中要求,如果程序员重写了equals方法,就一定要重写hashCode方法。当两个对象调用equals方法比较时,如果返回true,那么他们的hashCode值要求返回值相等。

补充:哈希值说明:
hashCode可以理解为身份证号,区别是身份证号相同一定是同一个人,但哈希码相同却不一定是同一个对象。
1)如果两个对象相同,那么他们的hashCode(哈希码)一定要相同;
2)如果两个对象的hashCode(哈希码)相同,这两个对象并不一定相同。
4、TreeSet添加元素时会根据规则自动排序,过程分析:

在这里插入图片描述

第一个元素,放在二叉树的顶端;第二个元素,调用compareTo方法与第一个元素比较,如果小于第一个元素(返回-1),放在左子树;如果大于(返回1),放在右子树;如果等于(返回0),舍弃该元素。之后的元素以此类推,按照左子树小于右子树的顺序进行排序。

综上所述,TreeSet在进行元素比较时,需要调用compareTo方法,该方法是Comparable接口中定义的,所以要存储的对象需要实现该接口,重写compareTo方法,否则将会报异常。
JDK中大部分类都实现了该接口,如Integer,Double,String等。但是自定义的类要想存储到TreeSet集合中,必须实现Comparable接口,重写compareTo方法,否则将报异常。
除了实现Comparable接口外,还可以自定义比较器,示例代码如下:

import java.util.*;
class MyComparator implements Comparator { // 定义比较器实现Comparator接口
public int compare(Object obj1, Object obj2) { // 实现比较方法
String s1 = (String) obj1;
String s2 = (String) obj2;
int temp = s1.length() - s2.length();
return temp;
}
}
public class Example14 {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new MyComparator());// 创建TreeSet对象时传入自定义比较器
ts.add("Jack");// 向该Set对象中添加字符串
ts.add("Helena");
ts.add("Eve");
Iterator it = ts.iterator(); // 获取Iterator对象
// 通过while循环,逐渐将集合中的元素打印出来
while (it.hasNext()) {
// 如果Iterator有元素进行迭代,则获取元素并进行打印
Object obj = it.next();
System.out.println(obj);
}
}
}

5、HashSet与TreeSet的使用代码示例

public class Student implements Comparable{



private String name;
private int age;



public Student(String name, int age) {
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;
}



//重写toString方法
@Override
public String toString() {
// TODO Auto-generated method stub
return "姓名:" + this.name + ",年龄:" + this.age;
}



//重写compareTo方法
@Override
public int compareTo(Object o) {
Student stu = (Student) o;
if(this.age > stu.getAge()) {
return 1;
} else if (this.age < stu.getAge()) {
return -1;
} else {
//如果年龄相同,按照姓名比较(字符串已经实现了compareTo方法)
return this.name.compareTo(stu.getName());
}
}



//重写hashCode方法
@Override
public int hashCode() {
//自定义编码公式,其中字符串类已经实现了hashCode方法,可以直接调用
return this.name.hashCode() * this.age;
}



//重写equals方法
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Student)) {//不是Student实例
return false;
}
if(obj == this) {//先比较地址相等
return true;
}
Student stu = (Student) obj;
if(this.name.equals(stu.getName())&&this.age == stu.getAge()) {
return true;
}
return false;
}



}



//以下是测试方法



//测试HashSet散列存储
//需要存储的对象重写hashCode方法和equals方法
@Test
public void testHashSet() {
Set<Student> s = new HashSet<>();
s.add(new Student("大王", 22));
s.add(new Student("张三", 22));
s.add(new Student("李四", 40));
s.add(new Student("李四", 40));



Iterator<Student> iterator = s.iterator();
while (iterator.hasNext()) {
Student student = (Student) iterator.next();
System.out.println(student);
}
}



//测试TreeSet的自动排序
//需要存储的对象重写compareTo方法
@Test
public void testTreeSet() {
Set<Student> s = new TreeSet<>();
s.add(new Student("大王", 22));
s.add(new Student("张三", 22));
s.add(new Student("李四", 40));
s.add(new Student("王五", 28));
s.add(new Student("李四", 40));



Iterator<Student> iterator = s.iterator();
while (iterator.hasNext()) {
Student student = (Student) iterator.next();
System.out.println(student);
}
}

Map集合

1、Map接口存放的是键值对。常用的实现类有HashMap、TreeMap、LinkedHashMap。其中LinkedHashMap与HashMap的区别是前者取出时的顺序与存入时的顺序相同,而后者不相同。原因:LinkedHashMap和LinkedList一样,底层使用双向循环链表实现每一个元素都记录了其前后元素的引用,所以遍历时的顺序与存入时的顺序相同。
2、Map集合如果存入元素的key相同,则会覆盖掉原来的key-value。

3、TreeMap与TreeSet一样,都是采用二叉树的原理实现,并且存储的键都是按照特定规则自动排序的。
所以作为键的对象应该实现Comparable接口,重写compareTo方法。比如Integer、Double、String等类型已经实现了该接口,如果使用这些类型作为键的类型,在存入键值对时,就会自动排序(如字符串类型的键是按照首字母排序);如果使用自定义的类型作为键的类型,自定义类就要实现Comparable接口,重写compareTo方法,或者自定义比较器,类似TreeSet。
4、HashMap允许存入null键null值。

5、遍历Map集合的两种方式
(1)方式一:先遍历所有的键,在分别取出对应的值。

//测试HashMap
@Test
public void testMap1() {
Map<String, String> map = new HashMap<>();
map.put("1", "Jack");
map.put("2", "Rose");
map.put("3", "Lucy");



//遍历输出map
Set<String> keys = map.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String key = (String) iterator.next();
String value = map.get(key);
System.out.println("key="+key+",value="+value);
}
}

(2)方式二:先取出Map集合中的所有键值对,再分别取出键和值。

//测试HashMap
@Test
public void testMap2() {
Map<String, String> map = new HashMap<>();
map.put("1", "Jack");
map.put("2", "Rose");
map.put("3", "Lucy");



//遍历输出map
Set<Entry<String,String>> entrySet = map.entrySet();
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
//Entry是Map接口内部类,每个Map.Entry代表一对键值对
Map.Entry entry = (Map.Entry) iterator.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
System.out.println("key="+key+",value="+value);
}
}

5、Properties集合是Hashtable类的子类,Hashtable与HashMap类似,区别是前者是线程安全的,但是存取速度慢,目前基本上被HashMap取代。Properties集合常用来存取配置项。
Properties的key和value只能是字符串类型。
HashMap的key和value可以是基本数据类型和引用类型。(但是key如果是引用类型注意要重写hashCode和equals方法)

6、HashMap与Hashtable的区别:增加于2018-04-20
①、HashMap允许null键null值,Hashtable不允许;
②、HashMap把Hashtable的contains方法去掉了,改成containsvalue、containsKey,因为contains容易引起误解。
③、Hashtable采用同步,线程安全;HashMap线程不安全。
④、Hashtable使用Enumeration进行遍历,HashMap使用Iterator进行遍历。
⑤、Hashtable中的hash数组默认大小是11,增加方式是old*2+1;HashMap中hash数组默认大小是16,而且一定是2的指数。
⑥、Hashtable直接使用对象的hashCode。
⑦、Hashtable类继承Dictionary类,实现Map接口;HashMap继承AbstractMap抽象类,AbstractMap实现Map接口。

Collections工具类

Collection是集合类的父接口,而Collections是操作集合的工具类,二者不可混为一谈。Collections提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作
在这里插入图片描述

Iterator接口

1、Collection接口有一个iterator方法,返回一个Iterator接口的实现类对象。该接口主要用来遍历集合,也叫作迭代器。
2、需要注意,如果在使用Iterator遍历集合时,删除集合中的元素,必须使用Iterator接口的remove方法,而不能使用Collection接口的remove方法,否则将会报异常。
3、Iterator接口有一个子类ListIterator,可以从后到前的遍历集合。

总结

1、集合类和Array的区别、择取
*
容器类仅能持有对象引用(指向对象的指针),而不是将对象信息copy一份至数列某位置。
*
一旦将对象置入容器内,便损失了该对象的型别信息。

2、 如何选择?
*
在各种Lists中,最好的做法是以ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList();
Vector总是比ArrayList慢,所以要尽量避免使用。
*
在各种Sets中,HashSet通常优于TreeSet(插入、查找)。只有当需要产生一个经过排序的序列,才用TreeSet。
TreeSet存在的唯一理由:能够维护其内元素的排序状态。
*
在各种Maps中
HashMap用于快速查找。
*
当元素个数固定,用Array,因为Array效率是最高的。
结论:最常用的是ArrayList,HashSet,HashMap,Array。而且,我们也会发现一个规律,用TreeXXX都是排序的。

注意:
1、Collection没有get()方法来取得某个元素。只能通过iterator()遍历元素。
2、Set和Collection拥有一模一样的接口。
3、List,可以通过get()方法来一次取出一个元素。使用数字来选择一堆对象中的一个,get(0)…。(add/get)
4、一般使用ArrayList。用LinkedList可以构造堆栈stack、队列queue。**
5、Map用 put(k,v) / get(k),还可以使用containsKey()/containsValue()来检查其中是否含有某个key/value。
HashMap可以利用对象的hashCode来快速找到key。

*hashing
哈希码就是将对象的信息经过一些转变形成一个独一无二的int值,这个值存储在一个array中。
我们都知道所有存储结构中,array查找速度是最快的。所以,可以加速查找。

发生碰撞时,让array指向多个values。即,数组每个位置上又生成一个梿表。
6、Map中元素,可以将key序列、value序列单独抽取出来。
使用keySet()抽取key序列,将map中的所有keys生成一个Set。
使用values()抽取value序列,将map中的所有values生成一个Collection。

为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值