Java基础day16-Vector类-Stack类-Collection子接口Set接口
目录
一、Vector类
1.Vector底层是用数组实现的。
2.Vector与ArrayList类的区别
(1)线程安全性
(2)数组初始化
(3)扩容
二、Stack类
三、子接口Set接口
1.HashSet
1.1 初始化
1.2 数据结构
1.3 常用方法
1.4 特点总结
2. LinkedHashSet
2.1 LinkedHashSet:有序且唯一
2.2 特点总结
3.TreeSet
3.1初始化
3.2 数据结构
3.3 注意
3.4 特点总结
4.常见问题
4.1Set集合如何过滤重复元素?
4.2 为什么重写 hashCode () 时,必须重写 equals ()?
4.3 为什么会产生哈希冲突?
一、Vector类
1.Vector底层是用数组实现的。
相关的方法都加了同步检查,因为“线程安全,效率低”。
比如,indexOf方法就增加了synchronized同步标记。
public class Demo01 {public static void main(String[] args) {//底层elementData数组//1.Vector的无参构造,数组的初始化容量为10,每次容量不足,扩容为原来的2倍Vector<String> v1=new Vector<>();//2.以指定的数组容量作为初始化Vector<String> v2=new Vector<>(20);//3.初始化提供数组的初始化容量和每次的扩容值,当容量不足时,以指定的扩容值作为扩容操作Vector<String> v3=new Vector<>(2,10);for (int i = 0; i <6 ; i++) {v3.add("张三");v3.add("李四");}System.out.println(v3);}
}
2.Vector与ArrayList类的区别
(1)线程安全性
Vector比ArrayList线程安全性高,因为Vector的方法前都被synchronized(锁)所修饰,而ArrayList没有。但ArrayList比Vector的性能高。
(2)数组初始化
ArrayList使用无参构造方法时的初始容量为0,是在添加第一个元素的时候才将容量扩充为10。
Vector使用无参构造方法是在刚开始的时候就将初始容量设置为10。
(3)扩容
ArrayList的扩容机制:
无参构造的对象初始容量为0,第一次添加元素时,扩容内存大小容量为10。
有参构造时,根据参数设置内存大小。
最大容量为Integer.MAX_VALUE - 8 到 Inter.Max_Value之间,如果超出。则输出OutMemoryError错误。
在进行 arrayList1.add()时,如果数组容量不足时,按照原容量的1.5倍进行扩容增长。
Vector的扩容机制:
使用无参构造方法的对象初始容量为10,当容量不足时,按照原容量的2倍进行扩容。
使用单参构造方法的时候,容量不足时,是根据给定的初始容量来进行2倍的扩容。
使用双参构造方法,初始化提供数组的初始化容量和每次的扩容值,当容量不足时,以指定的扩容值作为扩容操作。
二、Stack类
1.stack是基于FILO先进后出实现的栈。
2.继承自Vector类。
public class Demo01 {public static void main(String[] args) {//栈:先进后出 FILOStack<String> s1=new Stack<>();//入栈s1.push("x西安");s1.push("b北京");s1.push("c长沙");s1.push("x西安");System.out.println(s1);//获取到栈顶元素,并让栈顶元素出栈String item=s1.pop();System.out.println(item);System.out.println(s1);//获取栈顶元素,元素不出栈String item1=s1.peek();System.out.println(item1);System.out.println(s1);}
}输出:
[x西安, b北京, c长沙, x西安]
x西安
[x西安, b北京, c长沙]
c长沙
[x西安, b北京, c长沙]
3.使用栈进行字符串的逆序输出。
public class Demo02 {public static void main(String[] args) {System.out.println(reverse("sjwhdshbduw"));}public static String reverse(String str){Stack<Character> s1=new Stack<>();//字符串中的元素入栈char[] c1=str.toCharArray();for (char c:c1) {s1.push(c);}StringBuilder sb=new StringBuilder();//出栈while(!s1.isEmpty()){sb.append(s1.pop());}return sb.toString();}
}
三、子接口Set接口
set无下标概念。
//Collection:
//1.接口的方法:
// 添加: boolean add(E e);
// boolean addAll(Collection<?extends E> c);
// 查看: int size()
// boolean isEmpty();
// boolean contains(Object o);
// Iterator<E> iterator();
// Object[] toArray();
// <T> T[] toArray(T[] a);
// 移除: boolean remove(Object o);
// boolean removeAll(Collection<?> c);
// boolean retainAll(Collection<?> c);
// void clear();
//
//2.Collection接口的子接口
// List: 有序可重复
// boolean addAll(int index, Collection<? extends E> c);
// E get(int index);
// E set(int index, E element);
// void add(int index, E element);
// E remove(int index);
// int indexOf(Object o);
// ListIterator<E> listIterator();
// ListIterator<E> listIterator(int index);
// List<E> subList(int fromIndex, int toIndex);
// Set:
// HashSet:无序,且唯一
实现类如下:
1.HashSet
1.1 初始化
(1)public HashSet()--说明: 创建一个默认为空的HashSet。
(2)public HashSet(int initialCapacity, float loadFactor)--说明:创建一个HashSet容器,initialCapacity表示设置初始容量大小,loadFactor表示负载因子, 当容量达到最大容量*负载因子时,需要进行扩容,这属于HashMap的知识。
(3)public HashSet(Collection<? extends E> c)--说明:创建一个容器内容为c的集合。
1.2 数据结构
底层是HashMap。
1.3 常用方法
(1)public boolean add(E e)--说明:向集合中添加元素。
(2)public boolean remove(Object o)--说明:集合中删除元素。
(3)public void clear()--说明:清空集合元素。
(4)public int size()--说明:返回集合中元素的数量。
public class Demo01 {public static void main(String[] args) {HashSet<String> hs1 = new HashSet<>();//添加元素集合boolean b1 = hs1.add("曹操");hs1.add("周瑜");hs1.add("晁盖");boolean b2 = hs1.add("曹操");System.out.println("第一次添加元素:"+b1);System.out.println("第二次添加元素:"+b2);//快速生成list集合List<String> list = Arrays.asList("小乔","大乔","诸葛亮","晁盖");boolean b3 = hs1.addAll(list); //添加元素集合,只要成功的添加一个元素,返回值就为trueSystem.out.println("添加集合是否成功:"+b3);System.out.println(hs1);System.out.println("元素个数为:"+hs1.size());System.out.println("元素是否存在:"+hs1.contains("小乔"));System.out.println("元素是否为空:"+hs1.isEmpty());//使用迭代器进行遍历Iterator<String> itor = hs1.iterator();while(itor.hasNext()){System.out.print(itor.next()+" ");}for (String str:hs1) {System.out.print(str+" ");}}
}
//输出
第一次添加元素:true
第二次添加元素:false
添加集合是否成功:true
[大乔, 诸葛亮, 曹操, 晁盖, 周瑜, 小乔]
元素个数为:6
元素是否存在:true
元素是否为空:false
大乔 诸葛亮 曹操 晁盖 周瑜 小乔
大乔 诸葛亮 曹操 晁盖 周瑜 小乔
public class Demo02 {public static void main(String[] args) {HashSet<String> hs1 = new HashSet<>();hs1.addAll(Arrays.asList("小乔", "大乔", "诸葛亮", "晁盖"));System.out.println("初始化为:" + hs1);// 删除boolean b1 = hs1.remove("小乔");System.out.println("删除元素是否成功:" + b1);System.out.println(hs1);// 清空hs1.clear();System.out.println(hs1);}
}
//输出
初始化为:[大乔, 诸葛亮, 晁盖, 小乔]
删除元素是否成功:true
[大乔, 诸葛亮, 晁盖]
[]
HashSet排序例题:
public class Book {private String bookName;private String authorName;private int pageSize;private double price;public Book(String bookName, String authorName, int pageSize, double price) {this.bookName = bookName;this.authorName = authorName;this.pageSize = pageSize;this.price = price;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Book book = (Book) o;return pageSize == book.pageSize &&Double.compare(book.price, price) == 0 &&Objects.equals(bookName, book.bookName) &&Objects.equals(authorName, book.authorName);}@Overridepublic int hashCode() {return Objects.hash(bookName, authorName, pageSize, price);}@Overridepublic String toString() {return "Book{" +"bookName='" + bookName + '\'' +", authorName='" + authorName + '\'' +", pageSize=" + pageSize +", price=" + price +'}'+'\n';}
}
public class Demo04 {public static void main(String[] args) {//去重原理//1.根据hashcode查看是否存在相同的hashcode,如果无,则认为不重复,直接放//2.如果存在相同的hashcode,则进行内容的比较(equals),不同,认为不同,则放//如果hashcode和equals完全相同,则认为相同元素,不放HashSet<Book> set =new HashSet<>();Book b1=new Book("明朝那些事1","当年明月1",1231,89.0);Book b2=new Book("明朝那些事2","当年明月2",1232,90.0);Book b3=new Book("明朝那些事3","当年明月3",1233,88.0);Book b4=new Book("明朝那些事1","当年明月1",1231,89.0);set.add(b1);set.add(b2);set.add(b3);set.add(b4);System.out.println(set);}
}
1.4 特点总结
(1)元素是没有重复的,而且是无序的
(2)允许有null值
(3)是无序的,即不会记录插入的顺序
(4)不是线程安全的,如果多个线程尝试同时修改HashSet,则最终结果是不确定的。
2. LinkedHashSet
2.1 LinkedHashSet:有序且唯一
public class Demo05 {public static void main(String[] args) {//LinkedHashSet 有序且唯一HashSet<String> hs1 = new LinkedHashSet<>();//添加元素集合hs1.add("曹操");hs1.add("周瑜");hs1.add("晁盖");hs1.add("曹操");System.out.println(hs1);}
}
2.2 特点总结
(1)LinkedHashSet的底层使用LinkedHashMap存储元素。
(2)LinkedHashSet是有序的,维护了元素的插入顺序。
(3)LinkedHashSet是不支持按访问顺序对元素排序的,只能按插入顺序排序。
3.TreeSet
3.1初始化
(1)TreeSet()--说明: 构造一个空的有序set集合。
(2)public TreeSet(Comparator<? super E> comparator)--说明:构造一个传入排序规则的有序Set集合。
(3)public TreeSet(Collection<? extends E> c)--说明:构造一个集合元素为c的有序Set集合。
3.2 数据结构
TreeSet实际上是TreeMap实现的,底层用到的数据结果是红黑树。当我们构造TreeSet时;若使用不带参数的构造函数,则TreeSet的使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。
3.3 注意
如果自定义类没有实现comparable接口,重写comparaTo()方法,会报java.lang.ClassCastException: person cannot be cast to java.lang.Comparable,java.lang.Comparable说明自定义类需要实现comparable接口,重写comparaTo()方法。
例如1:
public class Book implements Comparable<Book>{private String bookName;private String authorName;private int pageSize;private double price;public Book(String bookName, String authorName, int pageSize, double price) {this.bookName = bookName;this.authorName = authorName;this.pageSize = pageSize;this.price = price;}public String getBookName() {return bookName;}public void setBookName(String bookName) {this.bookName = bookName;}public String getAuthorName() {return authorName;}public void setAuthorName(String authorName) {this.authorName = authorName;}public int getPageSize() {return pageSize;}public void setPageSize(int pageSize) {this.pageSize = pageSize;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Book book = (Book) o;return pageSize == book.pageSize &&Double.compare(book.price, price) == 0 &&Objects.equals(bookName, book.bookName) &&Objects.equals(authorName, book.authorName);}@Overridepublic int hashCode() {return Objects.hash(bookName, authorName, pageSize, price);}@Overridepublic String toString() {return "Book{" +"bookName='" + bookName + '\'' +", authorName='" + authorName + '\'' +", pageSize=" + pageSize +", price=" + price +'}'+'\n';}@Overridepublic int compareTo(Book o) {int result = this.pageSize - o.pageSize;if (result==0){return this.bookName.compareTo(o.bookName);}return result;}
}
public class Demo06 {public static void main(String[] args) {//TreeSet是可排序的,且唯一的//在使用TreeSet时需要注意,一定要提供排序规则给对象(泛型类实现Comparable接口或者对象提供Comparator对象)//1.无参的构造方法,排序规则使用泛型类型这个类的定义排序规则//2.有参构造TreeSet(Comparator comparator)排序比较规则按照传入的参数的规则为准Set<Book> set =new TreeSet<>();Book b1=new Book("明朝那些事1","当年明月1",1231,89.0);Book b2=new Book("明朝那些事2","当年明月2",1232,90.0);Book b3=new Book("明朝那些事3","当年明月3",1233,88.0);Book b4=new Book("明朝那些事1","当年明月1",1231,89.0);set.add(b1);set.add(b2);set.add(b3);set.add(b4);System.out.println(set);}
}
public class Demo07 {public static void main(String[] args) {//如果泛型类型不是Comparable类型,必须要提供一个Comparator对象提供比较规则----匿名内部类Set<Book> set =new TreeSet<>(new Comparator<Book>() {@Overridepublic int compare(Book o1, Book o2) {//按照书的价格进行排序if(o1.getPrice()>o2.getPrice()){return 1;}else if(o1.getPrice()==o2.getPrice()){return 0;}else {return -1;}}});Book b1=new Book("明朝那些事1","当年明月1",1231,89.0);Book b2=new Book("明朝那些事2","当年明月2",1232,90.0);Book b3=new Book("明朝那些事3","当年明月3",1233,88.0);Book b4=new Book("明朝那些事1","当年明月1",1231,89.0);set.add(b1);set.add(b2);set.add(b3);set.add(b4);System.out.println(set);}
}
例如2:
public class Demo08 {public static void main(String[] args) {TreeSet<String> set=new TreeSet<>();set.add("ab");set.add("abc");set.add("ac");set.add("ad");set.add("bc");set.add("bd");set.add("cd");set.add("cd");System.out.println(set);TreeSet<Integer> set1=new TreeSet<>();set1.add(12);set1.add(15);set1.add(66);set1.add(88);set1.add(45);System.out.println(set1);}
}
//输出
[ab, abc, ac, ad, bc, bd, cd]
[12, 15, 45, 66, 88]
3.4 特点总结
(1)最大的特点就是一个可排序-的去重集合容器。
(2)集合中的元素不保证插入顺序,而是默认使用元素的自然排序(字典排序),不过可以自定义排序器。
(3)jdk8以后,集合中的元素不可以是null。
(4)集合不是线程安全。
(5)相对于HashSet,性能更差。
4.常见问题
4.1Set集合如何过滤重复元素?
在 Java 中,Set
接口的常见实现类,如 HashSet
、TreeSet
和 LinkedHashSet
,过滤重复元素的方式各有不同:
(1)HashSet:它基于哈希表实现。当向 HashSet
中添加元素时,会先调用元素的 hashCode()
方法获取哈希值,根据哈希值确定元素在哈希表中的存储位置(桶的位置)。如果该位置没有元素,就直接将元素放入;如果该位置已经有元素,就会调用新元素和该位置已有元素的 equals()
方法进行比较,如果 equals()
方法返回 true
,就认为是重复元素,不会再次添加。
(2)TreeSet:TreeSet
是基于红黑树实现的。当添加元素时,会根据元素的自然顺序(元素实现了 Comparable
接口)或自定义顺序(通过构造函数传入 Comparator
),将元素插入到红黑树的合适位置。在插入过程中,如果发现要插入的元素和树中已有的某个元素比较结果相等(对于 Comparable
,是 compareTo()
方法返回 0;对于 Comparator
,是 compare()
方法返回 0 ),就认为是重复元素,不会插入。
(3)LinkedHashSet:LinkedHashSet
继承自 HashSet
,它在 HashSet
基于哈希表过滤重复元素的基础上,还使用双向链表维护元素的插入顺序。判断重复元素的逻辑和 HashSet
一致,先通过 hashCode()
方法确定位置,再用 equals()
方法判断是否重复。
4.2 为什么重写 hashCode () 时,必须重写 equals ()?
这是由 Java 中对象的哈希机制和集合类的工作原理决定的:
(1)哈希表的工作机制:在使用哈希表相关的集合(如 HashSet
、HashMap
)时,首先通过 hashCode()
方法计算元素的哈希值,来快速定位元素在哈希表中的存储位置。如果两个对象的哈希值不同,它们在哈希表中就会存储在不同的位置。但如果两个对象的哈希值相同,就需要进一步通过 equals()
方法判断这两个对象是否真的相等。
(2)保证一致性:如果只重写 hashCode()
而不重写 equals()
,可能会出现两个在逻辑上相等(应该被视为重复元素)的对象,由于 equals()
方法没有被正确重写,导致 equals()
方法返回 false
,而它们的 hashCode()
方法返回了相同的哈希值。这样在哈希表相关集合中,这两个本应相等的对象就会被错误地当作不同元素存储,违反了集合对元素唯一性的要求。反之,如果重写了 equals()
而不重写 hashCode()
,可能会出现两个相等的对象哈希值不同,导致在哈希表中被存放在不同位置,也会出现逻辑错误。
4.3 为什么会产生哈希冲突?
哈希冲突是指不同的输入数据经过哈希函数计算后,得到了相同的哈希值。产生哈希冲突的原因主要有以下几点:
(1)哈希函数的设计特点:哈希函数是将任意长度的数据映射为固定长度的哈希值。由于输入数据的取值范围往往是无限或者非常大的,而哈希值的取值范围是有限的(例如,int
类型的哈希值取值范围是 -2147483648 到 2147483647 )。根据鸽巢原理,当处理的输入数据量超过哈希值的取值范围时,必然会出现不同的输入数据得到相同哈希值的情况。
(2)数据分布不均匀:即使输入数据量没有超过哈希值的取值范围,如果数据分布不均匀,某些哈希值对应的输入数据过多,也容易导致哈希冲突。例如,使用一个简单的哈希函数,对字符串的长度进行取模运算得到哈希值,如果大部分字符串长度集中在某些数值附近,就会使这些哈希值对应的位置产生冲突。
(3)哈希函数的局限性:现有的哈希函数很难做到绝对的一一映射。虽然优秀的哈希函数会尽量让哈希值均匀分布,但仍然无法完全避免不同输入产生相同哈希值的情况。