SplayTreeSet包含重复的数值

huangapple go评论59阅读模式
英文:

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&lt;ChatRoomListModel&gt; _latestMessages = ObservableSet.splayTreeSetFrom(
    ObservableSet(),
    compare: (a, b) =&gt; 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) =&gt;
      identical(this, other) ||
      other is ChatRoomListModel &amp;&amp;
          runtimeType == other.runtimeType &amp;&amp;
          messagesModel.chatRoomId == other.messagesModel.chatRoomId;

  @override
  int get hashCode =&gt; messagesModel.chatRoomId.hashCode;

答案1

得分: 0

你的问题在于你对于两个ChatRoomListModel对象"相等"有两种完全不同的理解。你提供了compareTooperator ==的实现,但它们考虑了不同的属性集,因此当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,它也可以只是一个带有显式equalshashCode回调的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 chatRoomIds 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&lt;String, ChatRoomListModel&gt;. (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 &#39;dart:collection&#39;;

class Message {
  Message(this.creationTime, {required this.chatRoomId, required this.text});

  final DateTime creationTime;
  final String chatRoomId;
  final String text;

  @override
  String toString() =&gt; &#39;$creationTime: [$chatRoomId] $text&#39;;
}

class Messages {
  final _latestMessages = &lt;String, Message&gt;{};
  final _orderedMessages = SplayTreeSet&lt;Message&gt;((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 &amp;&amp;
        message.creationTime.compareTo(existingMessage.creationTime) &lt; 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() =&gt; _orderedMessages.forEach(print);
}

void main() {
  var messages = Messages();

  messages.add(Message(
    DateTime(2023, 1, 1),
    chatRoomId: &#39;foo&#39;,
    text: &#39;Hello foo!&#39;,
  ));

  messages.add(Message(
    DateTime(2023, 1, 2),
    chatRoomId: &#39;bar&#39;,
    text: &#39;Goodbye bar!&#39;,
  ));

  messages.add(Message(
    DateTime(2023, 1, 2),
    chatRoomId: &#39;foo&#39;,
    text: &#39;Goodbye foo!&#39;,
  ));

  messages.add(Message(
    DateTime(2023, 1, 1),
    chatRoomId: &#39;bar&#39;,
    text: &#39;Hello bar!&#39;,
  ));

  messages.printAll();
}

huangapple
  • 本文由 发表于 2023年2月19日 10:45:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75497693.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定