LinkedList类
LinkedList与ArrayList不同,LinkedList是方便添加或删除的List。实际开发中对一个集合元素的添加和删除经常会涉及到首尾的操作。
- LinkedList的特点是添加和删除块,但是查询慢。这是因为LinkedList是链表在添加和删除元素时,只需要修改上一个节点记录的地址值即可;而对于查询则需要遍历到要查询的元素为止。
- ArrayList的特点是查询快,添加和删除慢。查询快是由于数组的索引支持,所以可以通过直接计算出元素的地址值,因此可以直接通过元素的地址值获取指定的元素;添加和删除慢是因为ArrayList在添加和删除元素时,在底层会先创建一个新数组,先需要堆数组原有的数组进行拷贝,其次在末尾进行添加或者删除元素。
LinkedList提供了大量首尾操作:
public void addFirst(E e);
public void addLast(E e);
public E getFirst();
public E getLast();
代码实例如下:
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> list=new LinkedList<String>();
//添加元素
list.add("Jack");
list.add("Rose");
list.add("Trump");
//获取元素
String name=list.get(1);
System.out.println("第二个元素:"+name);
//返回集合的迭代器
Iterator<String> it=list.iterator();
//利用迭代器便利
while(it.hasNext()){
String thisName=it.next();
System.out.println(thisName);
}
System.out.println("------------");
//使用增强for循环便利
for(String thisName:list){
System.out.println(thisName);
}
System.out.println(list);
//删除
list.remove(); //默认删除第一个元素 也可以通过索引删除指定元素或者删除首尾元素
System.out.println(list);
}
}
运行结果:
第二个元素:Rose
Jack
Rose
Trump
------------
Jack
Rose
Trump
[Jack, Rose, Trump]
[Rose, Trump]
LinkedList可支持栈结构
当然Java也提供专门用于栈结构的类Stack
LinkedList对于栈结构提供了如下的方法:
push 压栈
pop 弹栈
peek 查询出即将要出栈的元素(栈顶元素),检查有没有要弹出的元素,当栈为空时就返回空值。
代码实例:
public class StackDemo {
public static void main(String[] args) {
LinkedList<String> stack = new LinkedList<String>();
stack.push("Jack");
stack.push("Rose");
stack.push("Lisa");
System.out.println(stack);
//获取一个元素 此时,由于是栈结构 所以获取的是最后压栈的元素
String popName=stack.pop(); //这里获取的是Lisa元素
System.out.println(popName);
System.out.println(stack);
String peekName=stack.peek();
System.out.println(peekName);
//peek查看元素不会减少元素
System.out.println(stack);
}
}
运行结果:
[Lisa, Rose, Jack]
Lisa
[Rose, Jack]
Rose
[Rose, Jack]
注意这里输出栈时,是从栈顶到栈底依次输出
LinkedList可支持队列结构
LinkedList为支持队列元素提供了如下的方法:
offer 加入队列
poll 离开队列
peek 查询出即将要离开队列的元素(即队首元素),可检查有没有要离开队列的元素,当队列为空时返回空值null
代码实例:
public class QueueDemo {
public static void main(String[] args) {
LinkedList<String> queue = new LinkedList<String>();
queue.offer("Jack");
queue.offer("Rose");
queue.offer("Lisa");
System.out.println(queue);
//获取队首元素
String pollName=queue.poll();
System.out.println(pollName);
System.out.println(queue);
String peekName=queue.peek();
System.out.println(peekName);
System.out.println(queue);
//清空队列
queue.remove();
queue.remove();
//检查是否还有要离开的元素
String name=queue.peek();
System.out.println(name); //队列为空时 peek()返回的是空值null
}
}
运行结果:
[Jack, Rose, Lisa]
Jack
[Rose, Lisa]
Rose
[Rose, Lisa]
null
HashSet类
HashSet是Set接口的子类,不包括重复相同的元素,并且是无序的,且提供索引,所以不能通过索引获取元素,只能通过迭代器访问数据
HashSet下还有子类LinkedHashSet,是可预测迭代顺序的Set的集合。
代码实例:
public class HashSetDemo {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("Jack");
set.add("Lisa");
set.add("Rose");
set.add("Rose");
System.out.println(set); //输出是无序的
//获取元素,只能迭代器
Iterator<String> it = set.iterator();
while(it.hasNext()){
String name = it.next();
System.out.println(name);
}
System.out.println("============");
//使用增强for遍历set集合
for(String name:set){
System.out.println(name);
}
}
}
运行结果:
[Rose, Jack, Lisa]
Rose
Jack
Lisa
============
Rose
Jack
Lisa
注意这里输出结果是无序的,还要记住不能通过索引来访问只能使用迭代器来访问数据
HashSet的简单应用:使用HashSet求一个字符串中一共有几个不重复的字母,区分大小写,比如a,A是算两个字符
public class HashSetDemo2 {
public static void main(String[] args) {
String s;
System.out.println("请输入一个字符串:");
Scanner scanner = new Scanner(System.in);
s=scanner.nextLine();
//创建HashSet集合
HashSet<Character> set = new HashSet<>();
//将字符串拆分成一个个单个字符
char[] chars = s.toCharArray();
for(char ch:chars){
//判断是否是字母
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')){
set.add(ch);
}
}
System.out.println("在该字符串中总共有"+set.size()+"个不重复的字母。");
}
}
ArrayList中使用contains方法判断是否有重复元素
自定义类型Person是Object类的子类,所以Person具备equals方法,而contains方法会调用equals方法,依次与集合当中已经存在的Person对象比较。 当Person类没有重写equals方法时,直接使用Object类继承过来的equals方法,而该方法比较的是对象的地址值。当Person类重写equals方法后,就可以将比较规则由比较地址值改为比较属性值。
下面代码是没有重写equals的:
public class ArrayList_contains {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
Person jack = new Person("Jack", "18");
Person rose = new Person("Rose", "22");
Person p = new Person("Jack","18");
list.add(jack);
list.add(rose);
//这里没有重写equals方法 所以比较的是地址值 因此返回的是False 若重写equals方法比较的是name和age的成员变量则返回true
System.out.println(list.contains(p));
}
}
下面代码是Person类中重写equals方法:
@Override
public boolean equals(Object obj) {
Person p=(Person)obj;
if(p.getName()==getName()&&p.getAge()==getAge()){
return true;
}
return false;
}
重写之后在运行上面代码会发现返回的结果是true。
HashSet判断元素唯一性准则
Set集合不能添加重复元素,那其添加方法在添加是判断是否有重复元素,那其判断规则是怎么样的呢???
HashSet集合由于是无序的,其判断唯一的依据是元素类型的hashCode与equals方法返回的结果。
规则如下:
把对象加入到HashSet时,它会先使用对象的hashCode值来判断对象加入的位置;
如果此位置上没有其他对象存在,则判断元素不同,可存入新对象。
如果此位置有对象存在,先判断新元素与集合内已经有的旧元素的HashCode值:如果不同,判断为是不同的对象,可存入新对象;如果相同,在判断equals比较结果,返回true相同,则不可存入新对象;返回是的false则仍然不同,即可存入新对象。
所以,使用HashSet存储自定义类型,如果没有重写该类的hashCode与equals方法,则判断重复时,使用的地址值,如果想通过内容比较元素是否相同,则需要重写该类的hashCode与equals方法。
hashCode方法重写规则:
将对象的各个属性值的hashCode相加即是整个对象的HashCode值。
如果是基本类型,类似int,则直接返回int值就是该属性的hash值;
如果是引用类型,类似String,就调用该成员变量的hashCode方法返回该成员变量的hash值。
这样可以根据对象的内容返回hashCode值,从而可以根据hashCode判断元素是否唯一。
但是由于一些“碰巧的”情况下,可能出现内容不同但hashCode相同的情况,为了避免这些情况,我们要加入一些干扰系数。
可是加入干扰系数后,仍会出现一些“碰巧”的情况,所以我们还要进行equals的二次判断。
重写hashCode方法代码如下:
@Override
public int hashCode() {
int thisHashCode
thisHashCode+=name.hashCode()*17;
thisHashCode+=age.hashCode
return thisHashCo
}
Java集合框架之总结&体系图
单列集合体系图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K9xDr7kG-1615558587998)(image.png)]
Collection:所有单列集合的直接或间接接口,其指定了所有集合应该具备的基本功能。
List:元素可重复,有序,带索引。
Set:元素不可重复,无序,没有索引。
ArrayList:底层是数组结构,ArrayList的出现代替了Vector,增删慢,查看快。
LinkedList:底层是链表结构,同事对元素的增删操作效率高。
HashSet:底层是哈希表结构。在不重复的基础上无序。
LinkedHashSet:底层是哈希表结构结合链表结构,在不重复的基础上可预测迭代顺序。
集合的常见使用
在设计集合的使用时,经常定义对应的接口类型。在使用时,通过多态的方式给接口变量赋值,用于提高程序扩展性。
如:定义方法public void method(List list){},在调用方法是依据需求,传入ArrayList或LinkedList的对象。