一、Java集合类(容器类)简介:
Java集合可用于存储数量不等的对象,并可以实现常用的数据结构(如栈、队列等待),还可以用于保存具有映射关系的关联数组。Java集合就一种容器,可以把多个对象放进容器中,Java集合可以记住容器中的对象的数据类型,从而可以使代码更加简洁和健壮。
Java集合大致可以分为Set、List、Queue、Map四种体系。
- Set:代表无序、不可重复
- List:代表有序、重复的集合
- Queue:代表一种队列集合实现
- Map:代表具有映射关系的集合
1.Java集合与数组的区别:
- 数组的长度是不可变化的,在数组初始化时指定了数组长度,如果需求要动态添加数据,此时数据就无可为力了,而集合可以保存不确定数量的数据,同时也可以保存具有映射关系的数据。
- 同一个数组的元素即可是基本类型的值,也可以是对象(实际上保存的是对象的引用变量);而集合只能保存同一类型的对象。
2.Java集合体系间的继承关系
Java集合主要有两个接口派生而出:Collection和Map,这个两个接口是Java集合框架的根接口。
其中HashSet、TreeSet、ArrayList、LinkedList是经常用到的实现类
Map实现类是用于保存具有映射关系的数据。Map保存的每项数据都是键值对(key-value),Map中的key是不可重复的,key用于标识集合里的每项数据。
其中HashMap、TreeMap是经常用到的实现类
二、Collection接口
简介
Collection接口是Set、List、Queue接口的父接口,该接口所有的方法可以供其子类调用实现。
1.1:接口中的相关方法
下面是一些常用方法的数据操作例子,主要是添加、删除、清空、是否为空等:
Collection c= new ArrayList();
//添加元素
c.add("今天");
c.add("明天");
c.add(Integer.toString(8)); //基本数据类型需要转成包装类才能放入集合中
System.out.println("c集合中的元素:"+c);
//输出元素个数
System.out.println("c集合中的元素个数为:"+c.size());
//删除指定元素
c.remove(Integer.toString(8));
//再次输出集合中的元素个数
System.out.println("c集合中的元素个数为:"+c.size());
//判断是否包含指定对象
System.out.println("c集合中是否包含\"今天\"字符串:"+c.contains("今天"));
System.out.println("c集合中所有的元素:"+c);
Collection h= new HashSet();
h.add("明天");
h.add("明天的天气会下雨哦!");
//判断c集合中是否完全包含h集合
System.out.println("c集合中是否完全包含h集合?:"+c.containsAll(h));
//用c集合减去h集合中的元素
c.removeAll(h);
System.out.println("c集合中的元素:"+c);
//删除c中所有的元素
c.clear();
System.out.println("c集合中的元素:"+c);
输出结果:
c集合中的元素:[今天, 明天, 8]
c集合中的元素个数为:3
c集合中的元素个数为:2
c集合中是否包含"今天"字符串:true
c集合中所有的元素:[今天, 明天]
c集合中是否完全包含h集合?:false
c集合中的元素:[今天]
c集合中的元素:[]
1.2:使用Iterator(迭代器)遍历集合
Iterator接口是Collection接口的父接口,因此Collection集合可以直接调用其方法。
public interface Collection<E> extends Iterable<E> {……}
Iterator接口也是Java集合框架中的一员,与Collection系列和Map系列不同的是:
- Collection系列、Map系列集合主要是用于盛装数据对象的。
- Iteration主要是用于遍历Collection中的元素。
Iterator接口相关方法:
下面通过Iterator接口类遍历集合元素:
public class IteratorTest {
public static void main(String[] args) {
testIterator();
}
static void testIterator() {
// 创建集合添加元素
Collection<Person> personList = new ArrayList<Person>();
Person p1 = new Person("钟梅", 25);
Person p2 = new Person("王兴", 34);
personList.add(p1);
personList.add(p2);
// 获取集合的迭代器iterator
Iterator<Person> iterator = personList.iterator();
while (iterator.hasNext()) {
// 获取集合中的下一个元素
Person person = iterator.next();
System.out.println("person:" + person.name + "--" + person.age);
if (person.name.equals("王兴")) {
// 删除上一次迭代器next返回的元素,
iterator.remove();
}
// 对person对象中的变量赋值,会改变集合中元素的值
person.name = "马云";
person.age = 88;
}
System.out.println(personList.toString());
}
static class Person {
String name;
int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
}
编译输出结果:
person:钟梅--25
person:王兴--34
[Person [name=马云, age=88]]
从上面输出结果可以看出,对迭代变量person对象进行赋值,当再次输出personList集合时,会看到集合中的元素发生了改变。由此可知,当使用Iteration对集合进行遍历迭代,会把集合元素值传递给迭代变量。
当使用Iteration迭代变量Collection集合时,不可以在迭代过程中进行对集合添加、删除等操作,否则会引发java.util.ConcurrentModificationException异常,只能利用迭代器Iteration的remove方法进行删除上一次的next返回的元素。
……………………
// 获取集合的迭代器iterator
Iterator<Person> iterator = personList.iterator();
while (iterator.hasNext()) {
// 获取集合中的下一个元素
Person person = iterator.next();
System.out.println("person:" + person.name + "--" + person.age);
if (person.name.equals("王兴")) {
// 删除上一次迭代器next返回的元素,
// iterator.remove();
/**
* 如果用集合本身的添加或删除方法进行修改集合元素,此时会报java.util.ConcurrentModificationException
* 如果要删除其中某个元素,只能用迭代器Interator的remove方法
*/
personList.remove(person);
}
// 对person对象中的变量赋值,会改变集合中元素的值
person.name = "马云";
person.age = 88;
}
System.out.println(personList.toString());
上面的代码执行就会抛出异常:
2:Set集合
简介
可以把多个对象存放入Set集合内,在集合内是无法记住元素的添加顺序。Set集合与Collection基本相同,没有额外提供其他方法,实际上Set就是Collection,只是行为略有不同,Set不允许包含重复元素。
如果视图在Set集合中添加相同的元素(注意是同一个对象的引用,并非是两个元素值相同的),add方法会操作失败,返回false,新元素是无法被加入的。
public class CollectionTest {
public static void main(String[] args) {
testSet();
}
static void testSet(){
Set<Person> set = new HashSet<Person>();
Person p1 = new Person("钟梅", 25);
//添加同一个对象两次
boolean add1 = set.add(p1);
boolean add2 = set.add(p1);
System.out.println("add1:"+add1+"\nadd2:"+add2);
System.out.println(set.toString());
}
}
输出结果:
add1:true
add2:false
[Person [name=钟梅, age=25]]
从结果上可以,添加同一对象两次到集合中,第二次会操作失败add返回false,由此可见Set集合中不能存在相同的对象。接下来可以测试下添加有相同的元素值的两个对象看看结果是怎么样的:
static void testSet(){
Set<Person> set = new HashSet<Person>();
Person p1 = new Person("钟梅", 25);
Person p2 = new Person("钟梅", 25);
boolean add1 = set.add(p1);
boolean add2 = set.add(p2);
System.out.println("add1:"+add1+"\nadd2:"+add2);
System.out.println(set.toString());
}
输出结果:
add1:true
add2:true
[Person [name=钟梅, age=25], Person [name=钟梅, age=25]]
由此可见:添加相同元素是指同一对象引用被多次添加会操作失败,而非相同元素值的多个对象被添加。
3:List集合
3.1:简介
List代表是一个元素有序、可重复的集合,集合中每个元素都有对应的顺序索引。List集合允许使用重复的元素,可以通过索引来访问指定位置的元素。List集合默认按元素的添加顺序来设置元素的索引,例如第一次添加的元素索引为0,第二次添加的元素索引为0,依次类推下去。
3.2:接口中定义的方法
List作为Collection的子接口,同样可以调用Collection的全部方法,List集合具有有序特点,同时List集合还有一些额外的方法:
void add(int index, Object element): 在列表的指定位置插入指定元素(可选操作)。
boolean addAll(int index, Collection<? extends E> c) : 将集合c 中的所有元素都插入到列表中的指定位置index处。
Object get(index): 返回列表中指定位置的元素。
int indexOf(Object o): 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
int lastIndexOf(Object o): 返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
Object remove(int index): 移除列表中指定位置的元素。
Object set(int index, Object element): 用指定元素替换列表中指定位置的元素。
List subList(int fromIndex, int toIndex): 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的所有集合元素组成的子集。
Object[] toArray(): 返回按适当顺序包含列表中的所有元素的数组(从第一个元素到最后一个元素)。
除此之外,Java 8还为List接口添加了如下两个默认方法。
void replaceAll(UnaryOperator operator): 根据operator指定的计算规则重新设置List集合的所有元素。
void sort(Comparator c): 根据Comparator参数对List集合的元素排序。
下面是List集合的一些常规用法:
public class ListTest {
public static void main(String[] args) {
testList();
}
static void testList(){
//创建List集合,初始化数据
List<Person> list = new ArrayList<Person>();
list.add(new Person("马云", 88));
list.add(new Person("马化腾", 44));
list.add(new Person("任正非", 99));
System.out.println(list.toString());
//在第二位置插入新数据
Person p1 = new Person("李彦宏", 66);
list.add(1, p1);
//普通变量集合for
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//删除第二个元素
list.remove(2);
System.out.println(list.toString());
//获取指定元素在集合中的位置
Person p2 = new Person("李彦宏", 66);
System.out.println(list.indexOf(p2));
//替换指定索引的元素
list.set(1,new Person("刘强东", 77));
System.out.println(list.toString());
//截取指定位置区域的元素成为子集合(注意是位置,不是索引值)
System.out.println(list.subList(1, 2));
}
}
输出结果:
[Person [name=马云, age=88], Person [name=马化腾, age=44], Person [name=任正非, age=99]]
Person [name=马云, age=88]
Person [name=李彦宏, age=66]
Person [name=马化腾, age=44]
Person [name=任正非, age=99]
[Person [name=马云, age=88], Person [name=李彦宏, age=66], Person [name=任正非, age=99]]
-1
[Person [name=马云, age=88], Person [name=刘强东, age=77], Person [name=任正非, age=99]]
[Person [name=刘强东, age=77]]
从结果可以当执行System.out.println(list.indexOf(p2))并没有返回指定的对象的索引值,返回是-1,说明集合不存在该元素,而控制台打印却有这个元素,是什么问题呢???
其实控制器打印出来的元素值并不是p2对象的,而是p1的,这两个对象并不是同一个对象,判断两个对象是否相等只要通过equals()方法比较就可以得知,相等则返回true。注意必须在实体类中重写equals()方法
boolean equals = p1.equals(p2);
System.out.println(equals);
//实体类
static class Person {
……
@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 != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
//执行结果:注:在没有重写实体类的equals方法,控制台输出结果是false
true
此时再次执行main方法,System.out.println(list.indexOf(p2))返回的值是1了。
4:Queue集合
4.1:简介
Queue用于模拟队列这种数据结构,队列通常是指“先进先出”(FIFO)的容器。队列的头部保存在队列中,存放时间最长的元素,队列的尾部保存在队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素。通常,队列不允许随机访问队列中的元素。
4.2:接口中定义的方法
boolean add(E e)
将指定的元素插入到此队列中,如果可以立即执行此操作,而不会违反容量限制, true在成功后返回 IllegalStateException如果当前没有可用空间,则抛出IllegalStateException。
E element()
检索,但不删除,这个队列的头。
boolean offer(E e)
如果在不违反容量限制的情况下立即执行,则将指定的元素插入到此队列中。
E peek()
检索但不删除此队列的头,如果此队列为空,则返回 null 。
E poll()
检索并删除此队列的头,如果此队列为空,则返回 null 。
E remove()
检索并删除此队列的头。
二、Map集合
1.1:简介
Map用户保存具有映射关系的数据,因此Map集合里保存着两组数,一组值用户保存Map里的key,另一组值用户保存Map里的value,key和value都可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false。
如下图所描述,key和value之间存在单向一对一关系,即通过指定的key,总能找到唯一的、确定的value。从Map中取出数据时,只要给出指定的key,就可以取出对应的value。
1.2:Map集合与Set集合、List集合的关系
与Set集合的关系
如果 把Map里的所有key放在一起看,它们就组成了一个Set集合(所有的key没有顺序,key与key之间不能重复),实际上Map确实包含了一个keySet()方法,用户返回Map里所有key组成的Set集合。
与List集合的关系
如果把Map里的所有value放在一起来看,它们又非常类似于一个List:元素与元素之间可以重复,每个元素可以根据索引来查找,只是Map中索引不再使用整数值,而是以另外一个对象作为索引。
1.3:常用接口定义的方法
void clear()
从该地图中删除所有的映射(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射,则返回 true 。
boolean containsValue(Object value)
如果此地图将一个或多个键映射到指定的值,则返回 true 。
Set<Map.Entry<K,V>> entrySet()
返回此地图中包含的映射的Set视图。
V get(Object key)
返回到指定键所映射的值,或 null如果此映射包含该键的映射。
boolean isEmpty()
如果此地图不包含键值映射,则返回 true 。
Set<K> keySet()
返回此地图中包含的键的Set视图。
V put(K key, V value)
将指定的值与该映射中的指定键相关联(可选操作)。
void putAll(Map<? extends K,? extends V> m)
将指定地图的所有映射复制到此映射(可选操作)。
V remove(Object key)
如果存在(从可选的操作),从该地图中删除一个键的映射。
default boolean remove(Object key, Object value)
仅当指定的密钥当前映射到指定的值时删除该条目。
default V replace(K key, V value)
只有当目标映射到某个值时,才能替换指定键的条目。
default boolean replace(K key, V oldValue, V newValue)
仅当当前映射到指定的值时,才能替换指定键的条目。
int size()
返回此地图中键值映射的数量。
Collection<V> values()
返回此地图中包含的值的Collection视图。
Map中还包括一个内部类Entry,该类封装了一个key-value对。Entry包含如下三个方法:
K getKey()
返回与此条目相对应的键。
V getValue()
返回与此条目相对应的值。
V setValue(V value)
用指定的值替换与该条目相对应的值(可选操作)。
下面是Map集合一些常规操作:
public class MapTest {
public static void main(String[] args) {
testMap();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
static void testMap(){
Map map = new HashMap();
//以key-value对放入map中
map.put("马云", 100);
map.put("马化腾", 250);
map.put("李彦宏", 88);
//value值是可以重复
map.put("任正非", 100);
//如果key重复了,value返回值被覆盖掉的value,也就是还是之前的value
System.out.println("马化腾:"+map.put("马化腾", 100));
//判断集合中是否指定的key和value
System.out.println("key中是否有\"马云\" :"+map.containsKey("马云"));
System.out.println("value中是否有\"340\" :"+map.containsValue(340));
//遍历map
for (Object key:map.keySet()) {
System.out.println("key:"+key+"--value:"+map.get(key));
}
//移除指定的的key-value元素
map.remove("马化腾");
System.out.println(map.toString());
}
输出结果:
马化腾:250
key中是否有"马云" :true
value中是否有"340" :false
key:任正非--value:100
key:马云--value:100
key:李彦宏--value:88
key:马化腾--value:100
/**
* 遍历Map的方法
*/
static void traverseMap() {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
//第一种:常规 通过Map.keySet遍历key和value
for (String key:map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
System.out.println("----------");
//第二种:通过key值的集合Set,再通过迭代器遍历;
Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, String> entry = iterator.next();
System.out.println("key= "+ entry.getKey() + " and value= " + entry.getValue());
}
System.out.println("----------");
//第三种
Set<Entry<String, String>> entrySet = map.entrySet();
for (Map.Entry<String, String> entry:entrySet) {
System.out.println("key= "+ entry.getKey() + " and value= " + entry.getValue());
}
System.out.println("----------");
//第四种:通过Map.values()遍历所有的value,但不能遍历key
for (String value:map.values()) {
System.out.println("value= " + value);
}
}