在Java的类集里面(java.util包)提供了两个最为核心的接口:Collection接口、Map接口。其中Collection接口的操作形式与之前编写链表的操作形式类似,每一次进行数据操作的时候只能够对单个对象进行处理。
一、Collection接口
Collection是单个集合保存的最大父接口。
用于表示任何对象或元素集合、接口,很少直接使用Collection接口,Collection接口只是一个存储数据的标准,并不能区分存储类型,比如存放的数据需要区分重复与不重复。在实际中会使用Collection接口派生出的子接口:List(允许数据重复)、Set(不允许数据重复),一共三个子接口还有Queue,在进行集合处理的时候,优先考虑List接口。定义:public interface Collection<E> extends Iterable<E>
,add()和iterator()方法最重要,子接口中都有
二、List接口(优先考虑)
定义:public interface List<E> extends Collection<E>
该接口在Collection接口的基础上扩充了俩个重要的方法public E get(int index)
根据索引取得数据和public E set(int index, Eelement)
修改数据,在List接口下有三个常用子类:ArrayList(数组链表)、Vector(向量、矢量)、LinkedList(单链表)。
//1.ArrayList子类优先考虑
public class ListDemo {
public static void main(String[] args) {
//此时集合只能保存String类型
List<String> list = new ArrayList<>();
//可以存放重复数据
System.out.println(list.size() + "、" + list.isEmpty());
list.add("Hello");
list.add("Hello");
list.add("java");
System.out.println(list);
System.out.println(list.size() + "、" + list.isEmpty());
System.out.println(list.remove("Hello"));
System.out.println(list.contains("java"));
System.out.println(list);
for (int i = 0; i < list.size(); i++) {
//根据索引取得数据
//这是List接口扩充的方法
System.out.println(list.get(i));
}
//如果现在操作的是Collection接口,只能将集合变为对象数组操作,一般不用Collection接口
//Object向下转型为ArrayList会产生 java.lang.ClassCastException,toArray()是Object方法
// ArrayList[] result = (ArrayList[]) list.toArray();
// System.out.println(Arrays.toString(result));
Collection<String> l = new ArrayList<>();
Object[] r = l.toArray();
System.out.println(Arrays.toString(r));
//2.旧的子类Vector使用较少,使用方法差不多
List<String> l=new Vector<>();
//3.LinkList子类
//向父接口转型使用形式和之前的没区别
// List<String> l=new LinkList<>();
l.add("l");
l.add("o");
System.out.println(l);
l.remove("o");
System.out.println(l);
System.out.println(l.size()+"\n"+l.contains("l");
}
}js
集合操作简单java类时,对于remove()、contains()方法需要equals()方法支持
1.请解释ArrayList与Vector区别
- 历史时间:ArrayList是从JDK1.2提供的,而Vector是从JDK1.0就提供了。
- 处理形式:ArrayList是异步处理,性能更高;Vector是同步处理,性能较低。
- 数据安全:ArrayList是非线程安全;Vector是线程安全。
- 输出形式:ArrayList支持Iterator、ListIterator、foreach;Vector支持Iterator、ListIterator、foreach、Enumeration。
在以后使用的时候优先考虑ArrayList,因为其性能更高,实际开发时很多时候也是每个线程拥有自己独立的集合资源。
2.解释ArrayList与LinkedList区别
1.观察ArrayList源码,可以发现ArrayList里面存放的是一个数组,DEFAULT_CAPACITY = 10;
,如果实例化此类对象时传入了数组大小,则里面保存的数组就会开辟一个定长的数组,但是后面再进行数据保存的时候发现数组个数不够了会进行数组动态扩充。 所以在实际开发之中,使用ArrayList最好的做法就是设置初始化大小。
2 .LinkedList:是一个纯粹的链表实现,与之前编写的链表程序的实现基本一样(ArrayList性能高)。
3.总结:
(1)这三个类都是List接口下的常用子类,其中ArrayList、Vector基于数组实现,LinkedList基于双向链表实现;ArrayList时间复杂度为1,而LinkedList的复杂度为n。在大部分场合一般考虑使用ArrayList,默认情况下这个更快,比LinkedList快,可以看源码的行数
(2)ArrayList采用懒加载策略,在第一次添加元素时初始化内部数组(大小为10).ArrayList扩容为原先数组的1.5倍,采用异步处理,线程不安全,性能较高。默认的插入删除操作ArrayList比较快,代码行数少,指定位置的插入删除会使用LinkedList
(3)Vector在产生对象时初始化大小为10的内部数组,Vector扩容为原先数组的2倍,Vector采用synchronized修饰常用的增删改查方法,线程安全,性能较低(读读互斥),Java提供的栈实现Stack是Vector子类,Stack中的方法有的也加了锁
(4)在任意位置的插入与删除会考虑使用LinkedList,Queue接口子类实现。
三、set集合接口
- 定义:
public interface Set<E> extends Collection<E>
,Set接口并没有对Collection接口进行扩充,而List对Collection进行了扩充。因此,在Set接口中没有get()方法。 - 在Set子接口中有两个常用子类:HashSet(无序存储)、TreeSet(有序存储)
- Set内部实际就是Map,保存的单个元素存储在Map的key中;我们可以看一下源码
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap<E,Object> map; //E就是Set
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
- Set不允许数据重复
TreeSet排序分析:
- 此时要想进行排序实际上是针对于对象数组进行的排序处理,而如果要进行对象数组的排序,对象所在的类一定要实现Comparable接口并且覆写compareTo()方法,只有通过此方法才能知道元素之间的大小关系,最后升序输出结果;所有属性必须全部进行比较操作。
- 元素要想保存到TreeSet中,要么元素本身所在的类实现Comparable,要么通过外部传入一个比较器(外部排序).
1. TreeSet使用升序排列
Set<String> l=new TreeSet<>();
l.add("p");
l.add("o");
System.out.println(l);//o p
2. HashSet无序排列
Set<String> lo=new HashSet<>();
lo.add("p");
lo.add("o");
System.out.println(lo);//p o
重复元素判断:HashSet判断两个对象是否重复:equals与hashCode
HashSet分析:
- Hash是无序的不能保证元素的排列顺序
- 不是同步的
由于HashSet跟Comparable没有任何关系,使用它判断重复元素的时候使用的是Object类中的方法
- hash码: public native int hashCode();取得任意一个对象的哈希码
- 对象比较:public boolean equals(Object obj);比较两个对象是否相等
- 在Java中进行对象比较的操作有两步:第一步要通过一个对象的唯一编码找到一个对象的信息,当编码匹配之后再调用equals()方法进行内容的比较。
- 如果要想标识出对象的唯一性,一定需要equals()与hashCode()方法共同调用,对象判断必须两个方法equals()、hashCode()返回值都相同才判断为相同。
- hashCode返回值相等的两个对象,equals不一定相等,equals返回值相等的两个对象,hashCode一定相等;想像一下x 与 f(x)函数关系
//覆写hashCode()与equals()方法消除重复
class Person implements Comparable<Person> {
private String name;
private Integer age;
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
if (this.age > o.age) {
return 1;
} else if (this.age < o.age) {
return -1;
} else {
return this.name.compareTo(o.name);
}
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Person person = (Person) obj;
return Objects.equals(name, person.name) &&
Objects.equals(age, person.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class HashCodeTest {
public static void main(String[] args) {
Set<Person> set = new HashSet<>();
set.add(new Person("张三", 2));
set.add(new Person("张三", 2));
set.add(new Person("李四", 6));
System.out.println(set);
}
}
建议
保存自定义对象的时候使用List接口;
保存系统类信息的时候使用Set接口(避免重复)
四、栈与队列
Stack栈:在Java集合中提供有Stack类,这个类是Vector的子类。需要注意的是,使用这个类的时候使用的不是Vector类中的方法,并且在使用时不要进行向上转型。因为要操作的方法不是由List定义的,而是由Stack定义的。
- 入栈 :
public E push(E item)
- 出栈 :
public synchronized E pop()
- 观察栈顶元素 :
public synchronized E peek()
//1.stack栈
Stack<String> stack = new Stack<>();
//入栈
stack.push("A");
stack.push("B");
stack.push("C");
//观察出入栈操作,C
System.out.println(stack.peek());
System.out.println(stack.pop());//C
System.out.println(stack.pop());//B
System.out.println(stack.pop());//A
//如果栈已经空了,那么再次出栈就会抛出空栈异常
//java.util.EmptyStackException
//System.out.println(stack.pop());
Stack先进后出,与之对应的Queue是先进先出。
Queue队列:
定义:Queue接口有一个子类LinkedList,
(1)检索并删除此队列的头
public E poll();
如果此队列为空,则返回 null
public E remove()
此方法与poll不同之处在于,如果此队列为空,它将抛出异常
(2)检索但不删除此队列的头部
E element()
如果此队列为空,它将抛出异常。
E peek()
如果此队列为空,则返回 null
(3)将指定的元素插入到此队列中
boolean add(E e)
,成功的话返回true
boolean offer(E e)
此方法通常优于add(E) ,这可能无法仅通过抛出异常来插入元素。
//2.队列,可以起到缓冲作用
Queue<String> queue = new LinkedList<>();
queue.add("A");
queue.add("B");
queue.add("C");
//返回位于Queue开始处的对象,但不将其移除
System.out.println(queue.peek());//A
System.out.println(queue.poll());//A
System.out.println(queue.poll());//B
System.out.println(queue.poll());//C
// 取完数据继续poll返回null
System.out.println(queue.poll());