ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java学习笔记之集合(上)

2021-04-12 18:32:23  阅读:95  来源: 互联网

标签:Java int 接口 Person add 笔记 集合 new public


目录

集合概述

为什么需要集合

  • 面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象进行操作,就需要对对象进行存储
  • 虽然我们可以使用 Array 存储对象,但这种存储对象的方式也有一些弊端。
    1. 数组初始化以后,长度就不可变了,不便于扩展
    2. 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高
    3. 同时无法直接获取存储元素的个数
    4. 数组存储的数据是有序的、可以重复的。即存储数据的特点单一

Java中的集合结构

image

Conection接口

Collection 接口用于存储单列数据的最大操作接口,定义了存取一组对象的方法的集合。
该接口中定义了以下方法:

No. 方法名称 描述
1 public boolean add(E e) 向集合中插入一个元素
2 public boolean addAll(Collection c) 向集合中插入一组元素
3 public void clear() 清空集合中的元素
4 public boolean contains(Object o) 查找一个元素是否存在
5 public boolean containsAll(Collection c) 查找一组元素是否存在
6 public boolean isEmpty() 判断集合是否为空
7 public Iterator iterator() 实例化该集合对应的迭代器
8 public boolean remove(Object o) 从集合中删除一个对象
9 boolean removeAll(Collection c) 从集合中删除一组对象
10 boolean retainAll(Collection c) 判断是否没有指定的集合
11 public int size() 求出集合中元素的个数
12 public Object[] toArray() 以对象数组的形式返回集合中的全部内容
13 T[] toArray(T[] a) 指定操作的泛型类型,并把内容返回
14 public boolean equals(Object o) 从 Object 类中覆写而来
15 public int hashCode() 从 Object 类中覆写而来

注意:

JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如: Set和List)实现
在 Java5 之前, Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理; 从 JDK 5.0 增加了泛型以后, Java 集合可以记住容器中对象的数据类型

List 接口

了解Collection接口之后,我们先来看看Collection接口中的子类List

List接口介绍

List 见名知意,即列表。那什么是列表呢?列表就相当于以前学习的数组,和数组不同的是列表被封装成了一个对象,增删改查操作依赖于其中的方法实现,同时列表对象内部自带动态增容,使我们无需关心其容量。
其中在Collection接口的基础上新增和重载了以下方法:

No. 方法名称 描述
1 public void add(int index,E element) 在指定位置处增加元素
2 boolean addAll(int index,Collection c) 在指定位置处增加一组元素
3 public E get(int index) 根据索引位置取出每一个元素
4 public int indexOf(Object o) 根据对象查找指定的位置,找不到返回-1
5 public int lastIndexOf(Object o) 从后面向前查找位置,找不到返回-1
6 public ListIterator listIterator() 返回该List对象对应的迭代器
7 public ListIterator listIterator(int index) 返回从指定位置的List对象对应的迭代器
8 public E remove(int index) 删除指定位置的内容
9 public E set(int index,E element) 修改指定位置的内容
10 List subList(int fromIndex,int toIndex) 返回子集合

List接口的实现类

ArrayList

ArrayList是对象引用的一个”变长”数组
基本操作示例:

public class ArrayListDemo {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>(); // 实例化List对象,并指定泛型类型
		list.add("hello"); // 增加内容,此方法从Collection接口继承而来
		list.add(0, "world");// 增加内容,此方法是List接口单独定义的
		System.out.println("集合中的内容是:" + list);// 打印list对象调用toString()方法
		list.remove(1); // 根据索引删除内容,此方法是List接口单独定义的
		list.remove("world");// 删除指定的对象
		System.out.print("集合中删除后的内容是:" + list);
		for (int x = 0; x < list.size(); x++) { // size()方法从Collection接口继承而来
			System.out.print(list.get(x) + "、"); // 此方法是List接口单独定义的
		}
	}
}

控制台输出如下:
image

Vector

Vector 是一个古老的集合, JDK1.0就有了,大多数操作与ArrayList相同。

  • 示例:将ArrayList示例中的ArrayList改为Vector即可
  • ArrayList的区别如下:
    • Vector是线程安全的,性能较低;ArrayList是非线程安全的,性能较高
    • ArrayList支持 Iterator、ListIterator 输出;Vector除了支持 Iterator、ListIterator 输出,还支持Enumeration 输出
LinkedList

ArrayList大部分操作相同,但是此类也是 Queue 接口的子类,Queue 接口定义了如下的方法:

No. 方法名称 描述
1 public boolean add(E e) 增加元素,如果有容量限制,并且已满,则抛出异常
2 public E element() 取得元素,但是不删除当前元素,如果队列为空则抛出异常
3 boolean offer(E e) 添加,如果有容量限制,并且已满,只是无法添加,但不抛出异常,返回 false
4 E peek() 取得头元素,但是不删除,如果队列为空,则返回null
5 E poll() 取得头元素,但是删除, 如果队列为空,则返回 null
6 E remove() 删除当前元素, 如果对列为空则抛出异常
  • 继承List示例将ArrayList示例中的ArrayList改为LinkedList即可
  • 继承队列Queue示例:
public class LinkedListDemo {
	public static void main(String[] args) {
		Queue<String> queue = new LinkedList<String>();
        	queue.add("A");
        	queue.add("B");
        	queue.add("C");
        	String element = queue.element();
        	System.out.println("element:" + element);
        	String peek = queue.peek();
        	System.out.println("peek:" + peek);
		//把queue的大小先取出来,否则每循环一次,移除一个元素,就少一个元素,那么queue.size()在变小,就不能循环queue.size()次了。
        	int len=queue.size();
        	for (int x = 0; x <len; x++) {
            		System.out.println(queue.poll());
        	}
        	System.out.println(queue);
	}
}

控制台输出:
image

  • ArrayList的区别如下:
    1. ArrayList底层是使用 Array 数组实现,LinkedList底层是使用链表实现
    2. 由于LinkedList底层使用链表实现,对于频繁的插入或删除元素的操作,效率较高

在这三个List实现类中,使用占比为:ArrayList(95%)、Vector(4%)、LinkedList(1%)
尽管 ArrayList 是最常用的,但我们应该根据业务场景作出选择
在各种list中,在不要求同步的情况下,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList; Vector总是比ArrayList慢,所以尽量避免使用

Set接口

Set 接口也是 Collection 的子接口,与 List 接口最大的不同在于,Set 接口里面的内容是不允许重复的。
Set 接口并没有对 Collection 接口进行扩充,基本上还是与 Collection 接口保持一致。因为此接口没有 List 接口中定义的 get(int index)方法,所以无法使用循环进行输出。
该接口中有两个常用的子类:HashSet、TreeSet。

HashSet

HashSet 属于散列的存放类集,里面的内容是无序存放的。

public class HashSetDemo {
	public static void main(String[] args) {
		Set<String> all = new HashSet<String>(); // 实例化Set接口对象
		all.add("A");
		all.add("A");//重复元素
		all.add("A");//重复元素
		all.add("B");
		all.add("P");
		all.add("D");
		all.add("E");
		System.out.println(all);
		//将其转换为Object数组,不推荐,因为在操作的时候已经指定了操作的泛型类型
		Object[] objects = all.toArray();
		//将其转换为指定的泛型类型数组
		String[] array = all.toArray(new String[]{});
		for (int i = 0; i < array.length; i++) {
			System.out.print(array[i] + ", ");
		}
	}
}

控制台输出如下:
image
以上字符串“A”设置了很多次,因为 Set 接口中是不能有任何的重复元素的,所以其最终结果只能有一个“A”。

TreeSet

  • 与 HashSet 不同的是,TreeSet 属于可以子类。
  • 存储基础数据类型示例:
public class TreeSetDemo {
	public static void main(String[] args) {
		Set<String> all = new TreeSet<>(); // 实例化Set接口对象
		all.add("P");
		all.add("E");
		all.add("D");
		System.out.println(all);
	}
}

控制台输出:
image
可以看到,TreeSet集合对基本数据类型的数据自动进行了排序

  • 如果存储自定义类类型,该自定义类必须实现Comparable接口,如果未实现该接口,则会抛出ClassCastException异常,示例:
    Person类:
public class Person implements Comparable<Person> {
    private String name;
    private int age;
    public int compareTo(Person per) {
        if (this.age > per.age) {
            return 1;
        } else if (this.age < per.age) {
            return -1;
        } else {
            return 0;
        }
    }
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    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;
    }
    public String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

TreeSetDemo类

public class TreeSetDemo {
	public static void main(String[] args) {
		Set<Person> all = new TreeSet<>(); // 实例化Set接口对象
		all.add(new Person("张三", 10));
		all.add(new Person("李四", 12));
		all.add(new Person("王五", 14));
		all.add(new Person("赵六", 10));
		all.add(new Person("孙七", 13));
		System.out.println(all);
	}
}

控制台输出如下:
image
从以上的结果可以看到我们已经实现了年龄排序,但是我们发现赵六没有了。因为赵六的年龄和张三的年龄是一样的,所以会被认为是同一个对象。则此时必须修改 Person 类,如果假设年龄相等的话,按名字进行排序。
修改的compareTo方法如下:

public int compareTo(Person per) {
	if (this.age > per.age) {
		return 1;
	} else if (this.age < per.age) {
		return -1;
	} else {
		return this.name.compareTo(per.name);
	}
}
  • 我们也可以通过TreeSet()构造方法传入一个Comparator的实现类来实现自定义类型比较,示例:
public class TreeSetDemo {
	public static void main(String[] args) {
		Set<Person> all = new TreeSet<>(new Comparator<Person>() {
			@Override
			public int compare(Person p1, Person p2) {
				if (p1.getAge() > p2.getAge()) {
					return 1;
				} else if (p1.getAge() < p2.getAge()) {
					return -1;
				} else {
					return p1.getName().compareTo(p2.getName());
				}
			}
		}); // 实例化Set接口对象,传入Comparator对象
		all.add(new Person("张三", 10));
		all.add(new Person("李四", 12));
		all.add(new Person("王五", 14));
		all.add(new Person("赵六", 10));
		all.add(new Person("孙七", 13));
		System.out.println(all);
	}
}

控制台输出如下:
image
可以看出两者得到的结果相同,但这种传入Comparator接口只能使用一次,而且每次都需要传入Comparator对象。

关于重复元素的说明

之前使用 Comparable 完成的对于重复元素的判断,那么 Set 接口定义的时候本身就是不允许重复元素的,那么证明
如果现在真的是有重复元素的话,使用 HashSet 也同样可以进行区分。

public class HashSetPersonDemo {
	public static void main(String[] args) {
		Set<Person> all = new HashSet<Person>();
		all.add(new Person("张三", 10));
		all.add(new Person("李四", 10));
		all.add(new Person("李四", 10));
		all.add(new Person("王五", 11));
		all.add(new Person("赵六", 12));
		all.add(new Person("孙七", 13));
		System.out.println(all);
	}
}

image
此时我们发现姓名:李四,年龄:10的对象出现了两次,并没有去掉所谓的重复元素,也就是说之前的操作并不是真正的重复元素的判断,而是通过 Comparable 接口间接完成的。
因此,如果我们要想判断两个对象是否相等,则必须使用 Object 类中的 equals()方法。
从最正规的来讲,如果要想判断两个对象是否相等,则有两种方法可以完成:

  • 第一种判断两个对象的编码是否一致,这个方法需要通过 hashCode()完成,即:每个对象有唯一的编码
  • 还需要进一步验证对象中的每个属性是否相等,需要通过 equals()完成。
    所以此时需要覆写 Object 类中的 hashCode()方法,此方法表示一个唯一的编码,一般是通过公式计算出来的。
public boolean equals(Object obj) {
	if (this == obj) {
		return true;
	}
	if (!(obj instanceof Person)) {
		return false;
	}
	Person per = (Person) obj;
	if (per.name.equals(this.name) && per.age == this.age) {
		return true;
	} else {
		return false;
	}
}
public int hashCode() {
	return this.name.hashCode() * this.age;
}

我们发现,此时已经不存在重复元素了,所以如果要想去掉重复元素是需要依靠hashCode()和 equals()方法共同完成的。

标签:Java,int,接口,Person,add,笔记,集合,new,public
来源: https://www.cnblogs.com/fgba/p/14645921.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有