java基础之List和Set集合详细介绍
java基础之List和Set集合详细介绍
- 一、集合
- 1、集合介绍
- 2、Java中的集合分类
- 3、Collection接口
- 4、迭代器
- 5、迭代器使用细节
- 5.1、获取元素异常
- 5.2、一次判断只调用一次next方法
- 5.3、遍历的时候不能使用集合自身的增删方法
- 二、List集合
- 1、List接口介绍
- 2、ListIterator接口
- 3、ArrayList类
- 3.1、ArrayList介绍
- 3.2、ArrayList演示
- 3.3、ArrayList底层结构
- 4、LinkedList类
- 4.1、LinkedList介绍
- 4.2、演示LinkedList
- 4.3、LinkedList集合底层结构
- 5、Vector类
- 三、Set集合
- 1、Set接口介绍
- 2、HashSet集合
- 2.1、HashSet集合
- 2.2、演示HashSet
- 2.3、给集合中存储自定义对象
- 2.4、哈希表结构
- 3、TreeSet集合
- 3.1、TreeSet介绍
- 3.2、TreeSet演示
- 3.3、TreeSet存放自定义对象
一、集合
1、集合介绍
基本类型变量:一个存储空间,可以存放常量。
引用变量:存放某个对象的引用地址。
数组:存放一组数据,这组数据的个数固定的,数据的类型也是固定。
字符串缓冲区:可以存放任意类型数据,最终都会调用toString将其转成字符串处理。
集合:它本质上也容器,主要用来存储对象(对象的引用),并且随着对象个数的增减,集合的容量会自动进行匹配。
2、Java中的集合分类
集合分成两大类:
单列集合:存放单一对象。
Collection:它是单列集合的顶层接口,定义了集合的最基本的操作方法。
List接口:存放可以重复的数据,并且数据有下标
Set接口:存放不重复数据
双列集合:存放的一组数据(key=value)。
Map接口:定义双列集合的基本操作方法。
Java中提供大量的针对不同集合接口的实现类,每个实现类其实主要是因为底层的数据结构(存储方式)不同。
开发中使用频率最高的三个技术:
ArrayList:
HashSet:
HashMap:
由于Java提供的集合太多,因此Java给集合提供统一的遍历方式:迭代器Iterator。
3、Collection接口
所有的集合都在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);}
}
4、迭代器
迭代:遍历。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);}}
}
5、迭代器使用细节
5.1、获取元素异常
如果在使用迭代器操作集合的时候,没有进行hasNext的判断,直接调用next取数据,那么可能会出现迭代器已经运行到集合的最后一个元素的后面,导致没有可以被取出的元素,就发生下面的异常

5.2、一次判断只调用一次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());}}
}
5.3、遍历的时候不能使用集合自身的增删方法
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集合
1、List接口介绍

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() );}}
}
2、ListIterator接口
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());}}
}
3、ArrayList类
3.1、ArrayList介绍

ArrayList类:它是真正意义上的集合类,它的底层数据结构是可变数组。

3.2、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());}}
}
3.3、ArrayList底层结构
ArrayList底层可变数组:数组一旦定义好,长度不可变。

ArrayList:增删相对较慢,查询较快。
4、LinkedList类
4.1、LinkedList介绍

List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
LinkedList集合(类):它是List接口的实现类,List接口中的所有方法在LinkedList中全部都有。然后由于LinkedList集合的底层使用的链表结构,因此在其中添加了根据头和尾对集合进行添加、删除和获取的方法。
4.2、演示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());}}
}
4.3、LinkedList集合底层结构
链表数据结构:
- 节点:保存当前数据和前后关系的整体数据
- 数据域Data:
- 指针域next:
- 头节点:head,保存的后面那个节点的地址
- 尾节点:指针域中存放的null

LinkedList集合:增删快,查询慢。
堆栈:数据先进后出(FILO),后进先出(LIFO)(JVM将内存划分的时候栈内存运行方法的时候采用这种结构)。
队列:先进先出(FIFO),后进后出(LILO)
5、Vector类
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集合
1、Set接口介绍

Set接口它是Collection接口下的一个子接口。Set接口下的所有实现类(集合)保存的元素都不能重复。
Set接口没有自己特有的方法,所有的方法全部来自于Collection接口。
2、HashSet集合
2.1、HashSet集合

此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
HashSet底层是哈希表数据结构,存放在HashSet集合中的元素,不保证存取的顺序。
2.2、演示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");// 遍历:只能使用Iteratorfor( Iterator it = set.iterator() ; it.hasNext() ; ) {System.out.println(it.next());}}
}
2.3、给集合中存储自定义对象
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;}@Overridepublic 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可以去重,但是最终的结果没有。
为什么存放自定义类的对象没有去重呢?是因为底层的哈希表的原因。
2.4、哈希表结构
当需要给哈希表结构中存放数据的时候,需要先根据数据的自身的一些特点通过哈希算法计算元素在哈希表中的存储位置。
假设需要将"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;@Overridepublic 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;}@Overridepublic 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;}@Overridepublic String toString() {return "Student [name=" + name + ", age=" + age + ", sex=" + sex + "]";}
}
3、TreeSet集合
3.1、TreeSet介绍

基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
TreeSet集合底层使用的二叉树结构(红黑树),给TreeSet集合中存放的元素可以排序。不能存放重复元素。
3.2、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() );}}
}
3.3、TreeSet存放自定义对象
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;@Overridepublic 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;}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
