collection接口
集合接口的继承关系:
—-| Collection 单列集合 的根接口
——-| List 如果是实现了List接口的集合类,该集合类具备的特点:有序,可重复。
——-| Set 如果是实现了Set接口的集合类,该集合类具备的特点: 无序,不可重复。
collection 接口方法
//增
add(E e);
addAll(Collection<? extends E> c);
//删
clear();
remove(Object o);
removeAll(Collection<?> c);
//交集
retainAll(Collection<?> c);
//判断
contains(Object o);
containsAll (Collection<?> c);
equals(Object o);
isEmpty();
//迭代器
iteratior();
//大小
size();
//转换为数组
toArray();
collection 迭代器
Collection—迭代的方法:
toArray()
iterator()迭代器的作用:就是用于抓取集合中的元素。
迭代器的方法:hasNext() 问是否有元素可遍历。如果有元素可以遍历,返回true,否则返回false 。 next() 获取元素... remove() 移除迭代器最后一次返回 的元素。
NoSuchElementException 没有元素的异常。
出现的原因: 没有元素可以被迭代了。。。
List接口
List接口中特有方法:
添加 add(int index, E element) addAll(int index, Collection<? extends E> c) 获取: get(int index) indexOf(Object o) lastIndexOf(Object o) subList(int fromIndex, int toIndex) 修改: set(int index, E element) 迭代 listIterator()
List接口中特有的方法具备的特点: 操作的方法都存在索引值。
只有List接口下面的集合类才具备索引值。其他接口下面的集合类都没有索引值。
listIterator()
ListIterator特有的方法:
hasPrevious() 判断是否存在上一个元素。 previous() 当前指针先向上移动一个单位,然后再取出当前指针指向的元素。 next(); 先取出当前指针指向的元素,然后指针向下移动一个单位。 add(E e) 把当前有元素插入到当前指针指向的位置上。 set(E e) 替换迭代器最后一次返回的元素。
迭代器注意事项
迭代器在变量元素的时候要注意事项: 在迭代器迭代元素的过程中,不允许使用集合对象改变集合中的元素个数,如果需要添加或者删除只能使用迭代器的方法进行操作。如果使用过了集合对象改变集合中元素个数那么就会出现ConcurrentModificationException异常。
迭代元素的过程中: 迭代器创建到使用结束的时间。
错误案例:ListIterator it = list.listIterator(); //获取到迭代器 list.add("aa");//改变元素个数,将会出现错误 it.next();
while(it.hasNext()){ System.out.print(it.next()+","); list.add("aa"); // add方法是把元素添加到集合的末尾处的。
正确使用:必须通过迭代器添加元素,因为迭代器指针会跳过通过迭代器添加的元素
while(it.hasNext()){ System.out.print(it.next()+","); it.add("aa"); // 把元素添加到当前指针指向位置 } //最后集合元素的输出 [张三, aa, 李四, aa, 王五, aa]
List接口的实现类
——| List 如果是实现了List接口的集合类,具备的特点: 有序,可重复。
—————| ArrayList ArrayList 底层是维护了一个Object数组实现的。 特点: 查询速度快,增删慢。
—————| LinkedList LinkedList 底层是使用了链表数据结构实现的, 特点: 查询速度慢,增删快。
—————| Vector(了解即可) 底层也是维护了一个Object的数组实现的,实现与ArrayList是一样的,但是Vector是线程安全的,操作效率低。
——| Set 如果是实现了Set接口的集合类,具备的特点: 无序,不可重复。
ArrayList
List的实现类主要有三种:ArrayList LinkedList 和 Vector(不重要)
ArrayList:
ArrayList 底层是维护了一个Object数组实现的, 特点: 查询速度快,增删慢。
什么时候使用ArrayList: 如果目前的数据是查询比较多,增删比较少的时候,那么就使用ArrayList存储这批数据。笔试题目: 使用ArrayList无参的构造函数创建一个对象时, 默认的容量是多少? 如果长度不够使用时又自增增长多少?
ArrayList底层是维护了一个Object数组实现的,使用无参构造函数时,Object数组默认的容量是10,当长度不够时,自动增长0.5倍。特有的方法:
ensureCapacity(int minCapaci上ty)//确保它至少能够容纳最小容量参数所指定的元素数 trimToSize()
LinkedList
特有方法:
addFirst(E e) addLast(E e) getFirst() getLast() removeFirst() removeLast()
LinkedList实现数据结构
1:栈 (1.6) : 主要是用于实现堆栈数据结构的存储方式。
先进后出 push() pop()
2:队列(双端队列1.5): 主要是为了让你们可以使用LinkedList模拟队列数据结构的存储方式。
先进先出 offer() poll()
返回逆序的迭代器对象
descendingIterator() 返回逆序的迭代器对象
Vector
笔试题: 说出ArrayLsit与Vector的区别?
相同点: ArrayList与Vector底层都是使用了Object数组实现的。不同点:
1. ArrayList是线程不同步的,操作效率高。
Vector是线程同步的,操作效率低。
2. ArrayList是JDK1.2出现,Vector是jdk1.0的时候出现的。
Set接口
- Set接口是无序的
无序: 添加元素 的顺序与元素出来的顺序是不一致的。 - 接口方法和collection 一致
Set接口的实现类
HashSet(依靠equals和HashCode的方法共同作用)
- 底层是使用了哈希表来支持的,特点: 存取速度快.
实现原理
往Haset添加元素的时候,HashSet会先调用元素的hashCode方法得到元素的哈希值 ,然后通过元素的哈希值经过移位等运算,就可以算出该元素在哈希表中的存储位置。情况1: 如果算出元素存储的位置目前没有任何元素存储,那么该元素可以直接存储到该位置上。
情况2: 如果算出该元素的存储位置目前已经存在有其他的元素了,那么会调用该元素的equals方法与该位置的元素再比较一次,如果equals返回的是true,那么该元素与这个位置上的元素就视为重复元素,不允许添加,如果equals方法返回的是false,那么该元素运行添加。
HashSet注意事项
HashSet是根据hashcode算出来的数值然后在哈希表中找相应的位置,如果某类是根据一个变量返回哈希值,如果修改了这个值,会影响remove的判断,如下@Override public int hashCode() { return this.name.hashCode(); } @Override public boolean equals(Object obj) { Book b = (Book)obj; return this.name.equals(b.name); }
public static void main(String[] args) { //不允许重复的书名存在。 HashSet<Book> books = new HashSet<Book>(); books.add(new Book("深入javaweb",34)); books.add(new Book("java神书",78)); //修改书名 Iterator<Book> it = books.iterator(); while(it.hasNext()){ Book b = it.next(); if(b.name.equals("java神书")){ b.name = "java编程思想"; } } //为什么修改名字之后不能删除了呢? books.remove(new Book("java神书",78)); System.out.println("集合的元素:"+ books); }
TreeSet
treeSet要注意的事项:
- 往TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。
往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,那么该元素所属的类必须要实现Comparable接口,把元素的比较规则定义在compareTo(T o)方法上。
如果比较元素的时候,compareTo方法返回的是0,那么该元素就被视为重复元素,不允许添加.(注意:TreeSet与HashCode、equals方法是没有任何关系。)
往TreeSet添加元素的时候, 如果元素本身没有具备自然顺序 的特性,而元素所属的类也没有实现Comparable接口,那么必须要在创建TreeSet的时候传入一个比较器。
往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类已经实现了Comparable接口, 在创建TreeSet对象的时候也传入了比较器, 那么是以比较器的比较规则优先使用。
如何自定义定义比较器: 自定义一个类实现Comparator接口即可,把元素与元素之间的比较规则定义在compare方法内即可。
自定义比较器的格式 :
class 类名 implements Comparator{ }
推荐使用:使用比较器(Comparator)。
TreeSet是可以对字符串进行排序 的, 因为字符串已经实现了Comparable接口。字符串的比较规则:
情况一: 对应位置有不同的字符出现, 就比较的就是对应位置不同的字符。
情况 二:对应位置上 的字符都一样,比较的就是字符串的长度。
set集合中的equals和hashCode (转)
- hashCode默认是计算出对象的内存地址,可以自己改写,例如String类就改写了
- Java 对equals方法和hashCode方法是这样规定的:
- 如果两个对象相同(使用equals比较,所以如果改写equals方法,一般也要改写hashCode方法),那么它们的hashCode值一定要相同;
- 如果两个对象的的hashCode相同,它们并不一定相同。(因为对于散列表来说每个位置是可能重复的)
- hashCode和equals的比较:
equals的方法是给用户调用的,如果你想判断两个对象是否相等,你可以重写equals方法
hashCode方法一般不会让用户调用,比如hashmap中,由于Key是不可以重复的(equals和hashCode只要有一个不等即使不重复),它在判断key是不是重复的时候就判断了hashcode方法,而且也用到了equals方法。
创建String对象过程的内存分配(转)
String s = “abc”;
在class文件被JVM装载到内存中,JVM会创建一块String Pool,当执行这句话时,JVM首先在Pool中查看是否存在相同的字符串(使用equals比较),如果存在该对象,则不用创建新的字符串,而直接使用String Pool中已存在的对象“abc,然后将引用s指向String Pool中创建的对象。String s = new String(“abc”);
首先在Pool中查看是否存在对象”abc”, 没有则创建,然后又在Heap创建一个新的对象,并赋给s.如果Pool中存在,则不创建,然后在Heap中创建的对象.
即New一个String对象 相当于创建了两个对象(常量池中一个 heap中一个)
注意:new 两个String对象,即使内容相同,内存地址也不相同
Tips
循环可以加标号
outer: while(true)
…
break outer;indexof 返回的是第一个
- 刚刚初始化的迭代器是不指向任何元素的
Random random = new random();
Random.nextint(大小) 这个范围不用减1,范围是0-大小字符串转Int
Integer.parseInt
小案例:使用LinkedList实现洗牌
package cn.itcast.list;
import java.util.LinkedList;
import java.util.Random;
/*
需求: 使用LinkedList存储一副扑克牌,然后实现洗牌功能。
*/
//扑克类
class Poker{
String color; //花色
String num; //点数
public Poker(String color, String num) {
super();
this.color = color;
this.num = num;
}
@Override
public String toString() {
return "{"+color+num+"}";
}
}
public class Demo2 {
public static void main(String[] args) {
LinkedList pokers = createPoker();
shufflePoker(pokers);
showPoker(pokers);
}
//洗牌的功能
public static void shufflePoker(LinkedList pokers){
//创建随机数对象
Random random = new Random();
for(int i = 0 ; i <100; i++){
//随机产生两个索引值
int index1 = random.nextInt(pokers.size());
int index2 = random.nextInt(pokers.size());
//根据索引值取出两张牌,然后交换两张牌的顺序
Poker poker1 = (Poker) pokers.get(index1);
Poker poker2 = (Poker) pokers.get(index2);
pokers.set(index1, poker2);
pokers.set(index2, poker1);
}
}
//显示扑克牌
public static void showPoker(LinkedList pokers){
for(int i = 0 ; i<pokers.size() ; i++){
System.out.print(pokers.get(i));
//换行
if(i%10==9){
System.out.println();
}
}
}
//生成扑克牌的方法
public static LinkedList createPoker(){
//该集合用于存储扑克对象。
LinkedList list = new LinkedList();
//定义数组存储所有的花色与点数
String[] colors = {"黑桃","红桃","梅花","方块"};
String[] nums = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
for(int i = 0 ; i < nums.length ; i++){
for(int j = 0 ; j<colors.length ; j++){
list.add(new Poker(colors[j], nums[i]));
}
}
return list;
}
}