Java Collection
集合
普通的对象数组的最大问题在于数组中的元素个数是固定的,不能动态的扩充大小,所以最 早的时候可以通过链表实现一个动态对象数组。但是这样做毕竟太复杂了,所以在 Java 中为了方便用户操作各个数据结构, 所以引入了类集的概念,有时候就可以把类集称为 java 对数据结构的实现。
在整个类集中的,这个概念是从 JDK1.2(Java 2)之后才正式引入的,最早也提供了很多的操作类,但是并没有完 整的提出类集的完整概念。 类集中最大的几个操作接口:Collection、Map、Iterator,这三个接口为以后要使用的最重点的接口。
所有的类集操作的接口或类都在 java.util 包中。
1. Collection接口
Collection 接口是在整个 Java 类集中保存单值的最大操作父接口,里面每次操作的时候都只能保存一个对象的数据。 此接口定义在 java.util 包中。
此接口定义如下:
public interface Collection<E> extends Iterable<E>
此接口的常用方法如下所示:
但是,在开发中不会直接使用 Collection 接口。而使用其操作的两个子接口:List、Set。
1.1 List 接口
在整个集合中 List 是 Collection 的子接口,里面的所有内容都是允许重复的。
List 子接口的定义:
public interface List<E> extends Collection<E>
此接口上依然使用了泛型技术。此接口对于 Collection 接口来讲有如下的扩充方法:
了解了 List 接口之后,那么该如何使用该接口呢?需要找到此接口的实现类,常用的实现类有如下几个: · ArrayList(95%)、Vector(4%)、LinkedList(1%)
1.1.1 ArrayList
ArrayList 是 List 接口的子类,此类的定义如下:
public class ArrayList extendsAbstractList implementsList , RandomAccess, Cloneable, Serializable
此类继承了 AbstractList 类。AbstractList 是 List 接口的子类。AbstractList 是个抽象类,适配器设计模式。
例子ArrayList增加和取得元素
package org.listdemo.arraylistdemo;
import java.util.ArrayList;
import java.util.List;
public class ArrayListDemo01 {
public static void main(String[] args) {
List<String> all = new ArrayList<String>(); // 实例化List对象,并指定泛型类型
all.add("hello "); // 增加内容,此方法从Collection接口继承而来
all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
all.add("world"); // 增加内容,此方法从Collection接口继承而来
System.out.println(all); // 打印all对象调用toString()方法
}
}
以上的操作向集合中增加了三个元素,其中在指定位置增加的操作是 List 接口单独定义的。随后进行输出的时候, 实际上调用的是 toString()方法完成输出的。 可以发现,此时的对象数组并没有长度的限制,长度可以任意长,只要是内存够大就行。
例:删除元素,并且使用循环的方式输出。
package org.listdemo.arraylistdemo;
import java.util.ArrayList;
import java.util.List;
public class ArrayListDemo02 {
public static void main(String[] args) {
List<String> all = new ArrayList<String>(); // 实例化List对象,并指定泛型类型
all.add("hello "); // 增加内容,此方法从Collection接口继承而来
all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
all.add("world"); // 增加内容,此方法从Collection接口继承而来
all.remove(1); // 根据索引删除内容,此方法是List接口单独定义的
all.remove("world");// 删除指定的对象
System.out.print("集合中的内容是:");
for (int x = 0; x < all.size(); x++) { // size()方法从Collection接口继承而来
System.out.print(all.get(x) + "、"); // 此方法是List接口单独定义的
}
}
}
1.1.2 Vector
Vector类实现了可增长的对象数组。 像数组一样,它包含可以使用整数索引访问的组件。 但是, Vector的大小可以根据需要增大或缩小,以便在创建Vector后添加和删除元素。
从Java 2平台v1.2开始,该类被改进以实现List接口,使其成为Java Collections Framework的成员。 与新的集合实现不同, Vector是同步的。 如果不需要线程安全实现,建议使用ArrayList代替Vector 。
与 ArrayList 一样,Vector 本身也属于 List 接口的子类,此类的定义如下:
public class Vector extends AbstractList implementsList,RandomAccess,Cloneable,Serializable
此类与 ArrayList 类一样,都是 AbstractList 的子类。所以,此时的操作只要是 List 接口的子类就都按照 List 进行操作。
package org.listdemo.vectordemo;
import java.util.List;
import java.util.Vector;
public class VectorDemo01 {
public static void main(String[] args) {
List<String> all = new Vector<String>(); // 实例化List对象,并指定泛型类型
all.add("hello "); // 增加内容,此方法从Collection接口继承而来
all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
all.add("world"); // 增加内容,此方法从Collection接口继承而来
all.remove(1); // 根据索引删除内容,此方法是List接口单独定义的
all.remove("world");// 删除指定的对象
System.out.print("集合中的内容是:");
for (int x = 0; x < all.size(); x++) { // size()方法从Collection接口继承而来
System.out.print(all.get(x) + "、"); // 此方法是List接口单独定义的
}
}
}
1.1.3 Vector和ArrayList的区别
同步处理和异步处理会在多线程中涉及到。
1.1.4 LinkedList
集合数据存储的结构是链表结构。方便元素添加、删除的集合。
实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方 法。这些方法我们作为了解即可:
例:验证 LinkedList 子类
import java.util.LinkedList;
import java.util.Queue;
public class TestDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
queue.add("A");
queue.add("B");
queue.add("C");
int len=queue.size();//把queue的大小先取出来,否则每循环一次,移除一个元素,就少 一个元素,那么queue.size()在变小,就不能循环queue.size()次了。
for (int x = 0; x <len; x++) {
System.out.println(queue.poll());
}
System.out.println(queue);
}
}
1.2 Set接口
Set 接口也是 Collection 的子接口,与 List 接口最大的不同在于, Set 接口中元素无序并且接口里面的内容是不允许重复的。 它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Collection 接口更加严格了。
1.2.1 散列存放HashSet
HashSet 是 Set 接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的 (即存取顺序不一致)。
HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证 元素唯一性的方式依赖于: hashCode 与 equals 方法。
例:
public class HashSetDemo {
public static void main(String[] args) {
//创建 Set集合
HashSet<String> set = new HashSet<String>(); // 实例化Set接口对象
//添加元素
set.add(new String("123"));
set.add("123");
set.add("123");
set.add("321");
// 用迭代器或者forEach遍历
for (String name : set) {
System.out.println(name);
}
// 或者将Set转化为数组进行遍历
String[] str = set.toArray();
for (int x = 0; x < obj.length; x++) {
System.out.print(obj[x] + "、");
}
}
}
输出结果如下,说明SET集合中不能存储重复元素:
123
321
1.2.2 排序子类TreeSet
与 HashSet 不同的是,TreeSet 本身属于排序的子类,此类的定义如下:
public class TreeSet extends AbstractSet implements NavigableSet, Cloneable, Serializable
例:展现排序
public class Demo1 {
public static void main(String[] args) {
TreeSet<String> data = new TreeSet<>();
data.add("B");
data.add("A");
data.add("C");
data.add("D");
for (String s: data) {
System.out.println(s);
}
}
}
输出结果是:
A
B
C
D
但是如果对自定义类排序会怎么样呢?
例:定义一个Person类
public class Demo1 {
public static void main(String[] args) {
TreeSet<Person> data = new TreeSet<>();
Person p1 = new Person("张三", 18);
Person p2 = new Person("李四", 19);
data.add(p1);
data.add(p2);
for (Person p : data) {
System.out.println(p);
}
}
static class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
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 +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
}
执行以上的操作代码之后,发现出现了如下的错误提示:
Exception in thread "main" java.lang.ClassCastException: class Java集合.Demo1$Person cannot be cast to class java.lang.Comparable (Java集合.Demo1$Person is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
at java.base/java.util.TreeMap.compare(TreeMap.java:1291)
at java.base/java.util.TreeMap.put(TreeMap.java:536)
at java.base/java.util.TreeSet.add(TreeSet.java:255)
at Java集合.Demo1.main(Demo1.java:19)
此时的提示是:Person类不能向 Comparable 接口转型的问题。通俗来说就是Java不知道怎么给Person进行排序。
所以,证明,如果现在要是想进行排序的话,则必须在 Person类中实现 Comparable 接口。
public class Demo1 {
public static void main(String[] args) {
TreeSet<Person> data = new TreeSet<>();
Person p1 = new Person("张三", 18);
Person p2 = new Person("李四", 19);
Person p3 = new Person("王二麻子", 19);
data.add(p1);
data.add(p2);
data.add(p3);
for (Person p : data) {
System.out.println(p);
}
}
static class Person implements Comparable<Person>{
String name;
int age;
@Override
public int compareTo(Person o) {
// 拿this与o比较
// 返回数据是:负数this小 / 0一样大 / 正数this大
if (this.age > o.age) {
return 1;
} else if (this.age == o.age) {
return 0; // 如果一样大说明有重复对象, 不存这个对象
}
return -1;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
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 +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
}
Person{name='张三', age=18}
Person{name='李四', age=19}
从以上的结果中可以发现,李四没有了。因为李四的年龄和张三的年龄是一样的,所以会被认为是同一个对象, 所以无法继续加入。
2. Map
Collection 中,每次操作的都是一个对象(单值存储),如果现在假设要操作一对对象(双值存储),则就必须使用 Map 了。
Map 接口。里面的所有内容都按照 key(不可重复) -> value 的形式保存,也称为二元偶对象。
此接口定义如下:
public interface Map<K,V>
此接口与 Collection 接口没有任何的关系,是第二大的集合操作接口。此接口常用方法如下:
Map 本身是一个接口,所以一般会使用以下的几个子类:HashMap、TreeMap、Hashtable
2.1 Map子类HashMap
HashMap 是 Map 的子类,此类的定义如下:
此类继承了 AbstractMap 类,同时可以被克隆,可以被序列化下来。
例:向集合中增加内容
package org.listdemo.hashmapdemo;
import java.util.HashMap;
import java.util.Map;
public class HashMapDemo01 {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "张三A");
map.put(1, "张三B"); // 新的内容替换掉旧的内容
map.put(2, "李四");
map.put(3, "王五");
String val = map.get(6);
System.out.println(val);
}
}
以上的操作是 Map 接口在开发中最基本的操作过程,根据指定的 key 找到内容,如果没有找到,则返回 null,找到 了则返回具体的内容。
例: 遍历输出所有的key和value
package org.listdemo.hashmapdemo;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class HashMapDemo03 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("ZS", "张三");
map.put("LS", "李四");
map.put("WW", "王五");
map.put("ZL", "赵六");
map.put("SQ", "孙七");
Set<String> set = map.keySet(); // 得到全部的key
for (String key:set) {
System.out.println(key + " --:> " + map.get(key));
}
}
}