基本类型变量:一个存储空间,可以存放常量。
引用变量:存放某个对象的引用地址。
数组:存放一组数据,这组数据的个数固定的,数据的类型也是固定。
字符串缓冲区:可以存放任意类型数据,最终都会调用toString将其转成字符串处理。
集合:它本质上也容器,主要用来存储对象(对象的引用),并且随着对象个数的增减,集合的容量会自动进行匹配。
集合分成两大类:
单列集合:存放单一对象。
Collection:它是单列集合的顶层接口,定义了集合的最基本的操作方法。
List接口:存放可以重复的数据,并且数据有下标
Set接口:存放不重复数据
双列集合:存放的一组数据(key=value)。
Map接口:定义双列集合的基本操作方法。
Java中提供大量的针对不同集合接口的实现类,每个实现类其实主要是因为底层的数据结构(存储方式)不同。
开发中使用频率最高的三个技术:
ArrayList:
HashSet:
HashMap:
由于Java提供的集合太多,因此Java给集合提供统一的遍历方式:迭代器Iterator。
所有的集合都在java.util包下。
Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set
和 List
)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
学习集合:添加、修改、删除、查询、遍历和判断,每个集合自身的的存储结构。
public class CollectionDemo {
public static void main(String[] args) {
// 创建集合对象(多态)
Collection coll = new ArrayList();
// 添加元素
coll.add("abc");
coll.add("xyz");
coll.add("abc");
// 打印集合的引用
System.out.println(coll);
// 删除元素
coll.remove("abcd");
System.out.println(coll);
// 元素个数
int size = coll.size();
System.out.println(size);
// 清空
coll.clear();
System.out.println(coll);
}
}
迭代:遍历。Java针对众多的集合,给出一种统一的遍历方式。
获取迭代器的方式:针对单列集合,直接通过集合对象的iterator() 方法,就可以得到用于遍历当前集合的迭代器。
迭代器一旦将集合遍历完,迭代器中的隐式的光标(指针)就被移动到集合最后一个元素的后面,无法再使用当前这个Iterator对集合进行遍历,如果还需要遍历,就需要重新对集合调用iterator方法,获取新的迭代器对象。
public class IteratorDemo {
public static void main(String[] args) {
// 创建集合对象
Collection coll = new LinkedList();
// 添加元素
coll.add("aaa");
coll.add("bbb");
coll.add("ddd");
coll.add("aaa");
coll.add("ffff");
coll.add("ffff");
System.out.println(coll);
// 遍历结合
Iterator it = coll.iterator();
while( it.hasNext() ) {
Object obj = it.next();
System.out.println(obj);
}
System.out.println("------------------------");
for( Iterator itr = coll.iterator() ; itr.hasNext() ; ) {
Object obj = itr.next();
System.out.println(obj);
}
System.out.println("------------------------");
for (Iterator itr2 = coll.iterator(); itr2.hasNext();) {
Object object = (Object) itr2.next();
System.out.println(object);
}
}
}
如果在使用迭代器操作集合的时候,没有进行hasNext的判断,直接调用next取数据,那么可能会出现迭代器已经运行到集合的最后一个元素的后面,导致没有可以被取出的元素,就发生下面的异常
public class IteratorDemo2 {
public static void main(String[] args) {
// 创建集合对象
Collection coll = new LinkedList();
// 添加元素
coll.add("aaa");
coll.add("bbb");
coll.add("ddd");
coll.add("aaa");
coll.add("ffff");
coll.add("ffff");
coll.add("ffff");
System.out.println(coll);
// 获取迭代器
for( Iterator it = coll.iterator() ; it.hasNext() ; ) {
Object obj = it.next();
System.out.println(it.next());
}
}
}
public class IteratorDemo3 {
public static void main(String[] args) {
// 创建集合对象
Collection coll = new LinkedList();
// 添加元素
coll.add("ddd");
coll.add("aaa");
coll.add("bbb");
coll.add("aaa");
System.out.println(coll);
// 获取迭代器
for( Iterator it = coll.iterator() ; it.hasNext() ; ) {
Object obj = it.next();
if( obj.equals("ddd") ) {
coll.remove(obj);
}
}
System.out.println(coll);
}
}
在使用迭代器对集合进行遍历的时候,不能使用集合本身的添加、删除对进行操作。
添加和删除集合数据会影响集合中原始的数据的存储结构(位置)。
如果在迭代的过程中需要对集合的元素进行删除操作,需要使用迭代器自身的remove方法。
List接口下描述的所有的集合都是有序的,可以保证存取的顺序,同时还可以存放重复的数据。
List接口中除了提供Collection接口中的所有方法之外,还围绕下标(index)而设计了根据下标对集合中的元素进行CRUD操作的方法。
public class ListDemo {
public static void main(String[] args) {
// 演示List接口
List list = new ArrayList();
// 添加元素
list.add("aaa");
list.add("aaa");
list.add("bbb");
// 给指定的位置上添加元素
list.add(3, "罗本珑");
System.out.println(list);
// 修改指定位置上的元素
list.set(1, "张帅");
System.out.println(list);
// 给集合中存储基本类型数据需要先自动装箱
list.add(3);
list.add(5);
list.add(2);
System.out.println(list);
// 删除
list.remove("bbb");
System.out.println(list);
/*
* 由于List接口中有2个remove
* remove(int index)
* remove(Object obj)
* 如果在调用list接口中的remove方法的时候,传递的是一个int类型的数字
* 这时默认会将数字当前下标去使用,调用remove(int index)方法
* 如果传递的是对象,调用remove(Object obj)方法
*/
list.remove(Integer.valueOf(2));
System.out.println(list);
// 获取方法
Object obj = list.get(2);
System.out.println(obj);
System.out.println("=========================");
// List接口下的集合可以通过下标进行遍历
for( int i = 0 ; i < list.size() ; i++ ) {
Object o = list.get(i);
System.out.println(o);
}
System.out.println("=========================");
for( Iterator it = list.iterator() ; it.hasNext() ; ) {
System.out.println( it.next() );
}
}
}
ListIterator:它是List接口特有的迭代器,主要它对集合进行正向或逆向的遍历。同时在遍历的过程中可以使用其中的方法对集合进行CRUD操作。
public class ListIteratorDemo {
public static void main(String[] args) {
// 演示List接口
List list = new ArrayList();
// 添加元素
list.add("吴山");
list.add("江彬");
list.add("江彬");
list.add("程娇");
list.add("李小杰");
System.out.println("=======使用Iterator遍历=======");
for( Iterator it = list.iterator() ; it.hasNext() ; ) {
System.out.println(it.next());
}
System.out.println("=======使用普通的for遍历=======");
for( int i = 0 ; i < list.size() ; i++ ) {
System.out.println(list.get(i));
}
System.out.println("=======使用ListIterator遍历=======");
// 获取List接口特有的迭代器的时候,可以指定迭代器起始遍历的位置
ListIterator lit = list.listIterator(2);
System.out.println( lit.hasPrevious() );
System.out.println( lit.hasNext() );
while( lit.hasNext() ) {
System.out.println(lit.next());
}
System.out.println( "迭代器光标在最后:" + lit.hasNext() );
while( lit.hasPrevious() ) {
System.out.println(lit.previous());
}
}
}
ArrayList类:它是真正意义上的集合类,它的底层数据结构是可变数组。
public class ArrayListDemo {
public static void main(String[] args) {
// 创建集合对象
ArrayList list = new ArrayList();
// 多态
// List list2 = new ArrayList();
list.add("陈烨文");
list.add("陈烨文");
list.add("饶昇");
list.add("杨阳");
list.add("万明");
for (Iterator it = list.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
}
ArrayList底层可变数组:数组一旦定义好,长度不可变。
ArrayList:增删相对较慢,查询较快。
List
接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null
)。除了实现 List
接口外,LinkedList
类还为在列表的开头及结尾 get
、remove
和 insert
元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
LinkedList集合(类):它是List接口的实现类,List接口中的所有方法在LinkedList中全部都有。然后由于LinkedList集合的底层使用的链表结构,因此在其中添加了根据头和尾对集合进行添加、删除和获取的方法。
public class LinkedListDemo {
public static void main(String[] args) {
// 创建集合对象
LinkedList list = new LinkedList();
// 添加元素
list.addLast("李美云");
list.add("郭耀元");
list.add("缪熙");
list.add("缪熙");
list.add("许文杰");
list.add("许文杰");
list.add("蔡炜斌");
// 使用LinkedList集合特有的添加方法
list.addFirst("吴丽青");
System.out.println(list);
// 获取数据
Object first = list.getFirst();
System.out.println(first);
// 删除头部的数据
Object remove = list.removeFirst();
System.out.println(remove);
System.out.println(list);
// 遍历
for( Iterator it = list.iterator() ; it.hasNext() ; ) {
System.out.println(it.next());
}
}
}
链表数据结构:
LinkedList集合:增删快,查询慢。
堆栈:数据先进后出(FILO),后进先出(LIFO)(JVM将内存划分的时候栈内存运行方法的时候采用这种结构)。
队列:先进先出(FIFO),后进后出(LILO)
Vector类是在JDK1.0时期就存在一个类似于ArrayList的一个集合类。
Vector的底层也是可变数组。在JDK1.2之后被集合体系收编。不过后期在使用中已经被ArrayList代替。·
public class VectorDemo {
public static void main(String[] args) {
// 创建Vector集合对象
Vector v = new Vector();
v.addElement("aaa");
v.addElement("aaa");
v.addElement("bbb");
v.addElement("aaa");
v.addElement("ddd");
// 删除
v.removeElement("ccc");
// 获取
Object e = v.elementAt(1);
System.out.println(e);
/*
* 遍历
* Enumeration:它是一个接口,功能类似于Iterator,
* 由于Enumeration方法名太长,开发中建议使用Iterator遍历集合
*/
Enumeration en = v.elements();
while( en.hasMoreElements() ) {
System.out.println(en.nextElement());
}
}
}
Set接口它是Collection接口下的一个子接口。Set接口下的所有实现类(集合)保存的元素都不能重复。
Set接口没有自己特有的方法,所有的方法全部来自于Collection接口。
此类实现 Set
接口,由哈希表(实际上是一个 HashMap
实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null
元素。
HashSet底层是哈希表数据结构,存放在HashSet集合中的元素,不保证存取的顺序。
public class HashSetDemo {
public static void main(String[] args) {
// 创建集合对象
HashSet set = new HashSet();
// 添加元素
set.add("aaa");
set.add("bbb");
set.add("ddd");
set.add("eee");
set.add("bbb");
set.add("aaa");
// 遍历:只能使用Iterator
for( Iterator it = set.iterator() ; it.hasNext() ; ) {
System.out.println(it.next());
}
}
}
public class Student {
private String name;
private int age;
private String sex;
public Student(String name, int age, String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
public Student() {
super();
}
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;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
}
public class HashSetDemo2 {
public static void main(String[] args) {
HashSet set = new HashSet();
set.add( new Student("董良伟",18,"女") );
set.add( new Student("况任动",17,"女") );
set.add( new Student("陈汉华",16,"女") );
set.add( new Student("陈汉华",16,"女") );
set.add( new Student("王惟华",18,"男") );
set.add( new Student("王惟华",18,"男") );
set.add( new Student("董良伟",18,"女") );
// 遍历
for( Iterator it = set.iterator() ; it.hasNext() ; ) {
System.out.println( it.next() );
}
}
}
给HashSet集合中存放了重复的自定义对象,但是希望HashSet可以去重,但是最终的结果没有。
为什么存放自定义类的对象没有去重呢?是因为底层的哈希表的原因。
当需要给哈希表结构中存放数据的时候,需要先根据数据的自身的一些特点通过哈希算法计算元素在哈希表中的存储位置。
假设需要将"ab" ,存放的哈希表中,这时先根据“ab”数据调用它的哈希算法,计算位置。
如果位置上没有元素,就直接报错。
如果某个元素进行哈希算法之后,算出的位置上已经存放了元素,这时称为哈希冲突,这时会调用equals方法判断两个对象是否是同一个,如果是,当前正要存放的对象被舍弃;如果不是,在当前的位置上通过散列划分出空间存放元素。
上面给HashSet中存放自定义的Student对象,调用每个Student对象的哈希算法计算存储位置。在Object类中hashCode的方法,hashCode方法是根据对象的内存地址计算哈希值的。如果hashCode相同,再调用equals进行比较。
结论:
HashSet保证对象唯一依赖的是存储的对象的hashCode和equals方法。因此自定义的类一定要复写hashCode和equals方法。
public class Student /* extends Object */{
private String name;
private int age;
private String sex;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((sex == null) ? 0 : sex.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (sex == null) {
if (other.sex != null)
return false;
} else if (!sex.equals(other.sex))
return false;
return true;
}
public Student(String name, int age, String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
public Student() {
super();
}
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;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
}
基于 TreeMap
的 NavigableSet
实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator
进行排序,具体取决于使用的构造方法。
TreeSet集合底层使用的二叉树结构(红黑树),给TreeSet集合中存放的元素可以排序。不能存放重复元素。
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
TreeSet set = new TreeSet();
// 存放元素
set.add("ccc");
set.add("aaa");
set.add("aaa");
set.add("abc");
set.add("abc");
set.add("aab");
set.add("cdd");
set.add("cda");
set.add("cda");
set.add("ddd");
// 遍历
for( Iterator it = set.iterator() ; it.hasNext() ; ) {
System.out.println( it.next() );
}
}
}
public class TreeSetDemo2 {
public static void main(String[] args) {
// 创建集合对象
TreeSet set = new TreeSet();
set.add( new Student("董良伟",18,"女") );
// 遍历
for( Iterator it = set.iterator() ; it.hasNext() ; ) {
System.out.println( it.next() );
}
}
}
给TreeSet中存放自定义对象,结果运行发生ClassCastException异常,异常提示Student类无法被转成Comparable类型。
因为:在Java规定一个类的对象如果需要进行自然顺序的比较,这个类首先必须实现Comparable接口,否则无法进行比较。
public class Student implements Comparable<Student>{
private String name;
private int age;
private String sex;
@Override
public int compareTo(Student o) {
// 比较姓名
int cmp = this.name.compareTo(o.name);
/*
// 说明姓名相同
if( cmp == 0 ) {
int sexCmp = this.sex.compareTo(o.sex);
if( sexCmp == 0 ) {
return this.age - o.age;
}else {
return sexCmp;
}
}else {
return cmp;
}
*/
cmp = cmp == 0 ? this.sex.compareTo(o.sex) : cmp;
return cmp == 0 ? this.age - o.age : cmp;
}
}