集合框架2

四、List接口
List接口是Collection接口的一个子接口。

  1. List接口概述
    鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组。

List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。这和数组特别相像,因此也常将List接口称作动态数组,长度可变。

JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector。

  1. ArrayList、LinkedList、Vector的异同
    相同之处:

ArrayList/LinkedList/Vector三个都是List接口的实现类,其对象中的元素都具有List集合类的特征:元素有序、且可重复。

不同之处:

Vector类是最早用于存储长度可变的数组元素的类,它在List接口出现之前就已将存在了。而ArrayList/LinkedList都是List接口出现后作为List接口的实现类存在的。List接口出现后,三者都作为其实现类存在。
ArrayList是List接口的主要实现类,它是线程不安全的,但效率高。其底层是基于动态数组Object[] elementData存储数据的。
LinkedList也是List接口的实现类,也是线程不安全的。其底层是基于链表存储的。对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。
Vector作为List的古老实现类,它是线程安全的。其底层是基于动态数组Object[] elementData存储数据的。它与ArrayList唯一的区别在于Vector是同步类,属于强同步类,因此效率较低。Vector还有一个子类Stack(栈)。
关于扩容:ArrayList和Vector的底层都是用一个对象数组来存储数据的,之所以可以存储长度不受限制。是因为当实例化一个类对象时,其底层会创建一个长度比初始化元素数量多默认值的一个数组,当有数据需要存储时,就依次存储在数组后面。当存储数据的数量超过数组长度时,就会创建一个新的长度足够的数组,并将原数组中的元素赋给新数组。这个过程称之为扩容。对于Vector类,新数组的长度一般为原数组大小的2倍空间,而ArrayList是1.5倍。
3. List接口的常用方法
List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来操作集合元素的方法:

方法 作用
void add(int index, Object ele) 在index位置插入ele元素
boolean addAll(int index, Collection eles) 从index位置开始将eles中的所有元素添加进来
Object get(int index) 的所有元素添加进来获取指定index位置的元素
int indexOf(Object obj) 返回obj在集合中首次出现的位置
int lastIndexOf(Object obj) 返回obj在当前集合中末次出现的位置
Object remove(int index) 移除指定index位置的元素,并返回此元素
Object set(int index, Object ele) 设置指定index位置的元素为ele
List subList(int fromIndex, int toIndex) 返回从fromIndex到toIndex位置的子集合,左闭右开取子集,不改变原有的集合。
注意这里的remove()方法是按照索引值删除元素的。Collection接口中的remove()方法是按照对象来删除的。属于方法重载。

增、删、改、查、插、长度、遍历

五、Set接口
Set接口是Collection接口的一个子接口。

  1. Set接口概述
    Set 接口实例存储的是无序的,不重复的数据。Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set 集合中,则添加操作失败。
    Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法。
    Set接口没有提供额外的方法。
    JDK API中List接口的实现类常用的有:HashSet、LinkedHashSet和TreeSet。
  2. HashSet、LinkedHashSet、TreeSet的区别
    HashSet:作为Set接口的主要实现类。线程不安全,可以存储null值。该类具有很好的存取、查找、删除性能。
    LinkedHashSet:是HashSet的子类,遍历其内部数据时,可以按照插入的方式进行。多用于需要频繁遍历的场合。
    TreeSet:可以按照添加对象的指定属性进行排序。
  3. Set接口的特性(存储逻辑)
    Set 接口实例存储的是无序的,不重复的数据。

HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类,该类具有很好的存取、查找、删除性能。接下来以HashSet为例说明Set接口的特性:

无序性不代表随机性。无序只代表数据存储不是像List接口那样在底层数组中按照添加元素的顺序依次存储,Set的存储顺序与存储元素的HashCode值有关,最终依然是有顺序的。
不可重复性:Set接口的不可重复性是指不会存储两个相同元素。对于元素相同的判断,是调用新添加元素的equals方法。
Set接口的存储逻辑:
以HashSet为例,但是HashSet的源码里数据存储用的是HashMap,因此下列逻辑不仅是HashSet的存储也是HashMap的存储:

HashSet的底层也是数组,初始容量为16,当使用率超过0.75,就会扩容为原来的2倍。

import java.util.;
public class Demo01 {
public static void main(String[] args) {
Set set = new HashSet();
set.add(123);
set.add(“Tom”);
set.add(new Person(“automan”,20));
set.add(new Person(“automan”,20));
set.add(789);
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
class Person {
private String name;
private int age;
@Override
public String toString() {
return “Person{” +
“name='” + name + ‘’’ +
“, age=” + age +
‘}’;
}
/

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
name.equals(person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
*/
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

运行结果:
Tom
789
123
Person{name=‘automan’, age=20}
Person{name=‘automan’, age=20}
//这里的Set集合里存放了两个Person对象,是因为它们都是用new创建的,hash值不同,Person的父类是Object,它的equals方法是比较hash值。若要改为比较内容,则需要重写Person类的equals方法。并且对于Set的比较,还应该重写hashcode方法。
总结:

向Set集合中添加数据时,应该重写数据所在类的equals方法和hashcode方法,并且保持一致性。即这两个对象的equals方法比较返回true时,这两个对象的hashcode方法的返回值也应该相等。

  1. LinkedHashSet的使用
    LinkedHashSet是HashSet的子类,它根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。

LinkedHashSet插入性能略低于 HashSet,但在频繁遍历 Set 里的全部元素时有很好的性能。

LinkedHashSet的存储结构:

  1. TreeSet的使用
    TreeSet 是 SortedSet 接口的实现类,TreeSet 可以按照添加对象的指定属性进行排序,这就要求TreeSet中的数据必须是相同类的对象。

TreeSet和TreeMap底层都是使用红黑树结构存储数据。

TreeSet 两种排序方法:自然排序和定制排序,也就是Comparable借口和Comparator接口。默认情况下,TreeSet 采用自然排序。

自然排序:

import java.util.*;
public class Demo01 {
public static void main(String[] args) {
//创建一个TreeSet
TreeSet treeSet = new TreeSet();
treeSet.add(new Goods(“harry”,20));
treeSet.add(new Goods(“alpha”,28));
treeSet.add(new Goods(“beer”,34));
treeSet.add(new Goods(“maxc”,25));
treeSet.add(new Goods(“sfsdg”,20));
treeSet.add(new Goods(“sfsdg”,56));
//使用迭代器遍历集合中的元素
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
class Goods implements Comparable {
private String name;
private double price;
@Override
//按照名称从高到低排序,名称一致的时候按价格的从低到高排
public int compareTo(Object o) {
//判断元素是都是Goods子类
if (o instanceof Goods) {
//是的话,就强制转换为Goods类,类型一致方可比较
Goods goods = (Goods) o;
int compare = -this.name.compareTo(goods.name);
if (compare != 0) {
return compare;
} else {
return Double.compare(this.price, goods.price);
}
}else {
throw new RuntimeException(“类型不一致”);
}
}
@Override
public String toString() {
return “Goods{” +
“name='” + name + ‘’’ +
“, price=” + price +
‘}’;
}
public Goods() {
}
public Goods(String name, double price) {
this.name = name;
this.price = price;
}
}

运行结果:
Goods{name=‘sfsdg’, price=20.0}
Goods{name=‘sfsdg’, price=56.0}
Goods{name=‘maxc’, price=25.0}
Goods{name=‘harry’, price=20.0}
Goods{name=‘beer’, price=34.0}
Goods{name=‘alpha’, price=28.0}
定制排序:

import java.util.*;
public class Demo01 {
public static void main(String[] args) {
//使用Comparator创建一个定制的排序标准
Comparator com = new Comparator() {
//按照价格从低到高进行排序,价格一样的丢失
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Goods && o2 instanceof Goods) {
Goods g1 = (Goods)o1;
Goods g2 = (Goods)o2;
return Double.compare(g1.getPrice(),g2.getPrice());
}else {
throw new RuntimeException(“类型不一致”);
}
}
};
//创建一个TreeSet,按照定制的Comparator对象标准排序,因为只定义了按名称排序,TreeSet中不允许有相同的元素,因此如果新对象的名称和原有元素名称相同,则添加失败。
TreeSet treeSet = new TreeSet(com);
treeSet.add(new Goods(“harry”,20));
treeSet.add(new Goods(“alpha”,28));
treeSet.add(new Goods(“beer”,34));
treeSet.add(new Goods(“maxc”,25));
treeSet.add(new Goods(“sfsdg”,20));
treeSet.add(new Goods(“sfsdg”,56));
//使用迭代器遍历集合中的元素
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
class Goods {
private String name;
private double price;
@Override
public String toString() {
return “Goods{” +
“name='” + name + ‘’’ +
“, price=” + price +
‘}’;
}
public Goods() {
}
public Goods(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}

运行结果:
Goods{name=‘harry’, price=20.0}
Goods{name=‘maxc’, price=25.0}
Goods{name=‘alpha’, price=28.0}
Goods{name=‘beer’, price=34.0}
Goods{name=‘sfsdg’, price=56.0}
总结:

自然排序中,比较两个对象是否相同的标准是,调用对象所在类的conpareTo()方法,而不是equals方法;
定制排序中,比较两个对象是否相同的标准是,调用对象所在类的conpare()方法,而不是equals方法;
6. 一个例子
在List内去除重复数字值,要求尽量简单

import java.util.*;
public class Demo02 {
public static void main(String[] args) {
//创建一个list
List list = new ArrayList();
list.add(new Integer(1));
list.add(new Integer(2));
list.add(new Integer(2));
list.add(new Integer(4));
list.add(new Integer(4));
//过滤方法
List list2 = duplicateList(list);
//foreach方法遍历
for (Object integer : list2) {
System.out.println(integer);
}
}
//过滤方法:接收list,返回list
public static List duplicateList(List list) {
//中间使用Set,Set不允许有相同的元素
//Integer包装类重写了equals方法,可以直接使用,若为自定义类对象需要重写其equals方法。
HashSet set = new HashSet();
set.addAll(list);
return new ArrayList(set);
}
}
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明,KuangStudy,以学为伴,一生相伴!

本文链接:https://www.kuangstudy.com/bbs/1381203315617984513

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值