英文:
SplayTreeSet contains duplicate values
问题
我有一个名为SplayTreeSet
的对象,其中包含ChatRoomListModel
对象。我想根据DateTime createTime
值对我的集合进行排序。
我不确定我哪里出错了,因为重复的项目正在被添加。后来我执行了_latestMessages.add(newMessage)
,但实际上没有调用重载,因此会添加重复项。
我通过使用_latestMessage.contains(newMessageButSameChatRoomId)
进行了测试,它返回false。
当我执行_latestMessage.toSet()
时,每个重复项都会消失。
如何使SplayTreeSet
使用我的重载的equals 方法?
谢谢!
ObservableSet<ChatRoomListModel> _latestMessages = ObservableSet.splayTreeSetFrom(
ObservableSet(),
compare: (a, b) => b.compareTo(a),
);
ChatRoomListModel
模型具有以下方法和重载:
int compareTo(ChatRoomListModel other){
return messagesModel.createTime.compareTo(other.messagesModel.createTime);
}
ChatRoomListModel copyWith({
String? firstName,
String? otherUserId,
MessagesModel? messagesModel,
}) {
return ChatRoomListModel(
firstName: firstName ?? this.firstName,
otherUserId: otherUserId ?? this.otherUserId,
messagesModel: messagesModel ?? this.messagesModel,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ChatRoomListModel &&
runtimeType == other.runtimeType &&
messagesModel.chatRoomId == other.messagesModel.chatRoomId;
@override
int get hashCode => messagesModel.chatRoomId.hashCode;
英文:
I have a SplayTreeSet
of the objects ChatRoomListModel
. I'd like to order my set based on the objects DateTime createTime
value.
I'm not sure where I'm going wrong because there's duplicate items being added item.
I later down the line perform a _latestMessages.add(newMessage)
and it's not actually calling the overloads and there's duplicates being added.
I tested by using _latestMessage.contains(newMessageButSameChatRoomId)
, it returns false.
When I perform _latestMessage.toSet()
every duplicate goes away.
How can I get SplayTreeSet
to use my overloading equals?
Thanks!
ObservableSet<ChatRoomListModel> _latestMessages = ObservableSet.splayTreeSetFrom(
ObservableSet(),
compare: (a, b) => b.compareTo(a),
);
The ChatRoomListModel
model has the following methods and overloads:
int compareTo(ChatRoomListModel other){
return messagesModel.createTime.compareTo(other.messagesModel.createTime);
}
ChatRoomListModel copyWith({
String? firstName,
String? otherUserId,
MessagesModel? messagesModel,
}) {
return ChatRoomListModel(
firstName: firstName ?? this.firstName,
otherUserId: otherUserId ?? this.otherUserId,
messagesModel: messagesModel ?? this.messagesModel,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ChatRoomListModel &&
runtimeType == other.runtimeType &&
messagesModel.chatRoomId == other.messagesModel.chatRoomId;
@override
int get hashCode => messagesModel.chatRoomId.hashCode;
答案1
得分: 0
你的问题在于你对于两个ChatRoomListModel
对象"相等"有两种完全不同的理解。你提供了compareTo
和operator ==
的实现,但它们考虑了不同的属性集,因此当operator ==
返回false时,compareTo
可能返回0,这至少会令人困惑。SplayTreeMap
仅考虑compareTo
,而不考虑operator ==
。从SplayTreeSet文档中可以看到:
集合的元素是使用构造函数中传递的
compare
函数进行比较的,用于排序和相等性。如果集合仅包含一个对象a
,那么当且仅当compare(a, b) == 0
时,set.contains(b)
将返回true
,而a == b
的值甚至不会被检查。
我假设你所谓的"duplicates"是具有相同的chatRoomId
但具有不同的创建时间的元素,而且你的SplayTreeSet
只关心创建时间。
如果你的目标是仅保留每个chatRoomId
的最新消息,你需要使用chatRoomId
(一个String
)作为键来维护数据结构。自然的集合类型可能是Map<String, ChatRoomListModel>
(由于ChatRoomListModel
知道自己的chatRoomId
,它也可以只是一个带有显式equals
和hashCode
回调的HashSet
)。
如果你还想按时间顺序保留消息,你要么需要在之后显式对它们进行排序,要么维护一个单独的数据结构,将它们按时间顺序排列。你可以继续使用SplayTreeSet
来实现这一点。基本上,在将任何条目添加到SplayTreeSet
之前,先检查Map
,看看是否已经存在该chatRoomId
的条目。
我不完全了解你的数据结构,但这是一个你可以适应的示例:
import 'dart:collection';
class Message {
Message(this.creationTime, {required this.chatRoomId, required this.text});
final DateTime creationTime;
final String chatRoomId;
final String text;
@override
String toString() => '$creationTime: [$chatRoomId] $text';
}
class Messages {
final _latestMessages = <String, Message>{};
final _orderedMessages = SplayTreeSet<Message>((message1, message2) {
var result = message1.creationTime.compareTo(message2.creationTime);
if (result != 0) {
return result;
}
result = message1.chatRoomId.compareTo(message2.chatRoomId);
if (result != 0) {
return result;
}
return message1.text.compareTo(message2.text);
});
void add(Message message) {
var existingMessage = _latestMessages[message.chatRoomId];
if (existingMessage != null &&
message.creationTime.compareTo(existingMessage.creationTime) < 0) {
// An existing message exists with a later creation date. Ignore the
// incoming message.
return;
}
_latestMessages[message.chatRoomId] = message;
_orderedMessages.remove(existingMessage);
_orderedMessages.add(message);
}
void printAll() => _orderedMessages.forEach(print);
}
void main() {
var messages = Messages();
messages.add(Message(
DateTime(2023, 1, 1),
chatRoomId: 'foo',
text: 'Hello foo!',
));
messages.add(Message(
DateTime(2023, 1, 2),
chatRoomId: 'bar',
text: 'Goodbye bar!',
));
messages.add(Message(
DateTime(2023, 1, 2),
chatRoomId: 'foo',
text: 'Goodbye foo!',
));
messages.add(Message(
DateTime(2023, 1, 1),
chatRoomId: 'bar',
text: 'Hello bar!',
));
messages.printAll();
}
英文:
Your issue is that you have two completely different notions of what it means for two ChatRoomListModel
objects to be "equal". You provide both compareTo
and operator ==
implementations, but they consider different sets of properties, so compareTo
can return 0 when operator ==
returns false, which is confusing at best. SplayTreeMap
considers only compareTo
, not operator ==
. From the SplayTreeSet
documentation:
> Elements of the set are compared using the compare
function passed in the constructor, both for ordering and for equality. If the set contains only an object a
, then set.contains(b)
will return true
if and only if compare(a, b) == 0
, and the value of a == b
is not even checked.
I'm presuming what you call "duplicates" are elements that have equal chatRoomId
s but that have different creation times, and creation times are the only things that your SplayTreeSet
cares about.
If your goal is to maintain only the latest message per chatRoomId
, you need to maintain a data structure that uses the chatRoomId
(a String
) as a key. The natural collection for that would be a Map<String, ChatRoomListModel>
. (Since the ChatRoomListModel
knows its own chatRoomId
, it also could just be a HashSet
with explicit equals
and hashCode
callbacks.)
If you additionally want to keep messages in chronological order, you either will need to explicitly sort them afterward or maintain a separate data structure that keeps them in chronological order. You could continue using a SplayTreeSet
for that. Basically before you add any entry to the SplayTreeSet
, check the Map
first to see if an existing entry for that chatRoomId
.
I don't fully understand your data structures, but here's an example that you presumably can adapt:
import 'dart:collection';
class Message {
Message(this.creationTime, {required this.chatRoomId, required this.text});
final DateTime creationTime;
final String chatRoomId;
final String text;
@override
String toString() => '$creationTime: [$chatRoomId] $text';
}
class Messages {
final _latestMessages = <String, Message>{};
final _orderedMessages = SplayTreeSet<Message>((message1, message2) {
var result = message1.creationTime.compareTo(message2.creationTime);
if (result != 0) {
return result;
}
result = message1.chatRoomId.compareTo(message2.chatRoomId);
if (result != 0) {
return result;
}
return message1.text.compareTo(message2.text);
});
void add(Message message) {
var existingMessage = _latestMessages[message.chatRoomId];
if (existingMessage != null &&
message.creationTime.compareTo(existingMessage.creationTime) < 0) {
// An existing message exists with a later creation date. Ignore the
// incoming message.
return;
}
_latestMessages[message.chatRoomId] = message;
_orderedMessages.remove(existingMessage);
_orderedMessages.add(message);
}
void printAll() => _orderedMessages.forEach(print);
}
void main() {
var messages = Messages();
messages.add(Message(
DateTime(2023, 1, 1),
chatRoomId: 'foo',
text: 'Hello foo!',
));
messages.add(Message(
DateTime(2023, 1, 2),
chatRoomId: 'bar',
text: 'Goodbye bar!',
));
messages.add(Message(
DateTime(2023, 1, 2),
chatRoomId: 'foo',
text: 'Goodbye foo!',
));
messages.add(Message(
DateTime(2023, 1, 1),
chatRoomId: 'bar',
text: 'Hello bar!',
));
messages.printAll();
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论