5

I am being pulled up by Sonar due to the following line of code:

public void setFileNames(LinkedHashSet<String> fileNames) {

With the error message:

Avoid using implementation types like 'LinkedHashSet'; use the interface instead

What is the way around this when I want to represent a non-sorted Set which keeps its insertion order? Do I just use a Set and make it clear that the iteration order will be kept?

The stored data will be serialized using JaxB and the iteration order is essential after deserialization.

(I am aware of and completely understand this)

Community
  • 1
  • 1
Rich
  • 15,602
  • 15
  • 79
  • 126

4 Answers4

3

There is no such interface as it makes no sense to require this behavior for an input. Code creating a Set might have an intention about the order and choose an appropriate implementation when creating the Set.

But how can the question whether a Set has an insertion order, alphabetical order or an arbitrary, e.g. hash based, order make a difference for a method like setFileNames(Set<String> fileNames)?

Declaring the parameter type as Set gives you the guaranty that there won’t be duplicates which has an impact on the behavior, but the insertion order is a meaningless information (unless the caller makes it meaningful) about the history of the Set.

If you insist on having a method signature setFileNames(LinkedHashSet<String> fileNames), I still can pass in a Set with a meaningless order, e.g. calling
setFileNames(new LinkedHashSet<String>(hashSet)) or a set with lexicographical order, e.g. setFileNames(new LinkedHashSet<String>(treeSet)). Your signature makes it only more complicated.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • I can't argue with your last paragraph. The caller could still do the wrong thing. However, don't you think `LinkedHashSet` along with documentation serves the caller better than just `Set` and documentation. Perhaps not. Perhaps in either case they may get it wrong (the first time) and then go debug and read the documentation and then finally get it right. – Michael Welch Feb 06 '15 at 16:51
  • @Michael Welch: the question remains why the working of the method should in any way rely on the `Set` being in *insertion* order. Or in other words, why the method should fail if the `Set` is a `SortedSet` instead. I’m not saying that the method’s documentation should tell that the `Set` ought to maintain insertion order, I’m saying that the method shouldn’t depend on such property. In this regard, considering your answer, I want to emphasize that `List` doesn’t maintain *insertion* order, it just *has* an order but `List`s can get arbitrarily reordered. – Holger Feb 06 '15 at 17:09
  • I see what you are saying. I guess if order is important it could/should be more explicitly defined. Perhaps a set of tuples, where one part of the tuple specifies where in the order that tuple falls. Then his method can sort or do whatever it needs to to ensure the elements are visited in the correct order. (I guess I'm assuming that his implementation relies on a known iteration order. Perhaps he's modelling a queue of some sort. I agree is not clear why "insertion order" is important".) – Michael Welch Feb 06 '15 at 18:17
  • forget my idea in the last comment. If you make it a tuple, then every item presumably is distinct because of the sort order data. Which breaks the guarantee that the original values are distinct. Perhaps a bidirectional mapping between sort order values and actual data values. – Michael Welch Feb 06 '15 at 19:36
2

I appreciate the other answers, and I also appreciate the Sonar warning. However, sometimes (like perhaps in your case) its okay to ignore the warning. It seems to me that you are using LinkedHashSet to precisely define the responsibilities of the caller. Set doesn't communicate your requirements (order not preserved). Neither does List (distinct elements not guaranteed). So perhaps it's okay to ignore this warning.

An alternative is that you allow a List and then you have to double check (inside your method) that the list has no duplicates and throw an exception if there are. That seems ridiculous to me.

As others stated, you should just figure out how to suppress the warning in Sonar. Hopefully that mechanism has a way to include a reason why you suppressed it. Then you can explain your decision to future maintainers.

Michael Welch
  • 1,754
  • 2
  • 19
  • 32
1

Just accept a Set, let the caller decide what implementation to pass in.

which keeps its insertion order?

That's up to the caller. LinkedHashSet preserves order base on insertion, TreeSet preserves order based on natural ordering. Why should your method care how order is achieved?

Steve Kuo
  • 61,876
  • 75
  • 195
  • 257
-1

If you need the elements that you are receiving to be ordered, then your method should receive a List and not a Set. But in order to get rid of the warning, you would have to use Set and not LinkedHashSet. It is good practice to use interfaces and not actual classes in your methods. You shouldn't expose actual implementations of an interface.

Also, if all you need is to iterate through the elements of the Set, you could receive an Iterator, and just iterate through it.

EDIT: If you really want to ensure that you only receive a LinkedHashSet, you can do something like this:

public void setFileNames(Set<String> fileNames) {
   if (!(fileNames instanceof LinkedHashSet)) {
      throw new IllegalArgumentException("I need a LinkedHashSet!");
   }
}

EDIT 2: I don't think there is an ideal answer here, but if you do really need above all things receive a LinkedHashSet, I would declare it in the interface and find a way to make Sonar to ignore that specific instance of that warning.

Ravi Wallau
  • 10,416
  • 2
  • 25
  • 34
  • You expect the elements in the List interface to be ordered. Usually that is not true for Sets. – Ravi Wallau Feb 06 '15 at 14:30
  • http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashSet.html . it says ordered. – Mustafa Genç Feb 06 '15 at 14:33
  • I am talking about what the method exposes in its signature. If he wants to use an interface as the parameter in the method, it would be better to use a List, and not a Set, since as far as interfaces go, you expect that a List will be ordered, but a Set won't. You are right, a LinkedHashSet is ordered, but I am thinking about his method signature and what callers will understand when looking at it. – Ravi Wallau Feb 06 '15 at 14:35
  • And that is where things go wrong: there doesn't seem to be an appropriate interface. For all other collections I am using the interface instead of the implementation. – Rich Feb 06 '15 at 14:36
  • 1
    Yeah, as @Michael said, in some cases you just need to ignore the warning and live with the ugliness. – Ravi Wallau Feb 06 '15 at 14:38
  • 3
    I just don't get this approach. If you need a LinkedHashSet, then make that the type of your parameter. If I was the consumer of this and passed any old set and then got an exception that I didn't pass a LinkedHashSet I'd say, why did you make the parameter so generic? By making it a Set you're communicating something to the user of your API. – Michael Welch Feb 06 '15 at 15:06
  • @MichaelWelch I agree. That was my Edit #2 - if you really need it, better to declare that you really need it, even if it is not according to pre-defined standards. – Ravi Wallau Feb 06 '15 at 17:56
  • @RaviWallau yeah sorry. I saw your second edit after commenting. – Michael Welch Feb 06 '15 at 18:18