如何通过多个字段比较对象

假设你有一些对象,它们有几个字段可以比较:

public class Person {


private String firstName;
private String lastName;
private String age;


/* Constructors */


/* Methods */


}

所以在这个例子中,当你问if:

a.compareTo(b) > 0

你可能会问a的姓是不是在b的姓之前,或者a的年龄是不是比b大,等等……

在不增加不必要的混乱或开销的情况下,在这些类型的对象之间进行多重比较的最干净的方法是什么?

  • java.lang.Comparable接口只允许通过一个字段进行比较
  • 在我看来,添加大量的比较方法(如compareByFirstName()compareByAge()等)是混乱的。

那么最好的解决办法是什么呢?

516530 次浏览

我认为如果你的比较算法是“聪明的”,那就更令人困惑了。我会选择你建议的众多比较方法。

对我来说唯一的例外就是平等。对于单元测试,重写. equals(在.net中)对我来说很有用,以便确定两个对象之间的几个字段是否相等(而不是引用是否相等)。

与比较方法不同,您可能只想在Person类中定义几种类型的“Comparator”子类。这样就可以将它们传递到标准的Collections排序方法中。

如果用户有多种订购person的方式,你也可以在某个地方设置多个__abc0作为常量。大多数排序操作和排序集合都以比较器作为参数。

你可以实现一个Comparator来比较两个Person对象,并且你可以检查任意多的字段。你可以在比较器中放入一个变量,告诉它与哪个字段进行比较,尽管只编写多个比较器可能会更简单。

如果你实现了类似的接口,你需要选择一个简单的属性来排序。这就是所谓的自然排序。把它看作默认值。通常在没有提供特定比较器时使用。通常这是名称,但您的用例可能调用不同的东西。您可以自由地使用任何数量的其他比较器,您可以提供给各种集合api来覆盖自然排序。

还要注意,通常如果a.c omareto (b) == 0,则a.c omareto (b) == true。如果没有也没关系,但是有副作用要注意。在Comparable接口上查看优秀的javadocs,您将找到许多关于这方面的有用信息。

你应该实现Comparable <Person>。假设所有字段都不为空(为简单起见),年龄是int型,比较排名是first, last, age, compareTo方法非常简单:

public int compareTo(Person other) {
int i = firstName.compareTo(other.firstName);
if (i != 0) return i;


i = lastName.compareTo(other.lastName);
if (i != 0) return i;


return Integer.compare(age, other.age);
}

你也可以看看实现Comparator的Enum。

http://tobega.blogspot.com/2008/05/beautiful-enums.html

如。

Collections.sort(myChildren, Child.Order.ByAge.descending());

要连续对多个字段排序,请尝试ComparatorChain

ComparatorChain是一个按顺序包装一个或多个比较器的比较器。ComparatorChain依次调用每个Comparator,直到1)任何一个Comparator返回非零结果(然后返回该结果),或2)ComparatorChain耗尽(并返回零)。这种类型的排序非常类似于SQL中的多列排序,并且该类允许Java类在对List排序时模拟这种行为。

为了进一步方便类似sql的排序,列表中任何一个Comparator的顺序都可以颠倒。

调用添加新的比较器或在调用compare(Object, Object)后更改上升/下降排序的方法将导致UnsupportedOperationException。但是,注意不要更改底层的比较器列表或定义排序顺序的BitSet。

ComparatorChain的实例不同步。类在构造时不是线程安全的,但是在所有设置操作完成后执行多次比较是线程安全的。

使用谷歌的番石榴库很容易做到。

例如Objects.equal(name, name2) && Objects.equal(age, age2) && ...

更多的例子:

  • https://stackoverflow.com/a/5039178/1180621 < a href = " https://stackoverflow.com/a/5039178/1180621 " > < / >

在我看来,为这样的用例手动编写Comparator是一个糟糕的解决方案。这种特别的方法有很多缺点:

  • 没有代码重用。违反了干。
  • 样板。
  • 增加了出错的可能性。

那么解决方案是什么呢?

首先是一些理论。

让我们用Ord A来表示命题“类型A支持比较”。(从程序的角度来看,你可以把Ord A看作一个包含比较两个__abc0的逻辑的对象。是的,就像Comparator。)

现在,如果Ord AOrd B,那么它们的组合(A, B)也应该支持比较。即Ord (A, B)。如果Ord AOrd BOrd C,则Ord (A, B, C)

我们可以将这个论证扩展到任意性,并说:

__abc0⇒__abc1

我们称这个为表述一。

复合材料的比较将像您在问题中描述的那样工作:首先尝试第一个比较,然后是下一个比较,然后是下一个比较,依此类推。

这是解的第一部分。现在是第二部分。

如果你知道Ord A,并且知道如何将B转换为A(称该转换函数为f),那么你也可以拥有Ord B。如何?当要比较两个B实例时,首先使用f将它们转换为A,然后应用Ord A

这里,我们将转换B → A映射到Ord A → Ord B。这被称为逆变映射(或简称comap)。

__abc0⇒__abc2 __abc1

我们称这个为表述二。


现在让我们把这个应用到你的例子中。

你有一个名为Person的数据类型,它包含三个类型为String的字段。

  • 我们知道Ord String。通过语句1,Ord (String, String, String)

  • 我们可以很容易地从Person(String, String, String)编写函数。(只返回三个字段。)由于我们知道Ord (String, String, String)Person → (String, String, String),通过表述2,我们可以使用comap得到Ord Person

QED。


我如何实现所有这些概念?

好消息是你不必这么做。已经存在一个图书馆,它实现了本文中描述的所有想法。(如果你好奇这些是如何实现的,你可以查看引擎盖下。)

下面是代码的外观:

Ord<Person> personOrd =
p3Ord(stringOrd, stringOrd, stringOrd).comap(
new F<Person, P3<String, String, String>>() {
public P3<String, String, String> f(Person x) {
return p(x.getFirstName(), x.getLastname(), x.getAge());
}
}
);

解释:

  • stringOrd是类型为Ord<String>的对象。这与我们最初的“支持比较”命题相对应。
  • p3Ord是一个接受Ord<A>Ord<B>Ord<C>并返回Ord<P3<A, B, C>>的方法。这对应于表述一。P3代表三个元素的产品。Product是复合材料的代数术语。)
  • comap对应于comap
  • F<A, B>表示转换函数A → B
  • p是一个用于创建产品的工厂方法。
  • 整个表达式对应于表述2。

希望这能有所帮助。

import com.google.common.collect.ComparisonChain;


/**
* @author radler
* Class Description ...
*/
public class Attribute implements Comparable<Attribute> {


private String type;
private String value;


public String getType() { return type; }
public void setType(String type) { this.type = type; }


public String getValue() { return value; }
public void setValue(String value) { this.value = value; }


@Override
public String toString() {
return "Attribute [type=" + type + ", value=" + value + "]";
}


@Override
public int compareTo(Attribute that) {
return ComparisonChain.start()
.compare(this.type, that.type)
.compare(this.value, that.value)
.result();
}


}

(从在Java中基于多个字段对对象列表进行排序的方法)

这个要点中的工作代码

使用Java 8 lambda(2019年4月10日添加)

Java 8通过lambda很好地解决了这个问题(尽管Guava和Apache Commons可能仍然提供了更大的灵活性):

Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
.thenComparing(Report::getStudentNumber)
.thenComparing(Report::getSchool));

感谢@高agong的下面的回答

杂乱而复杂:手工分类

Collections.sort(pizzas, new Comparator<Pizza>() {
@Override
public int compare(Pizza p1, Pizza p2) {
int sizeCmp = p1.size.compareTo(p2.size);
if (sizeCmp != 0) {
return sizeCmp;
}
int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);
if (nrOfToppingsCmp != 0) {
return nrOfToppingsCmp;
}
return p1.name.compareTo(p2.name);
}
});

这需要大量的输入和维护,而且很容易出错。

反射方式:用BeanComparator排序

ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));


Collections.sort(pizzas, chain);

显然,这更简洁,但更容易出错,因为使用string而失去了对字段的直接引用(没有类型安全,自动重构)。现在,如果字段被重命名,编译器甚至不会报告问题。此外,由于该解决方案使用反射,排序要慢得多。

到达那里:排序谷歌番石榴的ComparisonChain

Collections.sort(pizzas, new Comparator<Pizza>() {
@Override
public int compare(Pizza p1, Pizza p2) {
return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();
// or in case the fields can be null:
/*
return ComparisonChain.start()
.compare(p1.size, p2.size, Ordering.natural().nullsLast())
.compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast())
.compare(p1.name, p2.name, Ordering.natural().nullsLast())
.result();
*/
}
});

这样好多了,但对于最常见的用例,需要一些样板代码:默认情况下,null值的值应该更小。对于空字段,您必须向Guava提供一个额外的指令,在这种情况下要做什么。如果你想做一些特定的事情,这是一个灵活的机制,但通常你想要默认的情况(即。1, a, b, z, null)。

使用Apache Commons CompareToBuilder进行排序

Collections.sort(pizzas, new Comparator<Pizza>() {
@Override
public int compare(Pizza p1, Pizza p2) {
return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();
}
});

像Guava的ComparisonChain一样,这个库类很容易在多个字段上排序,但也为空值定义了默认行为。1, a, b, z, null)。但是,您也不能指定任何其他内容,除非您提供自己的Comparator。

因此

最终,这归结于口味和灵活性的需要(Guava的ComparisonChain) vs.简洁的代码(Apache的CompareToBuilder)。

额外的方法

我发现了一个很好的解决方案,在MultiComparator中按照优先级在CodeReview的顺序组合多个比较器:

class MultiComparator<T> implements Comparator<T> {
private final List<Comparator<T>> comparators;


public MultiComparator(List<Comparator<? super T>> comparators) {
this.comparators = comparators;
}


public MultiComparator(Comparator<? super T>... comparators) {
this(Arrays.asList(comparators));
}


public int compare(T o1, T o2) {
for (Comparator<T> c : comparators) {
int result = c.compare(o1, o2);
if (result != 0) {
return result;
}
}
return 0;
}


public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
Collections.sort(list, new MultiComparator<T>(comparators));
}
}

当然Apache Commons Collections已经有了一个util:

ComparatorUtils.chainedComparator(comparatorCollection) . /a> . ComparatorUtils.chainedComparator(comparatorCollection

Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
//here threshold,buyRange,targetPercentage are three keys on that i have sorted my arraylist
final Comparator<BasicDBObject>


sortOrder = new Comparator<BasicDBObject>() {
public int compare(BasicDBObject e1, BasicDBObject e2) {
int threshold = new Double(e1.getDouble("threshold"))
.compareTo(new Double(e2.getDouble("threshold")));
if (threshold != 0)
return threshold;


int buyRange = new Double(e1.getDouble("buyRange"))
.compareTo(new Double(e2.getDouble("buyRange")));
if (buyRange != 0)
return buyRange;


return (new Double(e1.getDouble("targetPercentage")) < new Double(
e2.getDouble("targetPercentage")) ? -1 : (new Double(
e1.getDouble("targetPercentage")) == new Double(
e2.getDouble("targetPercentage")) ? 0 : 1));
}
};
Collections.sort(objectList, sortOrder);

使用java8:

Comparator.comparing((Person p)->p.firstName)
.thenComparing(p->p.lastName)
.thenComparingInt(p->p.age);

如果你有访问方法:

Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge);

如果一个类实现Comparable,那么这个比较器可以用在compareTo方法中:

@Override
public int compareTo(Person o){
return Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge)
.compare(this, o);
}

另一个可以考虑的选项是Apache Commons。它提供了很多选项。

import org.apache.commons.lang3.builder.CompareToBuilder;

例:

public int compare(Person a, Person b){


return new CompareToBuilder()
.append(a.getName(), b.getName())
.append(a.getAddress(), b.getAddress())
.toComparison();
}
对于那些能够使用Java 8流API的人来说,这里有一个更整洁的方法: lambda和排序 < / p >

我正在寻找相当于c# LINQ:

.ThenBy(...)

我在Comparator上找到了Java 8的机制:

.thenComparing(...)

下面是演示算法的代码片段。

    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

请查看上面的链接,以获得更简洁的方法,并解释Java的类型推断如何使其与LINQ相比定义起来更笨拙。

下面是完整的单元测试供参考:

@Test
public void testChainedSorting()
{
// Create the collection of people:
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Dan", 4));
people.add(new Person("Andi", 2));
people.add(new Person("Bob", 42));
people.add(new Person("Debby", 3));
people.add(new Person("Bob", 72));
people.add(new Person("Barry", 20));
people.add(new Person("Cathy", 40));
people.add(new Person("Bob", 40));
people.add(new Person("Barry", 50));


// Define chained comparators:
// Great article explaining this and how to make it even neater:
// http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));


// Sort the stream:
Stream<Person> personStream = people.stream().sorted(comparator);


// Make sure that the output is as expected:
List<Person> sortedPeople = personStream.collect(Collectors.toList());
Assert.assertEquals("Andi",  sortedPeople.get(0).name); Assert.assertEquals(2,  sortedPeople.get(0).age);
Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
Assert.assertEquals("Bob",   sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
Assert.assertEquals("Bob",   sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
Assert.assertEquals("Bob",   sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
Assert.assertEquals("Dan",   sortedPeople.get(7).name); Assert.assertEquals(4,  sortedPeople.get(7).age);
Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3,  sortedPeople.get(8).age);
// Andi     : 2
// Barry    : 20
// Barry    : 50
// Bob      : 40
// Bob      : 42
// Bob      : 72
// Cathy    : 40
// Dan      : 4
// Debby    : 3
}


/**
* A person in our system.
*/
public static class Person
{
/**
* Creates a new person.
* @param name The name of the person.
* @param age The age of the person.
*/
public Person(String name, int age)
{
this.age = age;
this.name = name;
}


/**
* The name of the person.
*/
public String name;


/**
* The age of the person.
*/
public int age;


@Override
public String toString()
{
if (name == null) return super.toString();
else return String.format("%s : %d", this.name, this.age);
}
}

下面的博客给出了一个很好的链式比较器的例子

http://www.codejava.net/java-core/collections/sorting-a-list-by-multiple-attributes-example

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;


/**
* This is a chained comparator that is used to sort a list by multiple
* attributes by chaining a sequence of comparators of individual fields
* together.
*
*/
public class EmployeeChainedComparator implements Comparator<Employee> {


private List<Comparator<Employee>> listComparators;


@SafeVarargs
public EmployeeChainedComparator(Comparator<Employee>... comparators) {
this.listComparators = Arrays.asList(comparators);
}


@Override
public int compare(Employee emp1, Employee emp2) {
for (Comparator<Employee> comparator : listComparators) {
int result = comparator.compare(emp1, emp2);
if (result != 0) {
return result;
}
}
return 0;
}
}

打电话比较器:

Collections.sort(listEmployees, new EmployeeChainedComparator(
new EmployeeJobTitleComparator(),
new EmployeeAgeComparator(),
new EmployeeSalaryComparator())
);

乔布斯的回答开始,三元操作符可以使用:

public int compareTo(Person other) {
int f = firstName.compareTo(other.firstName);
int l = lastName.compareTo(other.lastName);
return f != 0 ? f : l != 0 ? l : Integer.compare(age, other.age);
}

在java中使用hashcode方法比较两个对象很容易。

public class Sample{


String a=null;
String b=null;


public Sample(){
a="s";
b="a";
}
public Sample(String a,String b){
this.a=a;
this.b=b;
}
public static void main(String args[]){
Sample f=new Sample("b","12");
Sample s=new Sample("b","12");
//will return true
System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));


//will return false
Sample f=new Sample("b","12");
Sample s=new Sample("b","13");
System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));


}
//Following is the example in jdk 1.8
package com;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;


class User {
private String firstName;
private String lastName;
private Integer age;


public Integer getAge() {
return age;
}


public User setAge(Integer age) {
this.age = age;
return this;
}


public String getFirstName() {
return firstName;
}


public User setFirstName(String firstName) {
this.firstName = firstName;
return this;
}


public String getLastName() {
return lastName;
}


public User setLastName(String lastName) {
this.lastName = lastName;
return this;
}


}


public class MultiFieldsComparision {


public static void main(String[] args) {
List<User> users = new ArrayList<User>();


User u1 = new User().setFirstName("Pawan").setLastName("Singh").setAge(38);
User u2 = new User().setFirstName("Pawan").setLastName("Payal").setAge(37);
User u3 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(60);
User u4 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(43);
User u5 = new User().setFirstName("Pawan").setLastName("Chamoli").setAge(44);
User u6 = new User().setFirstName("Pawan").setLastName("Singh").setAge(5);


users.add(u1);
users.add(u2);
users.add(u3);
users.add(u4);
users.add(u5);
users.add(u6);


System.out.println("****** Before Sorting ******");


users.forEach(user -> {
System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
});


System.out.println("****** Aftre Sorting ******");


users.sort(
Comparator.comparing(User::getFirstName).thenComparing(User::getLastName).thenComparing(User::getAge));


users.forEach(user -> {
System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
});


}


}

如果我们必须基于多个字段对Person对象进行排序,则相同的代码实现在这里。

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;


public class Person {


private String firstName;
private String lastName;
private int age;


public String getFirstName() {
return firstName;
}


public void setFirstName(String firstName) {
this.firstName = firstName;
}


public String getLastName() {
return lastName;
}


public void setLastName(String lastName) {
this.lastName = lastName;
}


public int getAge() {
return age;
}


public void setAge(int age) {
this.age = age;
}


public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}




static class PersonSortingComparator implements Comparator<Person> {


@Override
public int compare(Person person1, Person person2) {


// for first name comparison
int firstNameCompare = person1.getFirstName().compareTo(person2.getFirstName());


// for last name comparison
int lastNameCompare = person1.getLastName().compareTo(person2.getLastName());


// for last name comparison
int ageCompare = person1.getAge() - person2.getAge();


// Now comparing
if (firstNameCompare == 0) {
if (lastNameCompare == 0) {
return ageCompare;
}
return lastNameCompare;
}
return firstNameCompare;
}
}


public static void main(String[] args) {
Person person1 = new Person("Ajay", "Kumar", 27);
Person person2 = new Person("Ajay","Gupta", 23);
Person person3 = new Person("Ajay","Kumar", 22);




ArrayList<Person> persons = new ArrayList<>();
persons.add(person1);
persons.add(person2);
persons.add(person3);




System.out.println("Before Sorting:\n");
for (Person person : persons) {
System.out.println(person.firstName + " " + person.lastName + " " + person.age);
}


Collections.sort(persons, new PersonSortingComparator());


System.out.println("After Sorting:\n");
for (Person person : persons) {
System.out.println(person.firstName + " " + person.lastName + " " + person.age);
}
}


}

晚做总比不到好——如果你正在寻找不必要的混乱或开销,那么很难同时在最少的代码/快速执行方面击败下面的方法。

数据类:

public class MyData {
int id;
boolean relevant;
String name;
float value;
}

比较器:

public class MultiFieldComparator implements Comparator<MyData> {
@Override
public int compare(MyData dataA, MyData dataB) {
int result;
if((result = Integer.compare(dataA.id, dataB.id)) == 0 &&
(result = Boolean.compare(dataA.relevant, dataB.relevant)) == 0 &&
(result = dataA.name.compareTo(dataB.name)) == 0)
result = Float.compare(dataA.value, dataB.value);
return result;
}
}

如果你只是想按自定义顺序对集合进行排序,那么下面的代码就更清晰了:

myDataList.sort((dataA, dataB) -> {
int result;
if((result = Integer.compare(dataA.id, dataB.id)) == 0 &&
(result = Boolean.compare(dataA.relevant, dataB.relevant)) == 0 &&
(result = dataA.name.compareTo(dataB.name)) == 0)
result = Float.compare(dataA.value, dataB.value);
return result;
});

Java 8通过lambda方式,我们可以通过方法引用进行比较。 学生POJO < / p >

public class Student {
int id;
String firstName;
String lastName;
String subject;


public Student(int id, String firstName, String lastName, String subject) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.subject = subject;
}
enter code here

现在我们可以根据

1. id->FirstName->LastName->Subject 2. Subject->id->FirstName->LastName

我们将在数组Stream中使用Comparator

public class TestComprator {
public static void main(String[] args) {
Student s1= new Student(108, "James", "Testo", "Physics");
Student s2= new Student(101, "Fundu", "Barito", "Chem");
Student s3= new Student(105, "Sindhu", "Sharan", "Math");
Student s4= new Student(98, "Rechel", "Stephen", "Physics");
System.out.printf("----------id->FirstName->LastName->Subject-------------");
Arrays.asList(s1,s2,s3,s4).stream()
.sorted(Comparator.comparing(Student::getId)
.thenComparing(Student::getFirstName)
.thenComparing(Student::getLastName)
.thenComparing(Student::getSubject))
.forEach(System.out::println);


System.out.printf("----Subject->id->FirstName->LastName ------\n");
Arrays.asList(s1,s2,s3,s4).stream()
.sorted(Comparator. comparing(Student::getSubject)
.thenComparing(Student::getId)
.thenComparing(Student::getFirstName)
.thenComparing(Student::getLastName)
)
.forEach(System.out::println);
}

输出:

`----------id->FirstName->LastName->Subject-------------
Student{id=98, firstName='Rechel', lastName='Stephen', subject='Physics'}
Student{id=101, firstName='Fundu', lastName='Barito', subject='Chem'}
Student{id=105, firstName='Sindhu', lastName='Sharan', subject='Math'}
Student{id=108, firstName='James', lastName='Testo', subject='Physics'}
----Subject->id->FirstName->LastName ------
Student{id=101, firstName='Fundu', lastName='Barito', subject='Chem'}
Student{id=105, firstName='Sindhu', lastName='Sharan', subject='Math'}
Student{id=98, firstName='Rechel', lastName='Stephen', subject='Physics'}
Student{id=108, firstName='James', lastName='Testo', subject='Physics'}