>>> java门梨 凶_java集合(Collection接口下的 List、Set 深入理解)

什么是java集合?

1、java集合大致分为Set、List、Queue、Map四种体系。

List代表有序、可重复的集合;(有序指存储顺序和取出顺序一致)

Set表示无序、不可重复(元素唯一)的集合;(无序指存储顺序和取出顺序不一致)

Map代表具有映射关系的集合;

Queue代表一种队列集合。

2、java集合就像一个容器,可以把多个对象(实际上是对象的引用,习惯都称之为对象)“丢进”该容器中。

3、java集合可以记住容器中对象的数据类型,从而编写出更简洁、简装的代码。

为什么要使用java集合?

1、在编程时,常常需要集中存放多个数据。可以使用数组来存放多个对象,但是数组的长度是不可变化的。在一开始定义了数组的长度之后,这个长度就是不可变化的,当数据量超过数组的长度之后,数组就无能为力了。

2、数组不能存储具有映射关系的数据。

3、数组只能存储同一类型的元素,而集合可以存储不同类型元素

4、数组可以存储基本数据类型,也可以存储引用数据类型;但是集合只能存储引用类型

为什么会出现集合类?

面向对象语言对事务的体现都是以对象的形式,所以为了方便对多个对象的操作,java就提供了集合类。

集合类的特点?

集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。

集合只能存储引用类型,那么它要存储基本类型数据该怎么办呢?

在JDK5以后有了自动装箱的功能,JVM会把基本类型的数据自动转化为对应的包装类型,然后进行存储。

自动装箱和自动拆箱详解请看我这篇文章:http://blog.csdn.net/qq_36748278/article/details/77484436

如何访问不同集合中的元素?

1、如果访问List集合中的元素,可以直接根据元素的索引来访问

2、如果访问Map集合中的元素,可以根据每项元素的key来访问其value

3、如果访问Set集合中的元素,则只能根据元素本身来访问(因为把一个对象加入到Set集合时,Set无法记住添加这个元素的顺序,所以Set里的元素不能重复)

集合的使用步骤:

1、创建集合对象

2、创建元素对象

3、把元素添加到集合

4、遍历集合

a、通过集合对象获取迭代器

b、通过迭代器hasNext()方法判断是否有元素

c、通过迭代器对象的next()方法获取元素并移动到下一个位置

Collection

Collection接口:集合的顶层接口

Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法即可用于操作Set集合,也可用于操作List和Queue集合。

Collection接口里定义了如下操作集合的方法:

1、boolean add(Object o):向集合里添加一个元素,成功添加则返回true。

2、boolean addAll(Collection c):把集合c里的所有元素添加到指定集合里,成功添加,返回true。

3、void clear():清除集合里的所有元素,将集合长度变为0。

5、boolean contains(Object o):判断集合中是否包含指定元素

6、boolean containsAll(Collection c):判断集合中是否包含集合c中的所有元素。

7、boolean isEmpty():判断集合是否为空。为空的时候返回true,否则返回false

8、boolean remove(Object o):删除集合中指定的o元素,当集合中含有多个o元素的时候,只删除第一个符合条件的元素,并将返回true

9、boolean removeAll(Collection c):从集合中删除集合c里包含的所有元素(相当于调用该方法的集合 - 集合c)

10、boolean retainAll(Collection c):从集合中删除集合c里不包含的元素(也就是把调用该方法的集合变成该集合和集合c的交集)(交集方法)。

11、int size():返回集合里元素的个数

12、Object[] toArray():该方法把一个集合转换为数组,所有的集合元素变成对应的数组元素

什么是集合的继承体系结构?

由于需求不同,java就提供了不同的集合类。这些集合类的数据结构不同,但是他们都要提供存储和遍历功能的,把他们的共性不断向上提取,最终就像成了集合的继承体系图。

cf60e3125a17183a8c5c10009b3baa5d.png

Iterator接口

Iterator iterator():返回一个Iterator对象,用于遍历集合中的元素

测试一下retainAll

假设有集合A和B,A调用此方法。把A与B的交集,交集的结果保存在A中,B中保持不变。

返回值表示A中内容是否发生过改变。

public class TestRtainAll {

public static void main(String[] args) {

ArrayListlist = new ArrayList();

list.add("第一个元素");

list.add("第二个元素");

list.add("第三个元素");

ArrayListlist1 = new ArrayList();

list1.add("第二个元素");

list1.add("第四个元素");

list1.add("第三个元素");

boolean ret = list.retainAll(list1);

System.out.println(ret);

for (int i = 0; i < list.size(); i++) {

System.out.println(list.get(i));

}

System.out.println("------");

for (int i = 0; i < list1.size(); i++) {

System.out.println(list1.get(i));

}

}

}

输出结果为:

true

第二个元素

第三个元素

------

第二个元素

第四个元素

第三个元素

boolean ret = list.retainAll(list1);

调用retainAll方法之后,list集合就变成了list集合与list1集合的交集。而list1集合保持不变

测试一下aaddAll(Collection c)方法

public class CollectionDemo {

public static void main(String[] args) {

// 创建集合1

Collectionc1 = new ArrayList();

c1.add("abc1");

c1.add("abc2");

c1.add("abc3");

//创建集合2

Collectionc2 = new ArrayList();

c2.add("def1");

c2.add("abc2");

c2.add("def3");

System.out.println(c1);

c1.addAll(c2);

System.out.println(c1);

c1.removeAll(c2);

System.out.println(c1);

}

}

输出:

[abc1, abc2, abc3]

[abc1, abc2, abc3, def1, abc2, def3] //可见ArrayList()里面的值可以重复

[abc1, abc3]

集合的三种遍历方式

集合的遍历—–Object[] toArray():把集合转换成数组,可以实现集合的遍历(不推荐)

public class CollectionDemo {

public static void main(String[] args) {

Collectionc = new ArrayList();

c.add("I");

c.add("love");

c.add("you");

Object[] objs = c.toArray(); //数组里存放的是Object类型

for(int i = 0;i < objs.length;i++){

String s = (String)objs[i]; //把Object类型转换为String类型

System.out.println(s);

}

}

}

输出:

I

love

you

集合的遍历—–增强for循环:

public class CollectionDemo {

public static void main(String[] args) {

Collectionc = new ArrayList();

c.add("I");

c.add("love");

c.add("you");

for(int i = 0;i < c.size();i++){

System.out.println(c.get(i));

}

}

}

输出:

I

love

you

集合的遍历—–Iterator迭代器。迭代器是依赖于集合而存在的。先有集合,再有迭代器。

Iterator是个接口。这个接口有3个方法:

boolean hasNext() :如果仍有元素可以迭代,则返回 true。

E next() : 返回迭代的下一个元素。 最初指向第一个元素的上面开始。

void remove() :从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。

public class CollectionDemo {

public static void main(String[] args) {

Collectionc = new ArrayList();

c.add("you");

c.add("and");

c.add("me");

Iterator it = c.iterator(); //Iterator是个接口,所以实际返回的是子类对象。it是集合c的迭代器

while(it.hasNext()){

System.out.println(it.next());

}

}

}

一定要记住next()的强大作用。一定要慎用next()方法,不要多次使用这个方法。因为每次使用都是访问一个对象

比如Iterator遍历一个集合中存放的是Student对象(有name和age属性)的情况的时候;

public class Test {

public static void main(String[] args) {

Collectionc = new ArrayList();

Student s1 = new Student("梨梨",21);

Student s2 = new Student("熊熊",24);

Student s3 = new Student("菜菜",10);

c.add(s1);

c.add(s2);

c.add(s3);

Iterator it = c.iterator(); //Iterator是个接口,所以实际返回的是子类对象。

while(it.hasNext()){

//System.out.println(((Student) it.next()).getName() + "-----" ((Student)it.next()).getAge());

//报错,因为遍历最后一个对象的时候,next()在getName()的时候已经是最后一个对象元素了,在后面的getAge()的时候又进行了一次next()方法,所以越界了

Student s = (Student)it.next();

System.out.println(s.getName() + "----" + s.getAge());

}

}

}

迭代器为什么不定义成一个类?而是要定义成一个接口?

因为如果是实现类,它就必须提供具体实现。但是集合分为很多种不同的结构,它的每一个的实现类当然也就不同。

Collections类

Collections:针对集合操作的工具类,都是静态方法。

public static void sort(Listlist):排序。默认情况下是自然排序。

public static int binarySearch(List> list,T key):二分查找

public static T max(Collection> coll):最大值

public static void reverse(List> list):反转

public static void shuffle(List> list):随机置换

当ArrayList存储基本包装类时,Collections操作方法使用:

public class CollectionsDemo {

public static void main(String[] args) {

//创建集合对象

Listlist = new ArrayList();

//添加元素

list.add(30);

list.add(50);

list.add(10);

list.add(40);

list.add(20);

System.out.println(list);

//public static void sort(Listlist):排序,默认情况下是自然排序。

Collections.sort(list);

System.out.println(list);

//public static int binarySearch(List> list,T key):二分查找

System.out.println(Collections.binarySearch(list, 30));

System.out.println(Collections.binarySearch(list, 300));

//public static T max(Collection> coll):最大值

System.out.println(Collections.max(list));

//public static void reverse(List> list):反转

Collections.reverse(list);

System.out.println(list);

//public static void shuffle(List> list):随机置换(没有固定的顺序)

Collections.shuffle(list);

System.out.println(list);

}

}

输出:

[30, 50, 10, 40, 20]

[10, 20, 30, 40, 50]

2

-6

50

[50, 40, 30, 20, 10]

[10, 30, 20, 50, 40]

当ArrayList存储自定义对象的时候,Collections的这些静态方法的使用如下:

Student类:

public class Student2{

private String name;

private int age;

public Student2() {

}

public Student2(String name, int age) {

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;

}

}

测试类:

public class CollectionsDemo2 {

public static void main(String[] args) {

//创建集合对象

Listlist = new ArrayList();

//创建学生对象

Student2 stu1 = new Student2("caicai",21);

Student2 stu2 = new Student2("lili",12);

Student2 stu3 = new Student2("xiong",34);

Student2 stu4 = new Student2("xiong",34);

Student2 stu5 = new Student2("hehe",12);

//添加元素对象

list.add(stu1);

list.add(stu2);

list.add(stu3);

list.add(stu4);

list.add(stu5);

//排序

Collections.sort(list); //自然排序,报错

//遍历集合

for(Student2 s : list){

System.out.println(s.getName() + "----" + s.getAge());

}

}

}

我们会发现,程序sort()方法报错了。为什么呢?我们来看看sort方法:

public static super T>> void sort(Listlist)

可发现列表中的所有元素都必须实现 Comparable 接口。而此时列表中元素是Student类型,所以必须在Student类中实现Comparable 才行。

修改后的Student类如下:

public class Student2 implements Comparable{

private String name;

private int age;

public Student2() {

}

public Student2(String name, int age) {

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(Student2 s) {

int num = this.age - s.age;

int num2 = (num == 0 ? this.name.compareTo(s.name) : num);

return num2;

}

}

输出:

hehe----12

lili----12

caicai----21

xiong----34

xiong----34

上面问题还有一种解决办法:通过构造器排序

public static void sort(Listlist,Comparator super T> c)

我们可以重写这个Comparator来达到同样的目的。

如果同时又自然排序和比较器排序,以比较器排序为主

//法二:通过比较器

Collections.sort(list,new Comparator() {

@Override

public int compare(Student2 s1, Student2 s2) {

int num1 = s1.getAge() - s2.getAge();

int num2 = (num1 == 0 ? s1.getName().compareTo(s2.getName()):num1);

return num2;

}

});

输出:

hehe----12

lili----12

caicai----21

xiong----34

xiong----34

Collection集合的比较:

List:有序,可重复。(存入和取出顺序一致)

ArrayList:底层数据结构是数组, 查询快,增删慢。

线程不安全,效率高。

Vector:底层数据结构是数组, 查询快,增删慢。

线程安全,效率低。

LinkedList:底层数据结构是链表, 查询慢,增删快。

线程不安全,效率高。

Set:无序,唯一。

HashSet:底层数据结构是哈希表。

保证元素唯一性:hashCode()和equals()方法。

LinkedHashSet:底层数据结构是链表和哈希表

保证元素唯一性:哈希表

保证元素有序(存入和取出顺序一致,是特殊的Set):链表

TreeSet:底层数据结构是红黑树。

保证元素排序:自然排序和比较器排序

自然排序(元素具有比较性):让元素所属的类实现Comparable接口

比较器排序(集合具有比较性):让集合接收一个Comparator的实现类对象

保证元素唯一性:根据比较的返回值是否是0来决定

List接口

List集合的特点是什么?

1、有序(存储和取出的元素一致)

2、可重复的。

List集合的三个子类各有什么特点?

ArrayList

1、底层数据结构是数组,查询快,增删慢。

2、线程不安全,效率高

Vector

1、底层数据结构是数组,查询快,增删慢。

2、线程安全,效率低

LinkedList

1、底层数据结构是链表,查询慢,增删快。

2、线程不安全,效率高。

List的3个子类都在什么情况下使用?

看自己的需求:

1、要求安全性:Vector(不过现在大部分都不用Vector)

2、不要求安全性:ArraayList或者LinkedList

2.1、查询多:ArraayList

2.1、增删多:LinkedList

ArrayList()方法里面为什么允许有重复的值存在?

我们看一下add()方法的源码:

public boolean add(E e) {

ensureCapacity(size + 1);

elementData[size++] = e;

return true;

}

我们可以看到他永远返回的都是true,也就是他每次添加都能成功,所以也就是可以添加重复的对象。

List集合特有的一些功能(父元素没有的功能)。 要注意索引的范围是否越界。

void add(int index, Object element) :在指定位置添加元素

Object get(int index):获取指定位置的元素

ListIterator listIterator():List集合特有的迭代器。

Object remove(int index):根据索引删除元素,返回被删除的元素

Object set(int index, Object element):根据索引修改元素,返回被修改的元素

public class ListDemo {

public static void main(String[] args) {

ArrayList list = new ArrayList();

list.add("hello");

list.add("you");

list.add(1,"java");

//list.add(4,"no"); //出错,索引越界了

list.add(3,"last"); //可以添加此位置

System.out.println("get方法获取要获得的元素:" + list.get(1));

System.out.println(list);

System.out.println("set方法返回被修改的元素:" + list.set(2,"me"));

System.out.println(list);

}

}

输出:

get方法获取要获得的元素:java

[hello, java, you, last]

set方法返回被修改的元素:you

[hello, java, me, last]

List接口- - - - - ->List集合的特有的两种遍历方式

List集合特有的遍历:size()+get()方法结合(普通for循环)

public class ListDemo {

public static void main(String[] args) {

Listlist = new ArrayList();

list.add("today");

list.add("is");

list.add("yours");

for(int i = 0;i < list.size();i++){

String s = (String)list.get(i);

System.out.println(s);

}

}

}

输出:

today

is

yours

List集合特有的迭代器— >列表迭代器ListIterator(它的父亲是Iterator)

该迭代器继承了Iterator迭代器,所以就可以直接使用hasNext()和next()方法。

特特有的功能是它也可以向前访问元素。

Object previous():获取上一个元素。

boolean hasPrevious():判断是否有上一个元素。

此时就需要注意指针的位置。

public class ListDemo {

public static void main(String[] args) {

Listlist = new ArrayList();

list.add("today");

list.add("is");

list.add("yours");

ListIteratorlit = list.listIterator();

//此时指针在最前面,逆向遍历就没有元素。

while(lit.hasPrevious()){

String s = (String)lit.previous();

System.out.println(s);

}

System.out.println("------");

while(lit.hasNext()){

String s = (String)lit.next();

System.out.println(s);

}

System.out.println("------");

//ListIterator还可以往前找元素。先正向后逆向即可

while(lit.hasPrevious()){

String s = (String)lit.previous();

System.out.println(s);

}

}

}

输出:

------

today

is

yours

------

yours

is

today

List接口- - - - - ->Vector集合特有的功能(父类List集合没有的)

void addElement(Object obj):添加功能 ———————————– > 被add()替代

Object elementAt(int index):获取功能 ———————————— > 被get()替代

Enumeration elements():获取功能 ———————————– > 被Iterator iterator()替代

boolean hasMoreElements()— —————————————— > 被hasNext()替代

Object nextElement()—————————————————- > 被next()替代

只需要了解一下。不推荐使用了。

public class VectorDemo {

public static void main(String[] args) {

Vectorv = new Vector();

v.addElement("I");

v.addElement("miss");

v.addElement("you");

//第一种方式

for(int i = 0;i < v.size();i++){

String s = (String)v.elementAt(i);

System.out.println(s);

}

System.out.println("--------");

//第二种方式

//Enumeration是接口

Enumerationen = v.elements(); //返回的是实现类的对象

while(en.hasMoreElements()){

String s = (String)en.nextElement();

System.out.println(s);

}

}

}

输出:

I

miss

you

--------

I

miss

you

List接口- - - - - ->LinkedList集合特有的功能(父类List集合没有的)

addFirst(Object o):

addLast(Object o):

getFirst():

getLast():

removeFirst():

removeLast():

public class LinkedListDemo {

public static void main(String[] args) {

LinkedListlink = new LinkedList();

link.add("I");

link.add("love");

link.add("you");

link.addFirst("first");

link.addLast("last");

System.out.println("removeFirst方法:" + link.removeFirst());

System.out.println("removeLast方法:" + link.removeLast());

System.out.println(link.getFirst());

System.out.println(link.getLast());

System.out.println(link);

}

}

输出:

removeFirst方法:first

removeLast方法:last

I

you

[I, love, you]

并发修改异常

并发修改异常:ConcurrentModificationException

迭代器遍历集合,集合修改元素的时候会发生这个异常。

因为迭代器是依赖于集合而存在的,集合中新添加了元素,而迭代器却不知道,迭代器获取的还是修改之前的那个集合,所以会报错。这个错叫并发修改异常。

也就是说迭代器遍历元素的时候,集合是不可以修改元素的。

public class ListDemo {

public static void main(String[] args) {

Listlist = new ArrayList();

list.add("today");

list.add("is");

list.add("yours");

//迭代器遍历。

Iteratorit = list.iterator();

while(it.hasNext()){

String s = (String)it.next();

if("is".equals(s)){

//list.add("haha"); //报错。并发修改异常

}

System.out.println(s);

}

}

}

解决方法:

1、迭代器遍历元素,迭代器修改元素。Iterator没有添加方法,而它的子方法ListIterator有。

元素是跟在他刚才迭代的元素后面的。

2、集合遍历元素,集合修改元素。

public class ListDemo {

public static void main(String[] args) {

Listlist = new ArrayList();

list.add("today");

list.add("is");

list.add("yours");

//迭代器遍历。迭代器添加

ListIteratorlit = list.listIterator();

while(lit.hasNext()){

String s = (String)lit.next();

if("is".equals(s)){

lit.add("haha");

}

}

System.out.println(list);

}

}

输出:[today, is, yours, haha]

public class ListDemo {

public static void main(String[] args) {

Listlist = new ArrayList();

list.add("today");

list.add("is");

list.add("yours");

//集合遍历元素,集合修改元素

for(int i = 0;i < list.size();i++){

String s = (String)list.get(i);

if("is".equals(s)){

list.add("haha");

}

}

System.out.println(list);

}

}

输出:[today, is, yours, haha]

Set接口

Set集合:无序(存储顺序和取出顺序不一致),值不可以重复。但是虽然Set集合的元素无序,但是作为集合来说,他有自己的存储顺序。

Set接口- - - - - ->HashSet集合类

为什么HashSet存储字符串的时候,字符串相同的值只存储了一个呢?

可以去看我这篇文章:http://blog.csdn.net/qq_36748278/article/details/77842660

Set接口- - - - - ->LinkedHashSet集合类(继承HashSet集合类)

LinkedHashSet:特殊的set集合。底层数据结构由哈希表和链表组成。哈希表保证元素的唯一性,链表保证元素有序(存储和取出顺序是一致的)。

Set接口- - - - - ->TreeSet集合类

TreeSet:底层是二叉树。并且是红黑树。红黑树是一种自平衡二叉树。

TreeSet:能够对元素按照某种规则进行排序。

1、一种叫做自然排序。根据元素的自然顺序对元素进行排序

2、根据创建set时提供的Comparator进行排序。具体取决于使用的构造方法。

public TreeSet():构造一个新的空 set,该 set 根据其元素的自然顺序进行排序

public TreeSet(Comparator super E> comparator):构造一个新的空 TreeSet,它根据指定比较器进行排序。

TreeSet是如何保证元素的唯一性和排序的呢?

请看我这篇文章,有源码,解析的很透彻:http://blog.csdn.net/qq_36748278/article/details/77915801#t1

扩展

集合的toString方法的作用原理是什么呢?

public static void main(String[] args) {

Collection c = new ArrayList();

c.add("I");

c.add("am");

c.add("here");

System.out.println(c);

}

输出:

[I, am, here]

现在我们就有个疑惑了,为什么打印输出c输出的不是地址而是值呢?

出现这种情况,我们应该就会猜想集合c应该是调用了toString()方法,所以才没有输出地址值。我们假设是调用了toString()方法。Collection c = new ArrayList();这是多态的用法,所以调用的也可定时ArrayList的toString方法才对。为了解决我们的疑惑,我觉得看源码是直接的方式。

于是我去ArrayList类中找toString方法,但是没有找到,那怎么办呢?只能去ArrayList类的父类中找啦,于是去它的父类AbstractList类中找,还是没有找到,于是我们就去AbstractList类的父类中找,终于找到了toString()方法。

d55ba0b249732ef511ba699a837d1317.png

代码如下:

public String toString() {

Iteratorit = iterator(); //集合本身调用迭代器方法,得到集合迭代器

if (! it.hasNext()) //如果没有元素,就返回空

return "[]";

StringBuilder sb = new StringBuilder();

sb.append('[');

for (;;) {

E e = it.next(); //如果有元素,就进行拼接

sb.append(e == this ? "(this Collection)" : e);

if (! it.hasNext())

return sb.append(']').toString();

sb.append(',').append(' ');

}

}

杂记

1、ArrayList()方法默认的是构造一个初始容量为10的空列表。数据增长:当需要增长时,Vector 默认增长为原来一培,而ArrayList却是原来的一半 。

2、数组求长度用length属性;字符串String求长度用length()方法;集合求长度用size()方法

3、对象数组:数组即可以存储基本数据类型,也可以存储引用数据类型,它存储引用数据类型的时候的数组叫做对象数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值