集合框架—–Collection和Map
记录一下学习的集合框架,梳理思路,也便于以后的查看。如果有错误之处,还望大家不吝指出,多谢哈。
集合框架之Collection
来张Collection体系图:
Collection概述:
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
List
- 集合中存取的元素有序(存和取的顺序一致),有索引,可以存储重复值。
Set
- 集合中存取的元素无序(存和取的顺序不一致),无索引,不可以存储重复的值。
注意:因为List和Set都是Collection的子接口,如果想使用接口,就必须使用它的实现类。如:
List list = new Arraylist();
Collection集合遍历
- 1,将集合转换成数组,(利用
toArray()
方法 )再使用for循环遍历即可。 2,迭代器遍历(使用集合中
iterator()
方法获得Iterator对象)- 伪代码如下:
Iterator it = list.iterator(); //获取Iterator 对象 while(it.hasNext()) { //判断集合中是否还有元素 it.next(); //得到元素值 }
- 伪代码如下:
3,增强for循环遍历
- 伪代码如下
Collection c = new ArrayList(); //Object obj = new Student("张三",23); c.add(new Student("张三",23)); //Object obj = new Student("张三",23); 这里会自动类型提升为Object c.add(new Student("李四",24)); c.add(new Student("王五",25)); c.add(new Student("马六",26)); for (Object value : c) { System.out.println(value); }
- 1,将集合转换成数组,(利用
List集合特有的遍历
- 1,根据List集合的特点:有索引,可以通过
list.get(int index);
方法遍历 - 2,List特有的方法:ListIterator。根据
listIterator()
方法获得ListIterator迭代器对象。
- 1,根据List集合的特点:有索引,可以通过
集合遍历注意事项
- 当使用Iterator迭代器遍历集合需要增添或修改元素时,很可能会发生并发修改异常(ConcurrentModificationException)。
- 原因:当遍历集合时,增添或修改集合中元素,使用的是集合的方法,而迭代器并不知道,这就导致出现了并发修改异常。
- 解决办法:
- 1,使用普通for循环(通过索引删除,一个集合的最大索引为:list.size() - 1; 注意:索引要字减一)list.remove(index–);
- 2,使用ListIterator迭代器,因为此迭代器中特有添加和删除的方法。
- 当使用Iterator迭代器遍历集合需要增添或修改元素时,很可能会发生并发修改异常(ConcurrentModificationException)。
List集合中三个子类的特点
- ArrayList:底层数据结构是数组,查询修改快,增删慢,线程不安全,但效率高。
- Vector:底层数据结构是数组,查询修改增删都慢,线程安全,但效率低。
- LinkedList:底层数据结构是链表,查询修改慢,增删快,线程不安全,但效率高。
- 结论:
- 查询多用ArrayList
- 增山多用LinkedList
- 都多用Arraylist
泛型
- 如:
Collection<String> c = new Arraylist<>();
定义了一个泛型为String类型的集合。 - 使用泛型的好处:
- 提高安全性(将运行期的错误转换到编译期)
- 省去强转的麻烦
- 注意:
- <>中放的必须是引用数据类型。
- 前后的泛型必须一致,或者后面的泛型可以省略不写(JDK1.7新特性,菱形泛型)
- 泛型最好不要定义成Object,没有意义。
- 如:
- HashSet集合类(Set集合的实现类)
- 特点:
- 方法和Collection中方法一样
- 当向Set集合中存储重复元素时,返回值为false
- HashSet的继承体系中有重写toString方法
- HashSet存储自定义对象:
- 为了保证元素的唯一性,自定义对象的类中必须重写hashCode()和equals()方法。
- 当哈希值一样时,才会调用equals()方法。
- 为了保证元素的唯一性,自定义对象的类中必须重写hashCode()和equals()方法。
- 特点:
- LinkedHashSet集合类(HashSet的子类)
- 特点:
- 底层是链表实现的,是Set集合中唯一保证怎么存就怎么取。
- 该类是HashSet的子类,所以也是保证元素唯一的,与HashSet的原理一样。
- 特点:
- TreeSet集合类(Set集合的实现类)
- 特点:
- 保证元素的唯一性
- 可以对元素进行排序
- 排序的方式(自定义对象时)
- 自然排序:自定义对象的类需要实现Compareable接口,并重写接口中compareTo()方法。
- 在TreeSet集合类中如何存储元素取决于compareTo()方法的返回值:
- 返回值为0:集合中只会存储一个元素。
- 返回值为正数:集合怎么存,就会怎么输出。
- 返回值为负数:集合会将存储的元素倒序输出。
- 来个自然排序的代码:
- 在TreeSet集合类中如何存储元素取决于compareTo()方法的返回值:
- 自然排序:自定义对象的类需要实现Compareable接口,并重写接口中compareTo()方法。
- 特点:
//自定义对象,Person类
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
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;
}
@Override
//按照年龄排序
public int compareTo(Person p) {
//排序:如果是新的(后进来的)减去老的(先进来的),是从小到大排序。
// 如果是老的减去新的,就是从大到小排序
// this代表新的,p代表老的
int num = this.age - p.age; //判断年龄是否相同
//当年龄相同是,比较姓名是否相同。
int num2 = num == 0 ? this.name.compareTo(p.name) : num;
return num2;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
//测试类
import java.util.TreeSet;
public class TreeSetTest {
/**
* 演示
* 按年龄的大小排序(按年龄从小到大排序)
* TreeSet存储自定义对象并遍历(按年龄从小到大排序)
*/
public static void main(String[] args) {
TreeSet<Person> ts = new TreeSet<>();
ts.add(new Person("zhangsan", 23));
ts.add(new Person("lisi", 16));
ts.add(new Person("wangwu", 24));
ts.add(new Person("zhaoliu", 34));
ts.add(new Person("aaaa", 44));
System.out.println(ts);
}
}
//输出结果:
/*
[Person [name=lisi, age=16],
Person [name=zhangsan, age=23],
Person [name=wangwu, age=24],
Person [name=zhaoliu, age=34],
Person [name=aaaa, age=44]]
*/
比较排序:Comparator以内部类的形式实现。
代码如下:
//自定义对象Person类
public class Person {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
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;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
//测试类
import java.util.Comparator;
import java.util.TreeSet;
public class Test5_1 {
/**
* 演示
* TreeSet存储自定义对象并遍历(按照姓名的长度排序)
*/
public static void main(String[] args) {
//利用比较器排序(匿名内部类)
TreeSet<Person> ts = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
int length = p1.getName().length() - p2.getName().length();
int num2 = length == 0 ? p1.getName().compareTo(p2.getName()) : length;
return num2 == 0 ? p1.getAge() - p2.getAge() : num2;
}
});
ts.add(new Person("zhangsan", 23));
ts.add(new Person("lisi", 25));
ts.add(new Person("wangwu", 26));
ts.add(new Person("zhaoliu", 28));
ts.add(new Person("aaaa", 36));
System.out.println(ts);
}
}
//输出结果
/*
[Person2 [name=aaaa, age=36],
Person2 [name=lisi, age=25],
Person2 [name=wangwu, age=26],
Person2 [name=zhaoliu, age=28],
Person2 [name=zhangsan, age=23]]
*/
集合框架之Map
来张Map体系图:
- Map概述:
- 请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。
- Map接口和Collection的区别:
- 1,Map接口是双列,而Collection接口是单列。
- 2,Map体系中键是唯一的,Collection的子体系Set是唯一的。
- 3,Map集合的数据结构的值针对键有效,跟值无关。Collection集合的数据结构是针对元素有效。
- Map存储键值时:
- 代码演示,伪代码如下:
Map<String,Integer> map = new HashMap<>();
Integer i1 = map.put("张三",23); //返回值为null
Integer i2 = map.put("李四",24); //返回值为null
Integer i2 = map.put("张三",30); //返回值为23(出现相同键值对时,不会存储,会覆盖原来的键值对。)
System.out.println(map);
//输出结果
/*
{张三=30, 李四=24}
*/
- Map集合遍历
**注意:**Map集合中没有迭代器
- A :
- 1,根据键获取值:首先获取所有键的集合(
Set<> set keySet = map.keySet();
)接下来就可以使用迭代器了,根据键获取值(map.get(key);
) - 2,增强for循环遍历(和第一种方法相似)
-for(k ,key : map.keySet()) { System.out.println(key + " = " + map.get(key));}
- 1,根据键获取值:首先获取所有键的集合(
- B:根据键值对 对象,获取键和值。
- 利用entrySet()方法,获得键值对 对象,得到一个Set集合。遍历Set集合。
- 代码如下(来个完整版的):
- A :
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class MapEntry {
/**
* map遍历:
* 根据键值对 对象,获取键和值。
*/
public static void main(String[] args) {
Map<String ,Integer> map = new HashMap<String, Integer>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
map.put("赵六", 26);
map.put("马奇", 27);
//利用entrySet()方法获取键值对 对象 得到一个set集合(以下两种写法,均可)
//Map.Entry 这是Map接口内又写了Entry接口
Set<Map.Entry<String,Integer>> set = map.entrySet();
//Set<Entry<String,Integer>> set = map.entrySet();
//遍历键值对 对象 set集合
for (Entry<String, Integer> entry : set) {
String key = entry.getKey(); //通过键值对 对象 的getKey方法获得键
Integer value = entry.getValue(); //返回与此项对应的值。
System.out.println(key + "=" + value);
}
}
}
- HashMap集合类
- 注意:存储自定义对象时,和HashSet一样,需要重写hashCode()和equals()方法。(键 是自定义对象,值 是引用数据类型)
- LinkedHashMap集合类
- 和LinkedHashSet原理一样。
- TreeMap集合类
- 和TreeMap原理一样。
- (面试题)HashMap和Hashtable的区别:
- 共同点: 底层都是哈希算法,都是双列集合
- 不同点:
- A:
- 1,HashMap是线程不安全的,效率高,JDK1.2版本
- 2,Hastable是线程安全的,效率低,JDK1.0版本
- B:
- 1,HashMap可以存储null键和值
- 2,Hastable不可以存储null键和值
- A: