Java集合Collection
来源:互联网 发布:java接口怎么写 编辑:程序博客网 时间:2024/06/08 12:13
集合,就是存放数据对象的容器,类比于之前学过的一种容器–数组,集合有着比较高的优越性。就拿存放的数据来说,数组存放的数据类型是单一的,即仅能存放某一种类型的数据,而且数组一旦定义以后,其长度大小都不可再更改,而Collection集合可以存放任意类型的数据,集合的大小可以随着存储数据量的多少自动增容。(当然集合和数组中存放的都是对象的引用而非对象本身)
Collection接口类:最顶层的集合接口类:其中定义了集合的通用性方法,包含基本的操作CRUD
—–List接口:有序,可重复性
—–Set接口:无序,不可重复
上述两类都是单列集合
Collection接口的共性方法:
增加:
1:add() 将指定对象存储到容器中 add 方法的参数类型是Object 便于接收任意对象2:addAll() 将指定集合中的元素添加到调用该方法和集合中
删除:
3:remove() 将指定的对象从集合中删除4:removeAll() 将指定集合中的元素删除
修改
5:clear() 清空集合中的所有元素
判断
6:isEmpty() 判断集合是否为空7:contains() 判断集合何中是否包含指定对象 所以使用contains方法进行判断的时候,可能会需要重写equals方法,因为判断包含的标准可能不一样8:containsAll() 判断集合中是否包含指定集合 使用equals()判断两个对象是否相等
获取:
9:int size() 返回集合容器的大小
转成数组
10: toArray() 集合转换数组
List接口:有序,可重复性的元素;对于List类的集合,会涉及到对于某个索引的操作,允许在指定位置插入元素,通过索引来访问某个元素
List接口特有的方法:
1:增加
void add(int index, E element) 指定位置添加元素 boolean addAll(int index, Collection c) 指定位置添加集合
2:删除
E remove(int index) 删除指定位置元素
3:修改
E set(int index, E element) 返回的是需要替换的集合中的元素
4:查找:
E get(int index) 注意: IndexOutOfBoundsExceptionint indexOf(Object o) // 找不到返回-1lastIndexOf(Object o)
5:求子集合
List<E> subList(int fromIndex, int toIndex) // 不包含toIndex
List接口的具体实现类:
——-ArrayList:数组集合
实现原理:ArrayList底层是维护了一个Object数组实现的,利用ArrayList无参构造函数的时候,默认的数组长度是10,当然可以通过ArrayList的构造函数new ArrayList(20)来显示指定初始容量。当目前的容量不够存储对象时,会自动增容为原来的1.5倍。
特点:查询速度快,增删慢。
原因:因为ArrayList底层维护的是一个Object数组,我们都知道数组的空间内存地址是连续的,所以访问某个对象的时候,只需要通过索引便可以直接获取到。但是当我们向ArrayList对象添加对象的时候,首先判断目前的容量是否足够,如果足够,这种情况还好,直接添加元素就可以;如果不够,会创建一个新的ArrayList对象,容量为久对象容量的1.5倍,同时将旧数组中的所有元素拷贝到新数组中,如果数据量大,效率低得可怕。对于删除,假设有一个长度100的数组,假如删除了索引位置2处的一个元素,由于存储的元素是内存空间连续,所以说需要将索引3以后的全部元素拷贝到索引2开始的位置,其中也会有数据量的拷贝,所以效率也低下。
下面写一个小程序:去除ArrayList中重复的元素
思路:新建一个ArrayList对象,遍历旧集合,如果元素不包含在新的ArrayList集合中,则将该元素加入到新集合对象中
public class Demo6 { public static void main(String[] args) { ArrayList arr = new ArrayList(); Person p1 = new Person("jack", 20); Person p2 = new Person("rose", 18); Person p3 = new Person("rose", 18); arr.add(p1); arr.add(p2); arr.add(p3); System.out.println(arr); ArrayList arr2 = new ArrayList(); for (int i = 0; i < arr.size(); i++) { Object obj = arr.get(i); Person p = (Person) obj; if (!(arr2.contains(p))) { arr2.add(p); } } //利用Iterator /** Iterator it = arr.iterator(); Person p = (Person)it.next(); if(!arr2.contains(p)){ arr2.add(p); } } */ System.out.println(arr2); }}class Person { private String name; private int age; public Person() { } public Person(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 hashCode() { return this.name.hashCode() + age * 37; } @Override public boolean equals(Object obj) { if (!(obj instanceof Person)) { return false; } Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age; } @Override public String toString() { return "Person@name:" + this.name + " age:" + this.age; }}
在实际开发中,ArrayList是使用率最高的一个集合。
List接口的具体实现类:
——-LinkedList:链表集合
实现原理:底层是采用链表实现的,内存地址是不连续的。
特点:查找慢,增删快。
原因:因为底层是双向链表实现,每个节点存储了对象的引用和下一个节点的内存地址。内存地址不连续,所以说如果想要检索集合中的某个元素,必须通过遍历该集合,依次取出每个元素来进行比对判断是否是所要检索的元素,所以查找慢;但是增加元素的时候,链表在插入新元素的时候,只需要让前一个元素记住新元素,让新元素记住下一个元素就可以了,所以插入很快;删除元素的时候,只需要让前一个元素记住后一个元素,后一个元素记住前一个元素就可以了,所以删除也很快。
特有方法:
1:方法介绍
addFirst(E e) addLast(E e) getFirst() getLast() removeFirst() removeLast()
如果集合中没有元素,获取或者删除元
素抛:NoSuchElementException2:数据结构
1:栈 先进后出 push() pop() 2:队列 先进先出 offer() poll()
3:返回逆序的迭代器对象
descendingIterator() 返回逆序的迭代器对象
很多方法的功能无需解释,看方法就能猜个差不多,其中push()和addLast()实现的功能是一样的,都是在后面增加一个元素;pop()和removeLast()实现的功能也是一样的,都是移除最后一个元素;offer()和addLast()实现的功能是一样的,都是在后面增加一个元素;poll()和removeFirst()实现的功能是一样的,都是移除第一个元素。奇怪了?为什么设计这么多方法实现了相同的功能?
这是为了模拟堆栈结构和队列结构,下面的程序利用LinkedList集合模拟了堆栈结构和队列结构
import java.util.LinkedList;/** * Created by Dream on 2017/10/24. * StackList模拟的堆栈结构 * QueueList模拟的队列结构 */class StackList{ LinkedList list; public StackList(){ list = new LinkedList(); } public void add(Object o){ list.push(o); } public Object pop(){ return list.pop(); } public int size(){ return list.size(); }}class QueueList{ LinkedList list; public QueueList(){ list = new LinkedList(); } public void add(Object o){ list.offer(o); } public Object remove(){ return list.poll(); } public int size(){ return list.size(); }}public class LinkedListPrac { public static void main(String[] args){ StackList stack = new StackList(); stack.add("Dream11"); stack.add("Thia22"); stack.add("Sam33"); int size1 = stack.size(); for(int i=0;i<size1;i++){ System.out.print(stack.pop()+" "); } System.out.println(); QueueList queue = new QueueList(); queue.add("Dream11"); queue.add("Thia22"); queue.add("Sam33"); int size2 = queue.size(); for(int i=0;i<size2;i++){ System.out.print(queue.remove()+" "); } }}
下面再利用LinkedList集合实现洗牌的功能
import java.util.LinkedList;import java.util.Random;/** * Created by Dream on 2017/10/24. * 所有的花色和点数分别存放在String数组中,然后通过循环首先创建扑克牌 * 然后通过随机生成0~51范围的整数作为索引值,交换两个索引值的元素 * 如此往复100次,也可以其他次数,即可得到洗牌后的结果 */class Poker{ String color; String num; public Poker(String color,String num){ this.color = color; this.num = num; } public String toString(){ return "["+color+num+"]"; }}public class PokerLinkedList { public static void main(String[] args){ LinkedList pokers = createPoker(); shufflePokers(pokers); showPokers(pokers); } public static LinkedList createPoker(){ LinkedList pokers = new LinkedList(); String[] colors = {"黑桃","梅花","方片","红桃"}; String[] nums = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"}; for(int i=0;i<colors.length;i++){ for(int j=0;j<nums.length;j++){ Poker poker = new Poker(colors[i],nums[j]); pokers.add(poker); } } return pokers; } public static void shufflePokers(LinkedList pokers){ Random random = new Random(); for(int i=0;i<100;i++){ int index1 = random.nextInt(52); int index2 = random.nextInt(52); Poker p1 = (Poker) pokers.get(index1); Poker p2 = (Poker)pokers.get(index2); pokers.set(index1,p2); pokers.set(index2,p1); } } public static void showPokers(LinkedList pokers){ for(int i=0;i<pokers.size();i++){ System.out.print(pokers.get(i)); if(i%10 == 9) System.out.println(); } }}
List接口的具体实现类:
——-Vector:描述的是线程安全的ArrayList集合
实现原理:底层也是维护了一个Object数组实现的,实现原理与ArrayList是一样的。但是Vector线程安全,操作效率低
???ArrayList和Vector的区别
!!!相同点:底层都是通过维护一个Object数组实现的
不同点:1.ArrayList是线程不同步的,操作效率高;Vector是线程同步的,操作效率低。
2.ArrayList是JDK1.2出现的,Vector是JDK1.0就出现了
迭代方法并不是Iterator,而是通过Enumeration 接口
boolean hasMoreElements()
测试此枚举是否包含更多的元素。
E nextElement()
如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
Vector v = new Vector();// 遍历Vector遍历Enumeration ens = v.elements();while ( ens.hasMoreElements() ){ System.out.println( ens.nextElement() );}
迭代器:专门取出集合中的对象,通俗来讲,就是通过迭代器能够遍历集合中的对象。但是该对象是比较特殊的,不能通过new来创建,因为该类是以内部类的形式存在于集合内部,是依赖于集合而存在的
Collection接口类中定义了获取迭代器的方法iterator(),所有Collection集合体系的均可以获得自身集合的迭代器,只不过每个子类都进行了重写,因为底层存储数据的实现方式是不一样的
对于List集合,可以通过get方法或者Iterator获取元素,而Set集合没有get方法,只能通过Iterator获取
Collection集合的通用迭代器—Iterator接口该接口是集合的迭代器接口类,定义了常见的迭代方法
1:boolean hasNext()
判断集合中是否有元素,如果有元素可以迭代,就返回true。
2: E next()
返回迭代的下一个元素,注意: 如果没有下一个元素时,调用
next元素会抛出NoSuchElementException
3: void remove()
从迭代器指向的集合中移除迭代器返回的最后一个元素(可选操作)。
下面来分析一下迭代器的原理:
ListIterator it = list.listIterator(); //1 while(it.hasNext()){ //2 System.out.print(it.next()+" "); //3}
1.返回一个迭代器,获取迭代器的时候,迭代器中的指针指向了第一个元素
2.hasNext()判断迭代器当前指针是否有指向元素
3.next()方法获取指针当前指向的元素同时将指针地址+1,使其指向下一个元素
注意:在对集合进行迭代的过程中,是不允许迭代器以外的方式对元素进行操作,因为这样会产生安全隐患,Java会抛出 java.util.ConcurrentModificationException异常,普通迭代器只支持在迭代器中删除操作。
如下列代码就会报上述异常:
List list = new ArrayList();list.add("A1");list.add("B2");list.add("C3");list.add("D4");Iterator it = list.iterator();while(it.hasNext()){ System.out.println(it.next()); list.add("aaa");}
ListIterator:List特有的迭代器,除了有通用的迭代器方法,该迭代器还支持添加元素,逆序遍历元素
add(E e)
将指定的元素插入列表(可选操作)。该元素直接插入到 next 返回的下一个元素的前面(如果有)
void set(E o)
用指定元素替换 next 或 previous 返回的最后一个元素
hasPrevious()
逆向遍历列表,列表迭代器有多个元素,则返回 true。
previous()
返回列表中的前一个元素。
while (it.hasPrevious()){ //1 System.out.print(it.previous()+" "); //2}
1.hasPrevious判断是否存在上一个元素
2.如果存在上一个元素,首先将指针向上移动一个单位,然后再获取当前指针指向的元素
List list = new ArrayList();list.add("A1");list.add("B2");list.add("C3");list.add("D4");ListIterator it = list.listIterator; while (it.hasNext()){ //1 System.out.print(it.next()+" "); //2 it.add("aaa"); //3}
1.hasNext判断当前指针是否指向元素
2.获取当前指针指向的元素,同时将指针向下移动一个单位
3.添加元素的时候是插入到next返回的下一个元素前面
剖析一下上述代码执行的过程:
最初集合内元素:A1,B2,C3,D4
1.迭代器指针指向A1元素
2.此时的next返回的元素是A1,next返回的下一个元素是B2,而且此时迭代器指针已经指向B2
3.添加到元素B2之前
一次循环执行结束后集合内元素:A1,aaa,B2,C3,D4
继续执行:
1.迭代器指向B2
2.此时next返回的元素是B2,返回的下一个元素是C3,而且此时迭代器指针已经指向C3
3.添加到C3元素之前
第二次循环结束后集合内的元素:A1,aaa,B2,aaa,C3,D4
以此类推,最终集合内元素为:A1,aaa,B2,aaa,C3,aaa,D4,aaa
Set集合接口:集合中元素无序,且不可重复。存入的数据顺序和我们取出的数据顺序是不一定一致的。其中没有get方法,如果想得到其中存储的元素,只能通过迭代器获得
————HashSet:Set接口具体实现类的一种,底层是通过哈希表实现的。
实现原理:底层通过哈希表存储,在向hashSet集合中添加对象时,首先计算该对象的哈希值,然后根据对象的哈希值得出在哈希表中的存放位置(可以把哈希值简单理解为内存地址)。假设现在要新添加一个对象,计算该对象得到该对象的哈希值,然后就去哈希表里寻找这个位置
1.如果该位置没有存储任何元素,则可以直接将该元素放到此存储位置上
2.如果发现该位置已经存储有数据,那么新对象会和已存储的数据进行equals方法比较,如果相等,则该元素视为相同元素,不再添加到哈希表中,此时是添加新元素失败;如果equals方法返回了false,则表示并不是同一个元素,则将该元素也添加到哈希表的该位置上。咦?奇怪,怎么不是一个萝卜一个坑?这是因为哈希表中每个位置都是一个“桶式结构”,如果有相同哈希值但是不想等的元素就会被放置到同一个“桶”里。总结一下就是,先比较hashCode值,如果相同,再equals比较,这是最后一道防线。
特点:存取速度快,通过对象的哈希值直接可以得出在哈希表中的位置,相当于直接索引,所以存取速度快!
创建一个HashSet对象,每次向元素中添加对象的时候,首先会调用该对象的hashCode()方法,如果hashCode()方法返回的结果是一样的,则会再调用该对象的equals()方法,所以每次添加对象,hashCode方法是必调用,equals只有在hashCode方法返回true时才会被调用。对于自定义的对象,需要根据添加规则来重写hashCode方法和equals方法,否则默认继承Object的方法,有可能是不满足要求的!
写一个小例子:
import java.util.HashSet;/** * Created by Dream on 2017/10/26. * 对于要添加的对象,只要身份证号id一样,无论其名字是什么,视为同一人 */class Person{ int id; String name; public Person(int id,String name){ this.name = name; this.id = id; } public String toString(){ return "{id:"+this.id+" name:"+this.name+"}"; } public int hashCode(){ System.out.println("====hashCode====="); return this.id; } public boolean equals(Object o){ System.out.println("======equals===="); Person p = (Person)o; return this.id == p.id; }}public class HashSetPrac { public static void main(String[] args){ HashSet set = new HashSet(); set.add(new Person(110,"Dream")); set.add(new Person(440,"Thia")); set.add(new Person(220,"Marry")); set.add(new Person(330,"Tom")); set.add(new Person(110,"耿耿")); System.out.println(set); }}
输出结果:
====hashCode=====
====hashCode=====
====hashCode=====
====hashCode=====
====hashCode=====
======equals====
false
[{id:440 name:Thia}, {id:330 name:Tom}, {id:220 name:Marry}, {id:110 name:Dream}]
添加几次元素调用几次HashCode方法,其中最后一个元素添加失败,因为id号为110的元素已经被存储了
注意:String类对象重写了hashCode方法,如果两个String对象的内容是一致的,则其hashCode得到的结果也是一致的!
——–>TreeSet集合:底层是通过红黑树(二叉树)结构实现的。
如果元素具备自然顺序的特性,那么就按照元素具备的自然顺序特性进行排序。一提到自然顺序,可能想到的就是阿拉伯数字和英文字母,确实,如果向TreeSet集合中存储这些数据,得到的都是有序的数据。
但是,如果我们想要存储自定义的对象呢?TreeSet存储的元素是有序的,可想而知肯定是具有某种比较规则,如果是自定义的对象,就需要我们提供一定的比较规则!如果不定义比较规则,就会报错
1.添加的元素不具备自然顺序的特性,则元素所属的类必须实现Comparable接口,把元素的比较规则写在方法compareTo方法中,如果方法返回0则视该元素为重复元素,不再添加。
import java.util.TreeSet;/** * Created by Dream on 2017/10/26. * 人员按照工资多少排序 */class Employee implements Comparable{ int id; String name; int salary; public Employee(int id,String name,int salary){ this.id = id; this.name = name; this.salary = salary; } public String toString(){ return "{id:"+id+" name:"+name+" salary:"+salary+"}"; } /*负整数、零或正整数,根据此对象是小于、等于还是大于指定对象*/ public int compareTo(Object o){ Employee e = (Employee)o; return this.salary-e.salary; }}public class TreeSetPrac { public static void main(String[] args){ TreeSet set = new TreeSet(); set.add(new Employee(110,"Dream",300)); set.add(new Employee(220,"Thia",100)); set.add(new Employee(330,"Jerry",200)); set.add(new Employee(440,"Tom",500)); System.out.println(set); }}
红黑树是一种特殊的二叉树,左大右小,即左节点的值小于其父节点的值,右节点的值大于其父节点的值。
实现原理:底层是通过红黑树结构存储的。添加的第一个元素作为红黑树的根节点,并且根节点自己和自己也会有比较,添加第二个元素的时候,首先与跟元素比较,如果小于则存储为左节点,如果大于,则存储为右节点,同样的道理去添加后续的元素。每次添加完节点后,都要看一下当前的“树结构”是否是二叉树,如果不是,则首先调整为二叉树,然后再添加元素。
2.添加的元素不具备自然顺序的特性,同时对象自身也没有实现Comparable接口,那么创建TreeSet的时候必须传入一个比较器,如果比较方法返回0,则视为重复元素。
自定义比较器格式:
class myClass implements Comparator{ int compare(T t1,T t2){ }}
/*自定义比较器*/class MyComparator implements Comparator{ public int compare(Object o1,Object o2){ Employee e1 = (Employee)o1; Employee e2 = (Employee)o2; return e1.id - e2.id; }}public class TreeSetPrac { public static void main(String[] args){ MyComparator comparator = new MyComparator(); TreeSet set = new TreeSet(comparator); set.add(new Employee(110,"Dream",300)); set.add(new Employee(220,"Thia",100)); set.add(new Employee(330,"Jerry",200)); set.add(new Employee(440,"Tom",500)); System.out.println(set); }}
推荐使用Comparator比较器,因为这个比较器可以应用在很多类上,而Comparable的比较规则只能在一个类中使用。
如果同时传入比较器,而且对象本身也实现了Comparable接口,那么排序规则以比较器为准。
向TreeSet集合中存入字符串对象,是可以对字符串进行比较的,因为String类已经实现了Comparable接口,重写了compareTo方法,那么比较规则是什么?
1.对应位置处的字符不相同,比较的就是对应位置处不同的字符
2.如果对应上的字符都一样,比较的就是字符串的长度
- java Collection (java 集合)
- java collection(java集合)
- java集合-----Collection
- Java集合Collection介绍
- Java集合框架Collection
- Java集合框架Collection
- java集合Collection
- Java 集合框架-Collection
- java中的集合Collection
- java 集合-001 Collection
- Java 集合(Collection)
- Java collection 集合分析
- Java Collection框架集合
- Java Collection (集合框架)
- java集合框架->Collection
- Java Collection与集合
- Java集合之Collection
- java 集合Collection架构
- Bit Operation exchange & Tmp exchange
- ios将string字符串转换为array数组、将string字符串转换为array数组
- Spring学习——Spring中定时器实现
- softmax regression求导
- -bash: fork: Cannot allocate memory 问题的处理
- Java集合Collection
- C# 64位系统无法读取Access数据库
- HDU4348
- Web服务器
- MindManager 2018新增功能有这些
- jquery 限制文本中文输入30个英文数字或15个汉字
- 遗传算法(GA)干货
- kmeans对图像和数据进行分割
- poi处理excel问题