目录
一 ArrayList, LinkedList,Stack, Queue常用方法介绍及实现
1.实现Collection接口的ArrayList类的常用方法使用
实现Collection接口的LinkedList类的常用方法
四 List中 remove(int index) 和 remove(Object o) 方法的区别
Java集合大致可以分为Set、List、Queue和Map四种体系,其中Set代表无序、不可重复的集合;List代表有序、重复的集合;而Map则代表具有映射关系的集合,Java 5 又增加了Queue体系集合,代表一种队列集合实现。
Collection 接口的接口 对象的集合(单列集合)
├——-List 接口:元素按进入先后有序保存,可重复
│—————-├ LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
│—————-├ ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
│—————-└ Vector 接口实现类 数组, 同步, 线程安全
│ ———————-└ Stack 是Vector类的实现类
└——-Set 接口: 仅接收一次,不可重复,并做内部排序
├—————-└HashSet 使用hash表(数组)存储元素
│————————└ LinkedHashSet 链表维护元素的插入次序
└ —————-TreeSet 底层实现为二叉树,元素排好序
一 ArrayList, LinkedList,Stack, Queue常用方法介绍及实现
1.实现Collection接口的ArrayList类的常用方法使用
/**
* 使用Collection集合中的方法
* 1. boolean add(E e) 在集合末尾添加元素
* 2. boolean remove(Object o) 若本集合中有值与o相等的元素,则删除该元素并返回true
* 3. void clear() 清楚本集合中所有元素,调用完方法后本集合为空
* 4. boolean contains(Object o) 判断集合中是否包含某元素
* 5. boolean isEmpty() 判断集合是否为空
* 6. int size() 返回集合中的元素个数
* 7. boolean addAll(Collection c) 将一个集合c中的所有元素添加到另一个集合中
* 8. Object[] toArray() 返回一个包含本集合中所有元素的数组,类型为Object[]
* 9. Iterator iterator() 迭代器,集合的专用遍历方式
*/
List<Integer> list1 = new ArrayList<>();
list1.add(1); //在集合末尾添加元素
list1.add(2);
list1.add(3);
list1.add(4);
System.out.println(list1); //[1, 2, 3, 4]
Integer i = 4;
boolean flag1 = list1.remove(i); //若集合中有值和obj相等的元素,则删除该元素并返回true
System.out.println("flag1: " + flag1); //true
System.out.println(list1); //[1, 2, 3]
boolean flag2 = list1.contains(1); //判断集合中是否包含某元素
System.out.println("flag2: " + flag2); //true
boolean flag3 = list1.isEmpty(); //判断集合是否为空
System.out.println("flag3: " + flag3); //false
System.out.println(list1.size()); //返回集合中的元素个数 3
Object[] a = list1.toArray(); //返回一个包含本集合中所有元素的数组,类型为object[]
System.out.println(a); // [Ljava.lang.Object;@1540e19d
//创建iterator迭代器,遍历list1集合
Iterator<Integer> iterator = list1.iterator();
while(iterator.hasNext()){
int it = iterator.next();
System.out.print(it + " "); //1 2 3
}
System.out.println();
list1.clear();
System.out.println(list1.isEmpty()); //true
System.out.println(list1.size()); //0
2.实现List集合的ArrayList类的特有方法
/**
* 测试List特有的方法
* 1. void add(int index,Object obj) 在指定位置添加元素
* 2. Object remove(int index) 根据指定索引删除元素,并把删除元素返回
* 3. Object set(int index,Object obj) 把指定索引位置的元素修改为指定的值,返回修改前的值
* 4. int indexOf(Object o) 返回指定元素在集合中第一次出现的索引
* 5. Object get(int index) 获取指定位置的元素
* 6. ListIterator listIterator() 列表迭代器
* 7. List subList(int fromIndex,int toIndex) 截取从索引fromIndex到toIndex的元素集合 包左不包右
*/
List<Integer> list2 = new ArrayList<>();
List<Integer> list3 = new ArrayList<>();
list2.add(6);
list2.add(3);
list2.add(4);
list2.add(5);
list2.add(1,2); //在指定位置插入元素,后面的元素都往后移一个元素
System.out.println(list2); //[6, 2, 3, 4, 5]
list2.set(0,1); //在索引为index位置的元素更改为obj
System.out.println(list2); //[1, 2, 3, 4, 5]
int value1 = list2.remove(3); // 删除指定索引的元素
System.out.println("value1: " + value1); //4
System.out.println("list2: " + list2); //[1, 2, 3, 5]
Integer i1 = 3;
int value2 = list2.indexOf(i1); //返回集合中第一次出现的o元素的索引位置,如果list集合没有o元素,则返回-1
System.out.println(value2); //2
int i2 = list2.get(2); //返回集合中指定索引位置的元素
System.out.println(i2); //3
ListIterator<Integer> iterator1 = list2.listIterator();
while(iterator1.hasNext()){
System.out.print(iterator1.next()+" ");
}
System.out.println();
System.out.println(list2); //[1, 2, 3, 5]
iterator1.add(5);
System.out.println(list2); //[1, 2, 3, 5, 5]
while(iterator1.hasPrevious()){
System.out.print(iterator1.previous() + " "); //5 5 3 2 1
}
System.out.println();
list3 = list2.subList(1,3); //返回从索引fromIndex到toIndex的元素集合,包左不包右
System.out.println(list3); //[2, 3]
iterator1.next(); //返回集合中Iterator指向位置后面的元素
int i4 = iterator1.nextIndex(); //返回列表中ListIterator所需位置后面元素的索引
System.out.println(i4); // 1
实现Collection接口的LinkedList类的常用方法
/**
* 测试Collection中的方法
* 1. boolean add(E e) 在集合末尾添加元素
* 2. boolean remove(Object o) 若本集合中有值与o相等的元素,则删除该元素并返回true
* 3. void clear() 清楚本集合中所有元素,调用完方法后本集合为空
* 4. boolean contains(Object o) 判断集合中是否包含某元素
* 5. boolean isEmpty() 判断集合是否为空
* 6. int size() 返回集合中的元素个数
* 7. boolean addAll(Collection c) 将一个集合c中的所有元素添加到另一个集合中
* 8. Object[] toArray() 返回一个包含本集合中所有元素的数组,类型为Object[]
* 9. Iterator iterator() 迭代器,集合的专用遍历方式
*/
List<String> list1 = new LinkedList<>();
list1.add("1");
list1.add("2");
list1.add("3");
list1.add("4");
System.out.println(list1); //[1, 2, 3, 4]
System.out.println(list1.contains("2")); //true
System.out.println(list1.size()); //4
System.out.println(list1.isEmpty()); //false
System.out.println(list1.remove("3")); //true
System.out.println(list1); //[1, 2, 4]
Iterator<String> iterator = list1.iterator();
while(iterator.hasNext()){
String s = iterator.next();
System.out.print(s+" "); //1 2 4
}
System.out.println();
Object[] objects = list1.toArray();
System.out.println(objects); //[Ljava.lang.Object;@1540e19d
for (int i = 0 ;i < objects.length; i++){
System.out.print(objects[i] + " "); //1 2 4
}
System.out.println();
list1.clear();
System.out.println(list1.isEmpty()); //true
System.out.println(list1.size()); //0
System.out.println(Arrays.toString(objects)); //[1, 2, 4]
3.实现List接口的LinkedList类的常用方法
/**
* 测试List中特有的方法
* 1. void add(int index,Object obj) 在指定位置添加元素
* 2. Object remove(int index) 根据指定索引删除元素,并把删除元素返回
* 3. Object set(int index,Object obj) 把指定索引位置的元素修改为指定的值,返回修改前的值
* 4. int indexOf(Object o) 返回指定元素在集合中第一次出现的索引
* 5. Object get(int index) 获取指定位置的元素
* 6. ListIterator listIterator() 列表迭代器
* 7. List subList(int fromIndex,int toIndex) 截取从索引fromIndex到toIndex的元素集合 包左不包右
*/
List<String> list2 = new LinkedList<>();
list2.add("2");
list2.add("3");
list2.add("4");
System.out.println(list2); //[2, 3, 4]
list2.add(0,"1");
System.out.println(list2); //[1, 2, 3, 4]
String s = list2.remove(1);
System.out.println(s); //2
System.out.println(list2); //[1, 3, 4]
list2.set(1,"1");
System.out.println(list2); //[1, 1, 4]
System.out.println(list2.indexOf("1")); //0
System.out.println(list2.get(2)); //4
ListIterator listIterator = list2.listIterator();
listIterator.next();
listIterator.next();
while(listIterator.hasPrevious()){
String str = (String) listIterator.previous();
System.out.print(str + " "); //1 1
}
System.out.println();
List sub_list = new LinkedList();
System.out.println(list2); // [1, 1, 4]
sub_list = list2.subList(1,3);
System.out.println(sub_list); //[1, 4]
4.LinkedList中特有的方法
/**
* 测试LinkedList中的特有方法
* 1.addFirst(E e) 在该列表开头插入指定的元素
* 2.addLast(E e) 将指定的元素追加到此列表的末尾
* 3.E getFirst() 返回此列表中的第一个元素
* 4.E getLast() 返回此列表中的最后一个元素
* 5.E removeFirst() 从此列表中删除并返回第一个元素
* 6.E removeLast() 从此列表中删除并返回最后一个元素
*/
LinkedList<String> list3 = new LinkedList<>();
list3.add("2");
list3.add("3");
list3.add("4");
System.out.println(list3); //[2, 3, 4]
list3.addFirst("1");
list3.addLast("5");
System.out.println(list3); //[1, 2, 3, 4, 5]
System.out.println(list3.getFirst()); //1
System.out.println(list3.getLast()); //5
System.out.println(list3.removeFirst()); //1
System.out.println(list3); //[2, 3, 4, 5]
System.out.println(list3.removeLast()); //5
System.out.println(list3); //[2, 3, 4]
5.stack特有的方法
/**
* Stack 继承了Vector,Vector实现了List接口,因此Stack拥有List的所有方法
* stack特有的方法
* 1. boolean empty() 判断栈是否为空
* 2. E peek() 查看当前的栈顶元素
* 3. E pop() 删除当前的栈顶元素,并将其作为返回值返回
* 4. E push(E e) 添加该数据到栈顶
* 5. int search(object o) 若是栈顶,返回1,栈顶下一个,返回2,依次增加。找不到返回-1
*/
Stack<Integer> stack = new Stack<>();
stack.add(1);
stack.add(2);
stack.push(3);
System.out.println(stack); //[1, 2, 3]
System.out.println(stack.peek()); //3
int value1 = stack.pop();
System.out.println(value1); //3
System.out.println(stack); //[1, 2]
System.out.println(stack.peek()); //2
System.out.println(stack.isEmpty()); //false
System.out.println(stack.search(1)); //2
6.Queue特有的方法
Queue代表的线性表中的队列,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,遵循先进先出原则(FIFO,First In First Out)。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
/**
* Queue是接口,Deque双端队列继承了Queue接口,Queue和Deque都不能实例化对象
* LinkedList实现了Queue和Deque接口,可以用LinkedList实现队列的操作
* Queue接口特有的方法
* 1. boolean add(E e) 添加元素到队列中,如果不违反容量限制立即执行,若无可用可用空间会抛出异常
* 2. E element() 返回这个队列的头,但不删除
* 3. boolean offer(E e) 将指定的元素插入到队列中,如果不违反容量限制立即执行
* 4. E peek() 返回队头元素,但不删除,若队列为空,则返回null
* 5. E poll() 返回这个队列的队头元素并删除队头元素,若队列为空,则返回null
* 6. E remove() 返回队头元素并删
*/
Queue<Integer> queue = new LinkedList<>();
queue.add(1);//用add方式添加
queue.offer(2);//用offer添加
queue.offer(3);
System.out.println("队头元素:"+ queue.element()); //1
System.out.println("队头元素:"+queue.peek()); //1
System.out.println("删除元素:"+queue.poll()); //1
System.out.println("删除元素:"+queue.remove());//2
System.out.println("队头元素:"+ queue.element()); //3
二 实现List接口的类:
ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
LinkedList 底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素
Vector和ArrayList的区别
- Vector是线程安全的(只是加上 synchronized 关键字), ArrayList不是线程安全的,
- ArrayList不可以设置扩展的容量, 默认1.5倍; Vector可以设置, 默认2倍
- ArrayList无参构造函数中初始量为0; Vector的无参构造函数初始容量为10
适用场景分析:
1.Vector是线程同步的,所以它也是线程安全的,而ArrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用ArrayList效率比较高。
2.如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用Vector有一定的优势。
三 集合和数组的区别
- 长度区别:数组固定,集合可变
- 内容区别:数组可以使基本的数据类型,也可以是引用类型;集合只能是引用类型
- 元素内容:数组只能存储同一种类型;集合可以存储不同类型(map)
四 List中 remove(int index) 和 remove(Object o) 方法的区别
- Object remove(int index):该方法删除位于index结点处的元素
- boolean remove(Object o):删除特定Object元素
在大多数情况下,这两种方法的使用并不会存在歧义。
但是当我们将List的泛型参数设置为Integer时,我们只能使用前一种方式删除元素,也即是按结点位置的方法删除元素。因为无论传入的参数是int 类型还是Integer类型,都只会调用remove(int index)方法,而不会调用remove(Object o)方法。
五 ListIterator 和 Iterator的区别
1. Iterator迭代器包含的方法有:
- hasNext():如果迭代器指向位置后面还有元素,则返回 true,否则返回false
- next():返回集合中Iterator指向位置后面的元素
- remove():删除集合中Iterator指向位置后面的元素
2. ListIterator迭代器包含的方法有:
- add(E e): 将指定的元素插入列表,插入位置为迭代器当前位置之前
- hasNext():以正向遍历列表时,如果列表迭代器后面还有元素,则返回 true,否则返回false
- hasPrevious():如果以逆向遍历列表,列表迭代器前面还有元素,则返回 true,否则返回false
- next():返回列表中ListIterator指向位置后面的元素
- nextIndex():返回列表中ListIterator所需位置后面元素的索引
- previous():返回列表中ListIterator指向位置前面的元素
- previousIndex():返回列表中ListIterator所需位置前面元素的索引
- remove():从列表中删除next()或previous()返回的最后一个元素(有点拗口,意思就是对迭代器使用hasNext()方法时,删除ListIterator指向位置后面的元素;当对迭代器使用hasPrevious()方法时,删除ListIterator指向位置前面的元素)
- set(E e):从列表中将next()或previous()返回的最后一个元素返回的最后一个元素更改为指定元素e
3. 相同点
都是迭代器,当需要对集合中元素进行遍历不需要干涉其遍历过程时,这两种迭代器都可以使用。
4. 不同点
- 使用范围不同,Iterator可以应用于所有的集合,Set、List和Map和这些集合的子类型。而ListIterator只能用于List及其子类型。
- ListIterator有add方法,可以向List中添加对象,而Iterator不能。
- ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator不可以。
- ListIterator可以定位当前索引的位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
- 都可实现删除操作,但是ListIterator可以实现对象的修改,set()方法可以实现。Iterator仅能遍历,不能修改。
六 在java集合体系的位置
1. ArrayList
ArrayList list = new ArrayList();
List list = new ArrayList();
ArrayList是一个类,实现了List接口,List接口有继承了Colletion接口
2. LinkedList
LinkedList list = new LinkedList();
List list = new LinkedList();
LinkedList 是一个类,实现了List接口,Deque接口(双端队列),List接口有继承了Colletion接口,Deque接口继承了Queue接口,Queue接口继承了Colletion接口
3. Stack
Stack stack = new Stack();
Stack是一个类,继承了Vector类,Vector类实现了List接口,List接口有继承了Colletion接口。
Vector 是线程安全的动态数组,同 ArrayList 一样继承自 AbstractList 且实现了 List、RandomAccess、Cloneable、Serializable 接口。
内部实现依然基于数组,Vector 与 ArrayList 基本是一致的,唯一不同的是 Vector 是线程安全的,会在可能出现线程安全的方法前面加上 synchronized 关键字。
其和 ArrayList 类似,随机访问速度快,插入和移除性能较差(数组原因),支持 null 元素,有顺序,元素可以重复,线程安全。
4. Queue
Queue queue = new LinkedList();
Queue是作为Collection接口的一个子接口,而Deque继承了Queue接口,也就是说Deque拥有更多操作。不过需要注意的是,Queue和Deque都是接口,不能实例化对象,而LinkedList类实现了Deque接口,也就间接实现了Queue,所以我们可以用LinkedList来实现队列的操作。
七 Set接口
Set是继承Collection接口,因此具有其所有的方法。类 HashSet和类TreeSet实现了Set接口,LinkedHashSet类继承了HashSet类,并且实现了Set接口。
1. HashSet类
底层数据结构采用哈希表实现,元素无序且唯一,线程不安全,效率高,可以存储null元素,元素的唯一性是靠所存储元素类型是否重写hashCode()和equals()方法来保证的,如果没有重写这两个方法,则无法保证元素的唯一性。
具体实现唯一性的比较过程:
存储元素首先会使用hash()算法函数生成一个int类型hashCode散列值,然后和已经存储的元素的hashCode值比较。如果hashCode不相等,则所存储的两个对象一定不相等,此时存储当前的新的hashCode值处的元素对象;如果hashCode相等,存储元素的对象还是不一定相等,此时会调用equals()方法判断两个对象的内容是否相等。如果内容相等,那么就是同一个对象,无需存储;如果比较的内容不相等,那么就是不同的对象,就该存储了,此时就要采用哈希的解决地址冲突算法,在当前hashCode值处创建一个新的链表, 在同一个hashCode值的后面存储存储不同的对象,这样就保证了元素的唯一性。
Set的实现类的集合对象中不能够有重复元素,HashSet用一种算法来保证HashSet中的元素是不重复的。 HashSet采用哈希算法,底层用数组存储数据。默认初始化容量16,加载因子0.75
Object类中的hashCode()的方法是所有子类都会继承这个方法,这个方法会用Hash算法算出一个Hash(哈希)码值返回,HashSet会用Hash码值去和数组长度取模, 模(这个模就是对象要存放在数组中的位置)相同时才会判断数组中的元素和要加入的对象的内容是否相同,如果不同才会添加进去。
覆盖hashCode()方法的原则:
- 一定要让那些我们认为相同的对象返回相同的hashCode值
- 尽量让那些我们认为不同的对象返回不同的hashCode值,否则,就会增加冲突的概率。
- 尽量的让hashCode值散列开(两值用异或运算可使结果的范围更广)
相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成,我们应该为保存到HashSet中的对象覆盖hashCode()和equals()。
因为在将对象加入到HashSet中时,会首先调用hashCode方法计算出对象的hash值,接着根据此hash值调用HashMap中的hash方法,得到的值& (length-1)得到该对象在hashMap的transient Entry[] table中的保存位置的索引,接着找到数组中该索引位置保存的对象,并调用equals方法比较这两个对象是否相等,如果相等则不添加。
注意:要存入HashSet的集合对象中的自定义类必须覆盖hashCode(),equals()两个方法,才能保证集合中元素不重复。
在覆盖equals()和hashCode()方法时, 要使相同对象的hashCode()方法返回相同值,覆盖equals()方法再判断其内容。为了保证效率,所以在覆盖hashCode()方法时, 也要尽量使不同对象尽量返回不同的Hash码值。
如果数组中的元素和要加入的对象的hashCode()返回了相同的Hash值(相同对象),才会用equals()方法来判断两个对象的内容是否相同。
重要的方法
add(E e),添加的元素不可重复,是通过HashMap的put方法来保证的,往HashMap中put数据时,如果key是一样的,只会替换key对应的value,不会新插入一条数据。所以往HashSet中add相同的元素没有什么用,这里的相同是通过equals方法保证的。
remove(Object o), 从HashMap中移除一条数据。
还有比较总要的是size(), isEmpty(), contains(), clear()等。
注意由于Collection接口中没有get,set方法,故HashSet没有提供set(), get()方法。
2. LinkedHashSet
LinkedHashSet继承自HashSet,底层数据结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。线程不安全,效率高。唯一的区别是LinkedHashSet内部使用的是LinkHashMap。这样做的意义或者好处就是LinkedHashSet中的元素顺序是可以保证的,也就是说遍历序和插入序是一致的。
3. TreeSet
底层数据结构采用二叉树来实现,元素唯一且已经排好序。
唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性。
根据构造方法不同,分为自然排序(无参构造)和比较器排序(有参构造)。
- 自然排序要求元素必须实现Compareable接口,并重写里面的compareTo()方法,元素通过比较返回的int值来判断排序序列,返回0说明两个对象相同,不需要存储。
- 比较器排需要在TreeSet初始化是时候传入一个实现Comparator接口的比较器对象,或者采用匿名内部类的方式new一个Comparator对象,重写里面的compare()方法。
4. 小结
Set具有与Collection完全一样的接口,因此没有任何额外的功能,不像前面有两个不同的List。
实际上Set就是Collection,只是行为不同。(这是继承与多态思想的典型应用:表现不同的行为。)
Set 存入的每个元素都必须是唯一的,加入Set的元素必须定义equals()方法以确保对象的唯一性。
HashSet和TreeSet的区别
- 1.TreeSet 是二差树(红黑树的树据结构)实现的,Treeset中的数据是自动排好序的,不允许放入null值。
- 2.HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束。
- 3.HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例。
适用场景分析:HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。为快速查找而设计的Set,我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。
八 List和Set区别
List特点:元素有放入顺序,元素可重复,可以通过索引直接操作元素。和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,不能根据索引获取元素。检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 。另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。