1

Very similar question was close due someone mark it as a duplicate of question: "A quick and easy way to join array elements with a separator (the opposite of split) in Java".

I expected that producing a huge String that represents collection is not a best approach. Just provide me a method implementation. Thanks.

Community
  • 1
  • 1
Javman
  • 45
  • 5
  • Do you have any code to show us? – Martin M J Apr 30 '15 at 08:56
  • for(String value : collectionObject) { String[] valArray = value.split(delimeter); for(String val: valArray){ System.out.println(val); } } can use Iterator on collection – Muneeb Nasir Apr 30 '15 at 09:01
  • 5
    If you just need to print the elements and that you worried about building a huge String just for printing it, you can still write a good old for loop that prints the elements one by one followed by the separator. By that I mean that the new Java 8 features does not make these constructions deprecated... – Alexis C. Apr 30 '15 at 09:01
  • @Maroun Maroun Does my answer is base on opinion not on facts? – Karol Król Apr 30 '15 at 10:40
  • 1
    Seems to me that the OP is looking for implementation techniques here, not opinions about what is better or best. I've voted to reopen. – Stuart Marks Apr 30 '15 at 20:20

3 Answers3

3

If you just need to print the elements and that you worried about building a huge String just for printing it, you can still write a good old for loop that prints the elements one by one followed by the separator.

By that I mean that the new Java 8 features does not make these constructions deprecated.

For example, you could write a utility method if needed:

public static void printCollection(Collection<?> coll, String delimiter) {
    int size = coll.size();
    int i = 0;
    for(Object elem : coll) {
        System.out.print(elem);
        if(++i < size) {
            System.out.print(delimiter);
        }
    }
}

With Java 8, you could maybe make this sligthly more compact:

public static void printCollection(Collection<?> coll, String delimiter) {
    //you can still avoid the map call and use two print statements in the forEach
    coll.stream().limit(coll.size()-1).map(o -> o+delimiter).forEach(System.out::print);
    coll.stream().skip(coll.size()-1).forEach(System.out::print);
}
Alexis C.
  • 91,686
  • 21
  • 171
  • 177
  • What I dont understand is: If having a huge String a concern, then having a huge collection should be a bigger concern. If one can accomodate such large collections, then i see no point in generating a string out of it. – Jatin Apr 30 '15 at 09:22
  • 2
    @Jatin Maybe, but if you have a huge collection and that you need to print its content, I think it's better to print each elements separately rather than concatenating their content into a String that you print after. The point you bring is relevant but it's another problem, IMO. – Alexis C. Apr 30 '15 at 09:26
  • Yes. Could not agree more :). – Jatin Apr 30 '15 at 09:28
  • About java 8 solution: Yes, let iterate stream twice! – Karol Król Apr 30 '15 at 12:40
  • @KarolKrol To be honest I don't know the exact complexity, but you can still make proper measurements if you want to know which approach is faster. Furthermore, the doc said that `skip` is a cheap operation on sequential pipeline and there will be improvements to make this a constant time operation if the underlying stream is `SIZED` if I'm not mistaken. So not sure if the downvote is justified. – Alexis C. Apr 30 '15 at 14:26
1

In general case the input collection could contains thousands or millions of elements, so better approach than producing a huge String object as a result would be to direct printing into a specific output stream.

In my dump implementation I simply iterate through collection. Printing steps are separated to avoid creating a new String for each concat operation (element.toString() + delimiter). As @Pshemo notice Stream API approach "will end up printing delimiter even after last element".

    public static <T> void nicePrint(final Collection<T> collection,
                                     final PrintStream printStream,
                                     final Optional<String> delimiter) {
        final Iterator<T> iterator = collection.iterator();
        while (iterator.hasNext()) {
            printStream.print(iterator.next());
            if (iterator.hasNext()) {
                delimiter.ifPresent(printStream::print);
            }
        }
    }

Example of use:

final String DELIMITER = ", ";
nicePrint(Arrays.asList(1, 2, 3), printStream, Optional.of(DELIMITER));
Community
  • 1
  • 1
Karol Król
  • 3,320
  • 1
  • 34
  • 37
  • 2
    Question is about `Collection` of elements, which also includes `Set` which doesn't have `get(index)` method. – Pshemo Apr 30 '15 at 09:10
  • @Pshemo I modify my implementation. Is it good enough? :) – Karol Król Apr 30 '15 at 10:38
  • It works so it is fine, but I would prefer something more readable like http://pastebin.com/CD437fiP. And if someone likes more descriptive methods we could replace that inner while loop with http://pastebin.com/uFsxEGu5. – Pshemo Apr 30 '15 at 10:52
  • 2
    Also I don't see a point in using Optional as argument. I am fan of using it as result of method which could return null which could represent value, or lack of value and we could have problem with determining which case it was (like in `Map.get(key)` could return `null` if key was not present in ma, or if key was assigned value `null`), but it brings too much confusion as an argument. – Pshemo Apr 30 '15 at 10:53
  • @Pshemo If you have a problem with Option in this example method then write your own perfect answer. I don't get the point about Map.get(key). Java Maps doesn't extends Collection interface -> http://stackoverflow.com/questions/2651819/why-doesnt-java-map-extends-collection – Karol Król Apr 30 '15 at 12:03
  • I don't have a problem. I am just not a fan. Also I can't post answer to closed question. – Pshemo Apr 30 '15 at 12:04
  • 1
    Point about Maps wasn't related to Collections which OP wants to print, but about usage of `Optional` which primary goal was to avoid using `null`. Please read https://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained (it is about Optional from Guava but it also applies to one added in Java 8). IMO it would be easier to accept delimiter as `String` instead of `Optional` and if you want to avoid printing it you can simply use `if(!delimiter.isEmpty())`. That is why I said "Also I don't see a point in using Optional as argument" in my previous comment. – Pshemo Apr 30 '15 at 12:18
  • @Pshemo And because there could be a problem with using Optional with Maps I shouldn't use Optional in my method? Thanks 4 advice. The Optional in my method signature is used exactly to avoid using null - if user doesn't want to use delimiter. You are not an oracle of knowledge man. – Karol Król Apr 30 '15 at 12:24
  • 1
    "And because there could be a problem with using Optional with Maps I shouldn't use Optional in my method?" I am not sure where does it come from, did you read article from link? Also "if user doesn't want to use delimiter" then he can pass `""` which is way simpler than `Optional.empty()`. Even Guava with `Joinder` and JDK with `String.join` or `StringJoinder` doesn't use `Optional` as argument for delimiter. But lets just agree to disagree. We have right to have our own *likes* and *dislikes*. I never said that your answer was wrong, what was I saying was only my opinions. – Pshemo Apr 30 '15 at 12:36
  • 1
    Anyway here is some simple rule to follow with Optional: "in method prefer Optional as *return type*, avoid it as *argument*". Reasons are quite simple: with Optional you have clear view if returned `null` represents correct value, or lack of correct value (like `null` for `get(foo)` can mean that `foo` was incorrect argument, but it can also mean that `foo` was OK and proper result for it was in fact `null`). But Optional as argument beside providing `null-safety` also limits you because thanks to type erasure we can't have `foo(Optional){bar()}` and `foo(Optional){baz()}`. – Pshemo Apr 30 '15 at 13:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/76657/discussion-between-karol-krol-and-pshemo). – Karol Król Apr 30 '15 at 16:02
1

From what I understand, you are looking for Scala's mkString like ability.

public static String mkString(Stream<String> s, String delimeter){
    return s.collect(Collectors.joining(delimeter));
}

System.out.println(print(Stream.of("1", "2", "3"), ", "));
//prints: 1, 2, 3
Jatin
  • 31,116
  • 15
  • 98
  • 163
  • 2
    I think the OP is worried about building a potential huge String only for printing it. – Alexis C. Apr 30 '15 at 09:12
  • I guess then return a Stream like: `return s.map(x -> x+delimeter)`?. If you dont wish to build a huge string, then a stream of strings makes sense. You retrieve and print it and move to next one. – Jatin Apr 30 '15 at 09:15
  • "*I guess then return a Stream like: return s.map(x -> x+delimeter)*" but this way you will end up printing delimiter even after last element. – Pshemo Apr 30 '15 at 10:01
  • @Jatin this way you concat each element with delimiter that produce additional String object – Karol Król Apr 30 '15 at 10:36
  • @kjkrol Absolutely I understand that. This want clear at time of writing the answer! – Jatin May 04 '15 at 08:15